@@ -4,6 +4,7 @@ import { ExpressionType, TYPE_NONE } from './ExpressionType.js';
44import { SqlRuleError } from './errors.js' ;
55import {
66 BASIC_OPERATORS ,
7+ OPERATOR_IN ,
78 OPERATOR_IS_NOT_NULL ,
89 OPERATOR_IS_NULL ,
910 OPERATOR_JSON_EXTRACT_JSON ,
@@ -302,13 +303,18 @@ export class SqlTools {
302303 throw new Error ( 'Unexpected' ) ;
303304 }
304305 } else if ( op == 'IN' ) {
305- // Options:
306- // static IN static
307- // parameterValue IN static
308-
309- if ( isRowValueClause ( leftFilter ) && isRowValueClause ( rightFilter ) ) {
310- // static1 IN static2
311- return compileStaticOperator ( op , leftFilter , rightFilter ) ;
306+ // Special cases:
307+ // parameterValue IN rowValue
308+ // rowValue IN parameterValue
309+ // All others are handled by standard function composition
310+
311+ const composeType = this . getComposeType ( OPERATOR_IN , [ leftFilter , rightFilter ] , [ left , right ] ) ;
312+ if ( composeType . errorClause != null ) {
313+ return composeType . errorClause ;
314+ } else if ( composeType . argsType != null ) {
315+ // This is a standard supported configuration, takes precedence over
316+ // the special cases below.
317+ return this . composeFunction ( OPERATOR_IN , [ leftFilter , rightFilter ] , [ left , right ] ) ;
312318 } else if ( isParameterValueClause ( leftFilter ) && isRowValueClause ( rightFilter ) ) {
313319 // token_parameters.value IN table.some_array
314320 // bucket.param IN table.some_array
@@ -371,7 +377,8 @@ export class SqlTools {
371377 usesUnauthenticatedRequestParameters : rightFilter . usesUnauthenticatedRequestParameters
372378 } satisfies ParameterMatchClause ;
373379 } else {
374- return this . error ( `Unsupported usage of IN operator` , expr ) ;
380+ // Not supported, return the error previously computed
381+ return this . error ( composeType . error ! , composeType . errorExpr ) ;
375382 }
376383 } else if ( BASIC_OPERATORS . has ( op ) ) {
377384 const fnImpl = getOperatorFunction ( op ) ;
@@ -634,36 +641,19 @@ export class SqlTools {
634641 * @returns a compiled function clause
635642 */
636643 composeFunction ( fnImpl : SqlFunction , argClauses : CompiledClause [ ] , debugArgExpressions : Expr [ ] ) : CompiledClause {
637- let argsType : 'static' | 'row' | 'param' = 'static' ;
638- for ( let i = 0 ; i < argClauses . length ; i ++ ) {
639- const debugArg = debugArgExpressions [ i ] ;
640- const clause = argClauses [ i ] ;
641- if ( isClauseError ( clause ) ) {
642- // Return immediately on error
643- return clause ;
644- } else if ( isStaticValueClause ( clause ) ) {
645- // argsType unchanged
646- } else if ( isParameterValueClause ( clause ) ) {
647- if ( ! this . supports_parameter_expressions ) {
648- return this . error ( `Cannot use bucket parameters in expressions` , debugArg ) ;
649- }
650- if ( argsType == 'static' || argsType == 'param' ) {
651- argsType = 'param' ;
652- } else {
653- return this . error ( `Cannot use table values and parameters in the same clauses` , debugArg ) ;
654- }
655- } else if ( isRowValueClause ( clause ) ) {
656- if ( argsType == 'static' || argsType == 'row' ) {
657- argsType = 'row' ;
658- } else {
659- return this . error ( `Cannot use table values and parameters in the same clauses` , debugArg ) ;
660- }
661- } else {
662- return this . error ( `Parameter match clauses cannot be used here` , debugArg ) ;
663- }
644+ const result = this . getComposeType ( fnImpl , argClauses , debugArgExpressions ) ;
645+ if ( result . errorClause != null ) {
646+ return result . errorClause ;
647+ } else if ( result . error != null ) {
648+ return this . error ( result . error , result . errorExpr ) ;
664649 }
650+ const argsType = result . argsType ! ;
665651
666- if ( argsType == 'row' || argsType == 'static' ) {
652+ if ( argsType == 'static' ) {
653+ const args = argClauses . map ( ( e ) => ( e as StaticValueClause ) . value ) ;
654+ const evaluated = fnImpl . call ( ...args ) ;
655+ return staticValueClause ( evaluated ) ;
656+ } else if ( argsType == 'row' ) {
667657 return {
668658 evaluate : ( tables ) => {
669659 const args = argClauses . map ( ( e ) => ( e as RowValueClause ) . evaluate ( tables ) ) ;
@@ -705,7 +695,48 @@ export class SqlTools {
705695 }
706696 }
707697
708- parameterFunction ( ) { }
698+ getComposeType (
699+ fnImpl : SqlFunction ,
700+ argClauses : CompiledClause [ ] ,
701+ debugArgExpressions : Expr [ ]
702+ ) : { argsType ?: string ; error ?: string ; errorExpr ?: Expr ; errorClause ?: ClauseError } {
703+ let argsType : 'static' | 'row' | 'param' = 'static' ;
704+ for ( let i = 0 ; i < argClauses . length ; i ++ ) {
705+ const debugArg = debugArgExpressions [ i ] ;
706+ const clause = argClauses [ i ] ;
707+ if ( isClauseError ( clause ) ) {
708+ // Return immediately on error
709+ return { errorClause : clause } ;
710+ } else if ( isStaticValueClause ( clause ) ) {
711+ // argsType unchanged
712+ } else if ( isParameterValueClause ( clause ) ) {
713+ if ( ! this . supports_parameter_expressions ) {
714+ if ( fnImpl . debugName == 'operatorIN' ) {
715+ // Special-case error message to be more descriptive
716+ return { error : `Cannot use bucket parameters on the right side of IN operators` , errorExpr : debugArg } ;
717+ }
718+ return { error : `Cannot use bucket parameters in expressions` , errorExpr : debugArg } ;
719+ }
720+ if ( argsType == 'static' || argsType == 'param' ) {
721+ argsType = 'param' ;
722+ } else {
723+ return { error : `Cannot use table values and parameters in the same clauses` , errorExpr : debugArg } ;
724+ }
725+ } else if ( isRowValueClause ( clause ) ) {
726+ if ( argsType == 'static' || argsType == 'row' ) {
727+ argsType = 'row' ;
728+ } else {
729+ return { error : `Cannot use table values and parameters in the same clauses` , errorExpr : debugArg } ;
730+ }
731+ } else {
732+ return { error : `Parameter match clauses cannot be used here` , errorExpr : debugArg } ;
733+ }
734+ }
735+
736+ return {
737+ argsType
738+ } ;
739+ }
709740}
710741
711742function isStatic ( expr : Expr ) {
0 commit comments