@@ -331,8 +331,14 @@ const buildWhereClause = ({ schema, query, index }) => {
331331 if ( opts . indexOf ( 'i' ) >= 0 ) {
332332 operator = '~*' ;
333333 }
334+ if ( opts . indexOf ( 'x' ) >= 0 ) {
335+ regex = removeWhiteSpace ( regex ) ;
336+ }
334337 }
335- patterns . push ( `$${ index } :name ${ operator } $${ index + 1 } ` ) ;
338+
339+ regex = processRegexPattern ( regex ) ;
340+
341+ patterns . push ( `$${ index } :name ${ operator } '$${ index + 1 } :raw'` ) ;
336342 values . push ( fieldName , regex ) ;
337343 index += 2 ;
338344 }
@@ -1131,6 +1137,79 @@ function notImplemented() {
11311137 return Promise . reject ( new Error ( 'Not implemented yet.' ) ) ;
11321138}
11331139
1140+ function removeWhiteSpace ( regex ) {
1141+ if ( ! regex . endsWith ( '\n' ) ) {
1142+ regex += '\n' ;
1143+ }
1144+
1145+ // remove non escaped comments
1146+ return regex . replace ( / ( [ ^ \\ ] ) # .* \n / gmi, '$1' )
1147+ // remove lines starting with a comment
1148+ . replace ( / ^ # .* \n / gmi, '' )
1149+ // remove non escaped whitespace
1150+ . replace ( / ( [ ^ \\ ] ) \s + / gmi, '$1' )
1151+ // remove whitespace at the beginning of a line
1152+ . replace ( / ^ \s + / , '' )
1153+ . trim ( ) ;
1154+ }
1155+
1156+ function processRegexPattern ( s ) {
1157+ if ( s && s . startsWith ( '^' ) ) {
1158+ // regex for startsWith
1159+ return '^' + literalizeRegexPart ( s . slice ( 1 ) ) ;
1160+
1161+ } else if ( s && s . endsWith ( '$' ) ) {
1162+ // regex for endsWith
1163+ return literalizeRegexPart ( s . slice ( 0 , s . length - 1 ) ) + '$' ;
1164+ }
1165+
1166+ // regex for contains
1167+ return literalizeRegexPart ( s ) ;
1168+ }
1169+
1170+ function createLiteralRegex ( remaining ) {
1171+ return remaining . split ( '' ) . map ( c => {
1172+ if ( c . match ( / [ 0 - 9 a - z A - Z ] / ) !== null ) {
1173+ // don't escape alphanumeric characters
1174+ return c ;
1175+ }
1176+ // escape everything else (single quotes with single quotes, everything else with a backslash)
1177+ return c === `'` ? `''` : `\\${ c } ` ;
1178+ } ) . join ( '' ) ;
1179+ }
1180+
1181+ function literalizeRegexPart ( s ) {
1182+ const matcher1 = / \\ Q ( (? ! \\ E ) .* ) \\ E $ /
1183+ const result1 = s . match ( matcher1 ) ;
1184+ if ( result1 && result1 . length > 1 && result1 . index > - 1 ) {
1185+ // process regex that has a beginning and an end specified for the literal text
1186+ const prefix = s . substr ( 0 , result1 . index ) ;
1187+ const remaining = result1 [ 1 ] ;
1188+
1189+ return literalizeRegexPart ( prefix ) + createLiteralRegex ( remaining ) ;
1190+ }
1191+
1192+ // process regex that has a beginning specified for the literal text
1193+ const matcher2 = / \\ Q ( (? ! \\ E ) .* ) $ /
1194+ const result2 = s . match ( matcher2 ) ;
1195+ if ( result2 && result2 . length > 1 && result2 . index > - 1 ) {
1196+ const prefix = s . substr ( 0 , result2 . index ) ;
1197+ const remaining = result2 [ 1 ] ;
1198+
1199+ return literalizeRegexPart ( prefix ) + createLiteralRegex ( remaining ) ;
1200+ }
1201+
1202+ // remove all instances of \Q and \E from the remaining text & escape single quotes
1203+ return (
1204+ s . replace ( / ( [ ^ \\ ] ) ( \\ E ) / , '$1' )
1205+ . replace ( / ( [ ^ \\ ] ) ( \\ Q ) / , '$1' )
1206+ . replace ( / ^ \\ E / , '' )
1207+ . replace ( / ^ \\ Q / , '' )
1208+ . replace ( / ( [ ^ ' ] ) ' / , `$1''` )
1209+ . replace ( / ^ ' ( [ ^ ' ] ) / , `''$1` )
1210+ ) ;
1211+ }
1212+
11341213// Function to set a key on a nested JSON document
11351214const json_object_set_key = 'CREATE OR REPLACE FUNCTION "json_object_set_key"(\
11361215 "json" jsonb,\
0 commit comments