@@ -541,7 +541,7 @@ export abstract class BaseCrudDialect<Schema extends SchemaDef> {
541541 } else if ( isTypeDef ( this . schema , fieldDef . type ) ) {
542542 return this . buildTypedJsonFilter ( receiver , filter , fieldDef . type , ! ! fieldDef . array ) ;
543543 } else {
544- return this . true ( ) ;
544+ throw createInvalidInputError ( `Invalid JSON filter payload` ) ;
545545 }
546546 }
547547
@@ -597,7 +597,7 @@ export abstract class BaseCrudDialect<Schema extends SchemaDef> {
597597 // already handled
598598 break ;
599599 default :
600- invariant ( false , `Invalid JSON filter key: ${ key } ` ) ;
600+ throw createInvalidInputError ( `Invalid JSON filter key: ${ key } ` ) ;
601601 }
602602 }
603603 return this . and ( ...clauses ) ;
@@ -817,22 +817,15 @@ export abstract class BaseCrudDialect<Schema extends SchemaDef> {
817817 continue ;
818818 }
819819
820+ invariant ( typeof value === 'string' , `${ key } value must be a string` ) ;
821+
822+ const escapedValue = this . escapeLikePattern ( value ) ;
820823 const condition = match ( key )
821- . with ( 'contains' , ( ) =>
822- mode === 'insensitive'
823- ? this . eb ( fieldRef , 'ilike' , sql . val ( `%${ value } %` ) )
824- : this . eb ( fieldRef , 'like' , sql . val ( `%${ value } %` ) ) ,
825- )
824+ . with ( 'contains' , ( ) => this . buildStringLike ( fieldRef , `%${ escapedValue } %` , mode === 'insensitive' ) )
826825 . with ( 'startsWith' , ( ) =>
827- mode === 'insensitive'
828- ? this . eb ( fieldRef , 'ilike' , sql . val ( `${ value } %` ) )
829- : this . eb ( fieldRef , 'like' , sql . val ( `${ value } %` ) ) ,
830- )
831- . with ( 'endsWith' , ( ) =>
832- mode === 'insensitive'
833- ? this . eb ( fieldRef , 'ilike' , sql . val ( `%${ value } ` ) )
834- : this . eb ( fieldRef , 'like' , sql . val ( `%${ value } ` ) ) ,
826+ this . buildStringLike ( fieldRef , `${ escapedValue } %` , mode === 'insensitive' ) ,
835827 )
828+ . with ( 'endsWith' , ( ) => this . buildStringLike ( fieldRef , `%${ escapedValue } ` , mode === 'insensitive' ) )
836829 . otherwise ( ( ) => {
837830 throw createInvalidInputError ( `Invalid string filter key: ${ key } ` ) ;
838831 } ) ;
@@ -846,6 +839,33 @@ export abstract class BaseCrudDialect<Schema extends SchemaDef> {
846839 return this . and ( ...conditions ) ;
847840 }
848841
842+ private buildJsonStringFilter (
843+ receiver : Expression < any > ,
844+ operation : 'string_contains' | 'string_starts_with' | 'string_ends_with' ,
845+ value : string ,
846+ mode : 'default' | 'insensitive' ,
847+ ) {
848+ // build LIKE pattern based on operation, note that receiver is quoted
849+ const escapedValue = this . escapeLikePattern ( value ) ;
850+ const pattern = match ( operation )
851+ . with ( 'string_contains' , ( ) => `"%${ escapedValue } %"` )
852+ . with ( 'string_starts_with' , ( ) => `"${ escapedValue } %"` )
853+ . with ( 'string_ends_with' , ( ) => `"%${ escapedValue } "` )
854+ . exhaustive ( ) ;
855+
856+ return this . buildStringLike ( receiver , pattern , mode === 'insensitive' ) ;
857+ }
858+
859+ private escapeLikePattern ( pattern : string ) {
860+ return pattern . replace ( / \\ / g, '\\\\' ) . replace ( / % / g, '\\%' ) . replace ( / _ / g, '\\_' ) ;
861+ }
862+
863+ private buildStringLike ( receiver : Expression < any > , pattern : string , insensitive : boolean ) {
864+ const { supportsILike } = this . getStringCasingBehavior ( ) ;
865+ const op = insensitive && supportsILike ? 'ilike' : 'like' ;
866+ return sql < SqlBool > `${ receiver } ${ sql . raw ( op ) } ${ sql . val ( pattern ) } escape '\\'` ;
867+ }
868+
849869 private prepStringCasing (
850870 eb : ExpressionBuilder < any , any > ,
851871 value : unknown ,
@@ -1409,16 +1429,6 @@ export abstract class BaseCrudDialect<Schema extends SchemaDef> {
14091429 */
14101430 protected abstract buildJsonPathSelection ( receiver : Expression < any > , path : string | undefined ) : Expression < any > ;
14111431
1412- /**
1413- * Builds a JSON string filter expression.
1414- */
1415- protected abstract buildJsonStringFilter (
1416- receiver : Expression < any > ,
1417- operation : 'string_contains' | 'string_starts_with' | 'string_ends_with' ,
1418- value : string ,
1419- mode : 'default' | 'insensitive' ,
1420- ) : Expression < SqlBool > ;
1421-
14221432 /**
14231433 * Builds a JSON array filter expression.
14241434 */
0 commit comments