import { FilterNode } from '../operation-node/filter-node.js' ; import { freeze , isBoolean , isFunction , isNull , isString , } from '../util/object-utils.js' ; import { AnySelectQueryBuilder , AnyRawBuilder } from '../util/type-utils.js' ; import { isOperationNodeSource } from '../operation-node/operation-node-source.js' ; import { RawNode } from '../operation-node/raw-node.js' ; import { Operator , OperatorNode , OPERATORS , } from '../operation-node/operator-node.js' ; import { ParensNode } from '../operation-node/parens-node.js' ; import { ExtractTypeFromReferenceExpression , parseReferenceExpression , ReferenceExpression , } from './reference-parser.js' ; import { parseValueExpressionOrList , ValueExpression , ValueExpressionOrList , } from './value-parser.js' ; import { SelectQueryNode } from '../operation-node/select-query-node.js' ; import { JoinBuilder } from '../query-builder/join-builder.js' ; import { FilterExpressionNode } from '../operation-node/operation-node-utils.js' ; import { ValueNode } from '../operation-node/value-node.js' ; import { ParseContext } from './parse-context.js' ; import { WhereInterface } from '../query-builder/where-interface.js' ; import { HavingInterface } from '../query-builder/having-interface.js' ; export type FilterValueExpression < DB , TB extends keyof DB , RE > = ValueExpression < DB , TB , ExtractTypeFromReferenceExpression < DB , TB , RE > > ; export type FilterValueExpressionOrList < DB , TB extends keyof DB , RE > = ValueExpressionOrList < DB , TB , ExtractTypeFromReferenceExpression < DB , TB , RE > > ; export type ExistsExpression < DB , TB extends keyof DB > = ValueExpression < DB , TB , never > ; export type WhereGrouper < DB , TB extends keyof DB > = ( qb : WhereInterface < DB , TB > ) => WhereInterface < DB , TB > ; export type HavingGrouper < DB , TB extends keyof DB > = ( qb : HavingInterface < DB , TB > ) => HavingInterface < DB , TB > ; export type FilterOperator = Operator | AnyRawBuilder ; type FilterType = 'where' | 'having' | 'on' ; export function parseWhereFilter ( ctx , args ) { return parseFilter ( ctx , 'where' , args ) ; } export function parseHavingFilter ( ctx , args ) { return parseFilter ( ctx , 'having' , args ) ; } export function parseOnFilter ( ctx , args ) { return parseFilter ( ctx , 'on' , args ) ; } export function parseReferenceFilter ( ctx , lhs , op , rhs ) { return FilterNode . create ( parseReferenceExpression ( ctx , lhs ) , parseFilterOperator ( op ) , parseReferenceExpression ( ctx , rhs ) ) ; } export function parseExistFilter ( ctx , arg ) { return parseExistExpression ( ctx , 'exists' , arg ) ; } export function parseNotExistFilter ( ctx , arg ) { return parseExistExpression ( ctx , 'not exists' , arg ) ; } export function parseFilter ( ctx , type , args ) { if ( args . length === 3 ) { return parseThreeArgFilter ( ctx , args [ 0 ] , args [ 1 ] , args [ 2 ] ) ; } else if ( args . length === 1 ) { return parseOneArgFilter ( ctx , type , args [ 0 ] ) ; } throw createFilterError ( type , args ) ; } function parseThreeArgFilter ( ctx , left , op , right ) { if ( ( op === 'is' || op === 'is not' ) && ( isNull ( right ) || isBoolean ( right ) ) ) { return parseIsFilter ( ctx , left , op , right ) ; } return FilterNode . create ( parseReferenceExpression ( ctx , left ) , parseFilterOperator ( op ) , parseValueExpressionOrList ( ctx , right ) ) ; } function parseIsFilter ( ctx , left , op , right ) { return FilterNode . create ( parseReferenceExpression ( ctx , left ) , parseFilterOperator ( op ) , ValueNode . createImmediate ( right ) ) ; } function parseFilterOperator ( op ) { if ( isString ( op ) && OPERATORS . includes ( op ) ) { return OperatorNode . create ( op ) ; } else if ( isOperationNodeSource ( op ) ) { return op . toOperationNode ( ) ; } throw new Error ( `invalid comparison operator ${ JSON . stringify ( op ) } passed to a filter method` ) ; } function parseExistExpression ( ctx , type , arg ) { return FilterNode . create ( undefined , OperatorNode . create ( type ) , parseValueExpressionOrList ( ctx , arg ) ) ; } function parseOneArgFilter ( ctx , type , arg ) { if ( isFunction ( arg ) ) { return GROUP_PARSERS [ type ] ( ctx , arg ) ; } else if ( isOperationNodeSource ( arg ) ) { const node = arg . toOperationNode ( ) ; if ( RawNode . is ( node ) ) { return node ; } } throw createFilterError ( type , arg ) ; } function createFilterError ( type , args ) { return new Error ( `invalid arguments passed to a '${ type }' method: ${ JSON . stringify ( args ) }` ) ; } const GROUP_PARSERS = freeze ( { where ( ctx , callback ) { const query = callback ( ctx . createSelectQueryBuilder ( ) ) ; const queryNode = query . toOperationNode ( ) as SelectQueryNode ; if ( ! queryNode . where ) { throw new Error ( 'no `where` methods called insided a group callback' ) ; } return ParensNode . create ( queryNode . where . where ) ; } , having ( ctx , callback ) { const query = callback ( ctx . createSelectQueryBuilder ( ) ) ; const queryNode = query . toOperationNode ( ) as SelectQueryNode ; if ( ! queryNode . having ) { throw new Error ( 'no `having` methods called insided a group callback' ) ; } return ParensNode . create ( queryNode . having . having ) ; } , on ( ctx , callback ) { const joinBuilder = callback ( ctx . createJoinBuilder ( 'InnerJoin' , 'table' ) ) ; const joinNode = joinBuilder . toOperationNode ( ) ; if ( ! joinNode . on ) { throw new Error ( 'no `on` methods called insided a group callback' ) ; } return ParensNode . create ( joinNode . on . on ) ; } , } ) ;