@budibase/backend-core 2.33.2 → 2.33.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -90,6 +90,20 @@ function isSqs(table) {
90
90
  return (table.sourceType === types_1.TableSourceType.INTERNAL ||
91
91
  table.sourceId === types_1.INTERNAL_TABLE_SOURCE_ID);
92
92
  }
93
+ function escapeQuotes(value, quoteChar = '"') {
94
+ return value.replace(new RegExp(quoteChar, "g"), `${quoteChar}${quoteChar}`);
95
+ }
96
+ function wrap(value, quoteChar = '"') {
97
+ return `${quoteChar}${escapeQuotes(value, quoteChar)}${quoteChar}`;
98
+ }
99
+ function stringifyArray(value, quoteStyle = '"') {
100
+ for (let i in value) {
101
+ if (typeof value[i] === "string") {
102
+ value[i] = wrap(value[i], quoteStyle);
103
+ }
104
+ }
105
+ return `[${value.join(",")}]`;
106
+ }
93
107
  const allowEmptyRelationships = {
94
108
  [types_1.BasicOperator.EQUAL]: false,
95
109
  [types_1.BasicOperator.NOT_EQUAL]: true,
@@ -131,28 +145,24 @@ class InternalBuilder {
131
145
  get table() {
132
146
  return this.query.meta.table;
133
147
  }
148
+ get knexClient() {
149
+ return this.knex.client;
150
+ }
134
151
  getFieldSchema(key) {
135
152
  const { column } = this.splitter.run(key);
136
153
  return this.table.schema[column];
137
154
  }
155
+ supportsILike() {
156
+ return !(this.client === types_1.SqlClient.ORACLE || this.client === types_1.SqlClient.SQL_LITE);
157
+ }
138
158
  quoteChars() {
139
- switch (this.client) {
140
- case types_1.SqlClient.ORACLE:
141
- case types_1.SqlClient.POSTGRES:
142
- return ['"', '"'];
143
- case types_1.SqlClient.MS_SQL:
144
- return ["[", "]"];
145
- case types_1.SqlClient.MARIADB:
146
- case types_1.SqlClient.MY_SQL:
147
- case types_1.SqlClient.SQL_LITE:
148
- return ["`", "`"];
149
- }
159
+ const wrapped = this.knexClient.wrapIdentifier("foo", {});
160
+ return [wrapped[0], wrapped[wrapped.length - 1]];
150
161
  }
151
- // Takes a string like foo and returns a quoted string like [foo] for SQL Server
152
- // and "foo" for Postgres.
162
+ // Takes a string like foo and returns a quoted string like [foo] for SQL
163
+ // Server and "foo" for Postgres.
153
164
  quote(str) {
154
- const [start, end] = this.quoteChars();
155
- return `${start}${str}${end}`;
165
+ return this.knexClient.wrapIdentifier(str, {});
156
166
  }
157
167
  isQuoted(key) {
158
168
  const [start, end] = this.quoteChars();
@@ -167,6 +177,27 @@ class InternalBuilder {
167
177
  }
168
178
  return key.map(part => this.quote(part)).join(".");
169
179
  }
180
+ quotedValue(value) {
181
+ const formatter = this.knexClient.formatter(this.knexClient.queryBuilder());
182
+ return formatter.wrap(value, false);
183
+ }
184
+ rawQuotedValue(value) {
185
+ return this.knex.raw(this.quotedValue(value));
186
+ }
187
+ // Unfortuantely we cannot rely on knex's identifier escaping because it trims
188
+ // the identifier string before escaping it, which breaks cases for us where
189
+ // columns that start or end with a space aren't referenced correctly anymore.
190
+ //
191
+ // So whenever you're using an identifier binding in knex, e.g. knex.raw("??
192
+ // as ?", ["foo", "bar"]), you need to make sure you call this:
193
+ //
194
+ // knex.raw("?? as ?", [this.quotedIdentifier("foo"), "bar"])
195
+ //
196
+ // Issue we filed against knex about this:
197
+ // https://github.com/knex/knex/issues/6143
198
+ rawQuotedIdentifier(key) {
199
+ return this.knex.raw(this.quotedIdentifier(key));
200
+ }
170
201
  // Turns an identifier like a.b.c or `a`.`b`.`c` into ["a", "b", "c"]
171
202
  splitIdentifier(key) {
172
203
  const [start, end] = this.quoteChars();
@@ -206,7 +237,7 @@ class InternalBuilder {
206
237
  const alias = this.getTableName(endpoint.entityId);
207
238
  const schema = meta.table.schema;
208
239
  if (!this.isFullSelectStatementRequired()) {
209
- return [this.knex.raw(`${this.quote(alias)}.*`)];
240
+ return [this.knex.raw("??", [`${alias}.*`])];
210
241
  }
211
242
  // get just the fields for this table
212
243
  return resource.fields
@@ -225,17 +256,28 @@ class InternalBuilder {
225
256
  .map(({ table, column, field }) => {
226
257
  const columnSchema = schema[column];
227
258
  if (this.SPECIAL_SELECT_CASES.POSTGRES_MONEY(columnSchema)) {
228
- return this.knex.raw(`${this.quotedIdentifier([table, column].join("."))}::money::numeric as ${this.quote(field)}`);
259
+ // TODO: figure out how to express this safely without string
260
+ // interpolation.
261
+ return this.knex.raw(`??::money::numeric as "${field}"`, [
262
+ this.rawQuotedIdentifier([table, column].join(".")),
263
+ field,
264
+ ]);
229
265
  }
230
266
  if (this.SPECIAL_SELECT_CASES.MSSQL_DATES(columnSchema)) {
231
267
  // Time gets returned as timestamp from mssql, not matching the expected
232
268
  // HH:mm format
233
- return this.knex.raw(`CONVERT(varchar, ${field}, 108) as "${field}"`);
269
+ // TODO: figure out how to express this safely without string
270
+ // interpolation.
271
+ return this.knex.raw(`CONVERT(varchar, ??, 108) as "${field}"`, [
272
+ this.rawQuotedIdentifier(field),
273
+ ]);
274
+ }
275
+ if (table) {
276
+ return this.rawQuotedIdentifier(`${table}.${column}`);
277
+ }
278
+ else {
279
+ return this.rawQuotedIdentifier(field);
234
280
  }
235
- const quoted = table
236
- ? `${this.quote(table)}.${this.quote(column)}`
237
- : this.quote(field);
238
- return this.knex.raw(quoted);
239
281
  });
240
282
  }
241
283
  // OracleDB can't use character-large-objects (CLOBs) in WHERE clauses,
@@ -248,7 +290,7 @@ class InternalBuilder {
248
290
  const parts = this.splitIdentifier(field);
249
291
  const col = parts.pop();
250
292
  const schema = this.table.schema[col];
251
- let identifier = this.quotedIdentifier(field);
293
+ let identifier = this.rawQuotedIdentifier(field);
252
294
  if (schema.type === types_1.FieldType.STRING ||
253
295
  schema.type === types_1.FieldType.LONGFORM ||
254
296
  schema.type === types_1.FieldType.BB_REFERENCE_SINGLE ||
@@ -256,10 +298,13 @@ class InternalBuilder {
256
298
  schema.type === types_1.FieldType.OPTIONS ||
257
299
  schema.type === types_1.FieldType.BARCODEQR) {
258
300
  if (opts === null || opts === void 0 ? void 0 : opts.forSelect) {
259
- identifier = `to_char(${identifier}) as ${this.quotedIdentifier(col)}`;
301
+ identifier = this.knex.raw("to_char(??) as ??", [
302
+ identifier,
303
+ this.rawQuotedIdentifier(col),
304
+ ]);
260
305
  }
261
306
  else {
262
- identifier = `to_char(${identifier})`;
307
+ identifier = this.knex.raw("to_char(??)", [identifier]);
263
308
  }
264
309
  }
265
310
  return identifier;
@@ -367,7 +412,6 @@ class InternalBuilder {
367
412
  return query.andWhere(`${document}.fieldName`, "=", relationship.column);
368
413
  }
369
414
  addRelationshipForFilter(query, allowEmptyRelationships, filterKey, whereCb) {
370
- const mainKnex = this.knex;
371
415
  const { relationships, endpoint, tableAliases: aliases } = this.query;
372
416
  const tableName = endpoint.entityId;
373
417
  const fromAlias = (aliases === null || aliases === void 0 ? void 0 : aliases[tableName]) || tableName;
@@ -384,8 +428,8 @@ class InternalBuilder {
384
428
  if ((matchesTableName || matchesRelationName) &&
385
429
  relationship.to &&
386
430
  relationship.tableName) {
387
- const joinTable = mainKnex
388
- .select(mainKnex.raw(1))
431
+ const joinTable = this.knex
432
+ .select(this.knex.raw(1))
389
433
  .from({ [toAlias]: relatedTableName });
390
434
  let subQuery = joinTable.clone();
391
435
  const manyToMany = (0, utils_1.validateManyToMany)(relationship);
@@ -408,7 +452,7 @@ class InternalBuilder {
408
452
  this.on(`${toAlias}.${manyToMany.toPrimary}`, "=", `${throughAlias}.${manyToMany.to}`);
409
453
  })
410
454
  // check the document in the junction table points to the main table
411
- .where(`${throughAlias}.${manyToMany.from}`, "=", mainKnex.raw(this.quotedIdentifier(`${fromAlias}.${manyToMany.fromPrimary}`)));
455
+ .where(`${throughAlias}.${manyToMany.from}`, "=", this.rawQuotedIdentifier(`${fromAlias}.${manyToMany.fromPrimary}`));
412
456
  // in SQS the same junction table is used for different many-to-many relationships between the
413
457
  // two same tables, this is needed to avoid rows ending up in all columns
414
458
  if (this.client === types_1.SqlClient.SQL_LITE) {
@@ -427,7 +471,7 @@ class InternalBuilder {
427
471
  const toKey = `${toAlias}.${relationship.to}`;
428
472
  const foreignKey = `${fromAlias}.${relationship.from}`;
429
473
  // "join" to the main table, making sure the ID matches that of the main
430
- subQuery = subQuery.where(toKey, "=", mainKnex.raw(this.quotedIdentifier(foreignKey)));
474
+ subQuery = subQuery.where(toKey, "=", this.rawQuotedIdentifier(foreignKey));
431
475
  query = query.where(q => {
432
476
  q.whereExists(whereCb(updatedKey, subQuery.clone()));
433
477
  if (allowEmptyRelationships) {
@@ -448,7 +492,7 @@ class InternalBuilder {
448
492
  filters = this.parseFilters(Object.assign({}, filters));
449
493
  const aliases = this.query.tableAliases;
450
494
  // if all or specified in filters, then everything is an or
451
- const allOr = filters.allOr;
495
+ const shouldOr = filters.allOr;
452
496
  const isSqlite = this.client === types_1.SqlClient.SQL_LITE;
453
497
  const tableName = isSqlite ? this.table._id : this.table.name;
454
498
  function getTableAlias(name) {
@@ -479,7 +523,7 @@ class InternalBuilder {
479
523
  query = fn(query, alias ? `${alias}.${updatedKey}` : updatedKey, value);
480
524
  }
481
525
  else if (shouldProcessRelationship) {
482
- if (allOr) {
526
+ if (shouldOr) {
483
527
  query = query.or;
484
528
  }
485
529
  query = builder.addRelationshipForFilter(query, allowEmptyRelationships[operation], updatedKey, (updatedKey, q) => {
@@ -489,71 +533,90 @@ class InternalBuilder {
489
533
  }
490
534
  }
491
535
  const like = (q, key, value) => {
492
- const fuzzyOr = filters === null || filters === void 0 ? void 0 : filters.fuzzyOr;
493
- const fnc = fuzzyOr || allOr ? "orWhere" : "where";
494
- // postgres supports ilike, nothing else does
495
- if (this.client === types_1.SqlClient.POSTGRES) {
496
- return q[fnc](key, "ilike", `%${value}%`);
536
+ if ((filters === null || filters === void 0 ? void 0 : filters.fuzzyOr) || shouldOr) {
537
+ q = q.or;
497
538
  }
498
- else {
499
- const rawFnc = `${fnc}Raw`;
500
- // @ts-ignore
501
- return q[rawFnc](`LOWER(${this.quotedIdentifier(key)}) LIKE ?`, [
539
+ if (this.client === types_1.SqlClient.ORACLE ||
540
+ this.client === types_1.SqlClient.SQL_LITE) {
541
+ return q.whereRaw(`LOWER(??) LIKE ?`, [
542
+ this.rawQuotedIdentifier(key),
502
543
  `%${value.toLowerCase()}%`,
503
544
  ]);
504
545
  }
546
+ return q.whereILike(
547
+ // @ts-expect-error knex types are wrong, raw is fine here
548
+ this.rawQuotedIdentifier(key), this.knex.raw("?", [`%${value}%`]));
505
549
  };
506
550
  const contains = (mode, any = false) => {
507
- const rawFnc = allOr ? "orWhereRaw" : "whereRaw";
508
- const not = mode === (filters === null || filters === void 0 ? void 0 : filters.notContains) ? "NOT " : "";
509
- function stringifyArray(value, quoteStyle = '"') {
510
- for (let i in value) {
511
- if (typeof value[i] === "string") {
512
- value[i] = `${quoteStyle}${value[i]}${quoteStyle}`;
513
- }
551
+ function addModifiers(q) {
552
+ if (shouldOr || mode === (filters === null || filters === void 0 ? void 0 : filters.containsAny)) {
553
+ q = q.or;
554
+ }
555
+ if (mode === (filters === null || filters === void 0 ? void 0 : filters.notContains)) {
556
+ q = q.not;
514
557
  }
515
- return `[${value.join(",")}]`;
558
+ return q;
516
559
  }
517
560
  if (this.client === types_1.SqlClient.POSTGRES) {
518
561
  iterate(mode, types_1.ArrayOperator.CONTAINS, (q, key, value) => {
519
- const wrap = any ? "" : "'";
520
- const op = any ? "\\?| array" : "@>";
521
- const fieldNames = key.split(/\./g);
522
- const table = fieldNames[0];
523
- const col = fieldNames[1];
524
- return q[rawFnc](`${not}COALESCE("${table}"."${col}"::jsonb ${op} ${wrap}${stringifyArray(value, any ? "'" : '"')}${wrap}, FALSE)`);
562
+ q = addModifiers(q);
563
+ if (any) {
564
+ return q.whereRaw(`COALESCE(??::jsonb \\?| array??, FALSE)`, [
565
+ this.rawQuotedIdentifier(key),
566
+ this.knex.raw(stringifyArray(value, "'")),
567
+ ]);
568
+ }
569
+ else {
570
+ return q.whereRaw(`COALESCE(??::jsonb @> '??', FALSE)`, [
571
+ this.rawQuotedIdentifier(key),
572
+ this.knex.raw(stringifyArray(value)),
573
+ ]);
574
+ }
525
575
  });
526
576
  }
527
577
  else if (this.client === types_1.SqlClient.MY_SQL ||
528
578
  this.client === types_1.SqlClient.MARIADB) {
529
- const jsonFnc = any ? "JSON_OVERLAPS" : "JSON_CONTAINS";
530
579
  iterate(mode, types_1.ArrayOperator.CONTAINS, (q, key, value) => {
531
- return q[rawFnc](`${not}COALESCE(${jsonFnc}(${key}, '${stringifyArray(value)}'), FALSE)`);
580
+ return addModifiers(q).whereRaw(`COALESCE(?(??, ?), FALSE)`, [
581
+ this.knex.raw(any ? "JSON_OVERLAPS" : "JSON_CONTAINS"),
582
+ this.rawQuotedIdentifier(key),
583
+ this.knex.raw(wrap(stringifyArray(value))),
584
+ ]);
532
585
  });
533
586
  }
534
587
  else {
535
- const andOr = mode === (filters === null || filters === void 0 ? void 0 : filters.containsAny) ? " OR " : " AND ";
536
588
  iterate(mode, types_1.ArrayOperator.CONTAINS, (q, key, value) => {
537
- let statement = "";
538
- const identifier = this.quotedIdentifier(key);
539
- for (let i in value) {
540
- if (typeof value[i] === "string") {
541
- value[i] = `%"${value[i].toLowerCase()}"%`;
542
- }
543
- else {
544
- value[i] = `%${value[i]}%`;
545
- }
546
- statement += `${statement ? andOr : ""}COALESCE(LOWER(${identifier}), '') LIKE ?`;
547
- }
548
- if (statement === "") {
589
+ if (value.length === 0) {
549
590
  return q;
550
591
  }
551
- if (not) {
552
- return q[rawFnc](`(NOT (${statement}) OR ${identifier} IS NULL)`, value);
553
- }
554
- else {
555
- return q[rawFnc](statement, value);
556
- }
592
+ q = q.where(subQuery => {
593
+ if (mode === (filters === null || filters === void 0 ? void 0 : filters.notContains)) {
594
+ subQuery = subQuery.not;
595
+ }
596
+ subQuery = subQuery.where(subSubQuery => {
597
+ for (const elem of value) {
598
+ if (mode === (filters === null || filters === void 0 ? void 0 : filters.containsAny)) {
599
+ subSubQuery = subSubQuery.or;
600
+ }
601
+ else {
602
+ subSubQuery = subSubQuery.and;
603
+ }
604
+ const lower = typeof elem === "string" ? `"${elem.toLowerCase()}"` : elem;
605
+ subSubQuery = subSubQuery.whereLike(
606
+ // @ts-expect-error knex types are wrong, raw is fine here
607
+ this.knex.raw(`COALESCE(LOWER(??), '')`, [
608
+ this.rawQuotedIdentifier(key),
609
+ ]), `%${lower}%`);
610
+ }
611
+ });
612
+ if (mode === (filters === null || filters === void 0 ? void 0 : filters.notContains)) {
613
+ subQuery = subQuery.or.whereNull(
614
+ // @ts-expect-error knex types are wrong, raw is fine here
615
+ this.rawQuotedIdentifier(key));
616
+ }
617
+ return subQuery;
618
+ });
619
+ return q;
557
620
  });
558
621
  }
559
622
  };
@@ -574,44 +637,41 @@ class InternalBuilder {
574
637
  });
575
638
  }
576
639
  if (filters.oneOf) {
577
- const fnc = allOr ? "orWhereIn" : "whereIn";
578
640
  iterate(filters.oneOf, types_1.ArrayOperator.ONE_OF, (q, key, array) => {
641
+ if (shouldOr) {
642
+ q = q.or;
643
+ }
579
644
  if (this.client === types_1.SqlClient.ORACLE) {
645
+ // @ts-ignore
580
646
  key = this.convertClobs(key);
581
- array = Array.isArray(array) ? array : [array];
582
- const binding = new Array(array.length).fill("?").join(",");
583
- return q.whereRaw(`${key} IN (${binding})`, array);
584
- }
585
- else {
586
- return q[fnc](key, Array.isArray(array) ? array : [array]);
587
647
  }
648
+ return q.whereIn(key, Array.isArray(array) ? array : [array]);
588
649
  }, (q, key, array) => {
589
- if (this.client === types_1.SqlClient.ORACLE) {
590
- const keyStr = `(${key.map(k => this.convertClobs(k)).join(",")})`;
591
- const binding = `(${array
592
- .map((a) => `(${new Array(a.length).fill("?").join(",")})`)
593
- .join(",")})`;
594
- return q.whereRaw(`${keyStr} IN ${binding}`, array.flat());
650
+ if (shouldOr) {
651
+ q = q.or;
595
652
  }
596
- else {
597
- return q[fnc](key, Array.isArray(array) ? array : [array]);
653
+ if (this.client === types_1.SqlClient.ORACLE) {
654
+ // @ts-ignore
655
+ key = key.map(k => this.convertClobs(k));
598
656
  }
657
+ return q.whereIn(key, Array.isArray(array) ? array : [array]);
599
658
  });
600
659
  }
601
660
  if (filters.string) {
602
661
  iterate(filters.string, types_1.BasicOperator.STRING, (q, key, value) => {
603
- const fnc = allOr ? "orWhere" : "where";
604
- // postgres supports ilike, nothing else does
605
- if (this.client === types_1.SqlClient.POSTGRES) {
606
- return q[fnc](key, "ilike", `${value}%`);
662
+ if (shouldOr) {
663
+ q = q.or;
607
664
  }
608
- else {
609
- const rawFnc = `${fnc}Raw`;
610
- // @ts-ignore
611
- return q[rawFnc](`LOWER(${this.quotedIdentifier(key)}) LIKE ?`, [
665
+ if (this.client === types_1.SqlClient.ORACLE ||
666
+ this.client === types_1.SqlClient.SQL_LITE) {
667
+ return q.whereRaw(`LOWER(??) LIKE ?`, [
668
+ this.rawQuotedIdentifier(key),
612
669
  `${value.toLowerCase()}%`,
613
670
  ]);
614
671
  }
672
+ else {
673
+ return q.whereILike(key, `${value}%`);
674
+ }
615
675
  });
616
676
  }
617
677
  if (filters.fuzzy) {
@@ -632,61 +692,58 @@ class InternalBuilder {
632
692
  }
633
693
  const lowValid = (0, utils_1.isValidFilter)(value.low), highValid = (0, utils_1.isValidFilter)(value.high);
634
694
  const schema = this.getFieldSchema(key);
695
+ let rawKey = key;
696
+ let high = value.high;
697
+ let low = value.low;
635
698
  if (this.client === types_1.SqlClient.ORACLE) {
636
- // @ts-ignore
637
- key = this.knex.raw(this.convertClobs(key));
699
+ rawKey = this.convertClobs(key);
700
+ }
701
+ else if (this.client === types_1.SqlClient.SQL_LITE &&
702
+ (schema === null || schema === void 0 ? void 0 : schema.type) === types_1.FieldType.BIGINT) {
703
+ rawKey = this.knex.raw("CAST(?? AS INTEGER)", [
704
+ this.rawQuotedIdentifier(key),
705
+ ]);
706
+ high = this.knex.raw("CAST(? AS INTEGER)", [value.high]);
707
+ low = this.knex.raw("CAST(? AS INTEGER)", [value.low]);
708
+ }
709
+ if (shouldOr) {
710
+ q = q.or;
638
711
  }
639
712
  if (lowValid && highValid) {
640
- if ((schema === null || schema === void 0 ? void 0 : schema.type) === types_1.FieldType.BIGINT &&
641
- this.client === types_1.SqlClient.SQL_LITE) {
642
- return q.whereRaw(`CAST(${key} AS INTEGER) BETWEEN CAST(? AS INTEGER) AND CAST(? AS INTEGER)`, [value.low, value.high]);
643
- }
644
- else {
645
- const fnc = allOr ? "orWhereBetween" : "whereBetween";
646
- return q[fnc](key, [value.low, value.high]);
647
- }
713
+ // @ts-expect-error knex types are wrong, raw is fine here
714
+ return q.whereBetween(rawKey, [low, high]);
648
715
  }
649
716
  else if (lowValid) {
650
- if ((schema === null || schema === void 0 ? void 0 : schema.type) === types_1.FieldType.BIGINT &&
651
- this.client === types_1.SqlClient.SQL_LITE) {
652
- return q.whereRaw(`CAST(${key} AS INTEGER) >= CAST(? AS INTEGER)`, [
653
- value.low,
654
- ]);
655
- }
656
- else {
657
- const fnc = allOr ? "orWhere" : "where";
658
- return q[fnc](key, ">=", value.low);
659
- }
717
+ // @ts-expect-error knex types are wrong, raw is fine here
718
+ return q.where(rawKey, ">=", low);
660
719
  }
661
720
  else if (highValid) {
662
- if ((schema === null || schema === void 0 ? void 0 : schema.type) === types_1.FieldType.BIGINT &&
663
- this.client === types_1.SqlClient.SQL_LITE) {
664
- return q.whereRaw(`CAST(${key} AS INTEGER) <= CAST(? AS INTEGER)`, [
665
- value.high,
666
- ]);
667
- }
668
- else {
669
- const fnc = allOr ? "orWhere" : "where";
670
- return q[fnc](key, "<=", value.high);
671
- }
721
+ // @ts-expect-error knex types are wrong, raw is fine here
722
+ return q.where(rawKey, "<=", high);
672
723
  }
673
724
  return q;
674
725
  });
675
726
  }
676
727
  if (filters.equal) {
677
728
  iterate(filters.equal, types_1.BasicOperator.EQUAL, (q, key, value) => {
678
- const fnc = allOr ? "orWhereRaw" : "whereRaw";
729
+ if (shouldOr) {
730
+ q = q.or;
731
+ }
679
732
  if (this.client === types_1.SqlClient.MS_SQL) {
680
- return q[fnc](`CASE WHEN ${this.quotedIdentifier(key)} = ? THEN 1 ELSE 0 END = 1`, [value]);
733
+ return q.whereRaw(`CASE WHEN ?? = ? THEN 1 ELSE 0 END = 1`, [
734
+ this.rawQuotedIdentifier(key),
735
+ value,
736
+ ]);
681
737
  }
682
738
  else if (this.client === types_1.SqlClient.ORACLE) {
683
739
  const identifier = this.convertClobs(key);
684
- return q[fnc](`(${identifier} IS NOT NULL AND ${identifier} = ?)`, [
685
- value,
686
- ]);
740
+ return q.where(subq =>
741
+ // @ts-expect-error knex types are wrong, raw is fine here
742
+ subq.whereNotNull(identifier).andWhere(identifier, value));
687
743
  }
688
744
  else {
689
- return q[fnc](`COALESCE(${this.quotedIdentifier(key)} = ?, FALSE)`, [
745
+ return q.whereRaw(`COALESCE(?? = ?, FALSE)`, [
746
+ this.rawQuotedIdentifier(key),
690
747
  value,
691
748
  ]);
692
749
  }
@@ -694,16 +751,28 @@ class InternalBuilder {
694
751
  }
695
752
  if (filters.notEqual) {
696
753
  iterate(filters.notEqual, types_1.BasicOperator.NOT_EQUAL, (q, key, value) => {
697
- const fnc = allOr ? "orWhereRaw" : "whereRaw";
754
+ if (shouldOr) {
755
+ q = q.or;
756
+ }
698
757
  if (this.client === types_1.SqlClient.MS_SQL) {
699
- return q[fnc](`CASE WHEN ${this.quotedIdentifier(key)} = ? THEN 1 ELSE 0 END = 0`, [value]);
758
+ return q.whereRaw(`CASE WHEN ?? = ? THEN 1 ELSE 0 END = 0`, [
759
+ this.rawQuotedIdentifier(key),
760
+ value,
761
+ ]);
700
762
  }
701
763
  else if (this.client === types_1.SqlClient.ORACLE) {
702
764
  const identifier = this.convertClobs(key);
703
- return q[fnc](`(${identifier} IS NOT NULL AND ${identifier} != ?) OR ${identifier} IS NULL`, [value]);
765
+ return (q
766
+ .where(subq => subq.not
767
+ // @ts-expect-error knex types are wrong, raw is fine here
768
+ .whereNull(identifier)
769
+ .and.where(identifier, "!=", value))
770
+ // @ts-expect-error knex types are wrong, raw is fine here
771
+ .or.whereNull(identifier));
704
772
  }
705
773
  else {
706
- return q[fnc](`COALESCE(${this.quotedIdentifier(key)} != ?, TRUE)`, [
774
+ return q.whereRaw(`COALESCE(?? != ?, TRUE)`, [
775
+ this.rawQuotedIdentifier(key),
707
776
  value,
708
777
  ]);
709
778
  }
@@ -711,14 +780,18 @@ class InternalBuilder {
711
780
  }
712
781
  if (filters.empty) {
713
782
  iterate(filters.empty, types_1.BasicOperator.EMPTY, (q, key) => {
714
- const fnc = allOr ? "orWhereNull" : "whereNull";
715
- return q[fnc](key);
783
+ if (shouldOr) {
784
+ q = q.or;
785
+ }
786
+ return q.whereNull(key);
716
787
  });
717
788
  }
718
789
  if (filters.notEmpty) {
719
790
  iterate(filters.notEmpty, types_1.BasicOperator.NOT_EMPTY, (q, key) => {
720
- const fnc = allOr ? "orWhereNotNull" : "whereNotNull";
721
- return q[fnc](key);
791
+ if (shouldOr) {
792
+ q = q.or;
793
+ }
794
+ return q.whereNotNull(key);
722
795
  });
723
796
  }
724
797
  if (filters.contains) {
@@ -791,9 +864,7 @@ class InternalBuilder {
791
864
  if (this.client === types_1.SqlClient.ORACLE) {
792
865
  const groupByFields = qualifiedFields.map(field => this.convertClobs(field));
793
866
  const selectFields = qualifiedFields.map(field => this.convertClobs(field, { forSelect: true }));
794
- query = query
795
- .groupByRaw(groupByFields.join(", "))
796
- .select(this.knex.raw(selectFields.join(", ")));
867
+ query = query.groupBy(groupByFields).select(selectFields);
797
868
  }
798
869
  else {
799
870
  query = query.groupBy(qualifiedFields).select(qualifiedFields);
@@ -805,7 +876,10 @@ class InternalBuilder {
805
876
  if ("distinct" in aggregation && aggregation.distinct) {
806
877
  if (this.client === types_1.SqlClient.ORACLE) {
807
878
  const field = this.convertClobs(`${tableName}.${aggregation.field}`);
808
- query = query.select(this.knex.raw(`COUNT(DISTINCT ${field}) as ${this.quotedIdentifier(aggregation.name)}`));
879
+ query = query.select(this.knex.raw(`COUNT(DISTINCT ??) as ??`, [
880
+ field,
881
+ aggregation.name,
882
+ ]));
809
883
  }
810
884
  else {
811
885
  query = query.countDistinct(`${tableName}.${aggregation.field} as ${aggregation.name}`);
@@ -864,7 +938,11 @@ class InternalBuilder {
864
938
  else {
865
939
  let composite = `${aliased}.${key}`;
866
940
  if (this.client === types_1.SqlClient.ORACLE) {
867
- query = query.orderByRaw(`${this.convertClobs(composite)} ${direction} nulls ${nulls}`);
941
+ query = query.orderByRaw(`?? ?? nulls ??`, [
942
+ this.convertClobs(composite),
943
+ this.knex.raw(direction),
944
+ this.knex.raw(nulls),
945
+ ]);
868
946
  }
869
947
  else {
870
948
  query = query.orderBy(composite, direction, nulls);
@@ -889,18 +967,21 @@ class InternalBuilder {
889
967
  }
890
968
  buildJsonField(field) {
891
969
  const parts = field.split(".");
892
- let tableField, unaliased;
970
+ let unaliased;
971
+ let tableField;
893
972
  if (parts.length > 1) {
894
973
  const alias = parts.shift();
895
974
  unaliased = parts.join(".");
896
- tableField = `${this.quote(alias)}.${this.quote(unaliased)}`;
975
+ tableField = `${alias}.${unaliased}`;
897
976
  }
898
977
  else {
899
978
  unaliased = parts.join(".");
900
- tableField = this.quote(unaliased);
979
+ tableField = unaliased;
901
980
  }
902
981
  const separator = this.client === types_1.SqlClient.ORACLE ? " VALUE " : ",";
903
- return `'${unaliased}'${separator}${tableField}`;
982
+ return this.knex
983
+ .raw(`?${separator}??`, [unaliased, this.rawQuotedIdentifier(tableField)])
984
+ .toString();
904
985
  }
905
986
  maxFunctionParameters() {
906
987
  // functions like say json_build_object() in SQL have a limit as to how many can be performed
@@ -969,11 +1050,11 @@ class InternalBuilder {
969
1050
  });
970
1051
  }
971
1052
  // add the correlation to the overall query
972
- subQuery = subQuery.where(correlatedTo, "=", knex.raw(this.quotedIdentifier(correlatedFrom)));
1053
+ subQuery = subQuery.where(correlatedTo, "=", this.rawQuotedIdentifier(correlatedFrom));
973
1054
  const standardWrap = (select) => {
974
1055
  subQuery = subQuery.select(`${toAlias}.*`).limit(getRelationshipLimit());
975
1056
  // @ts-ignore - the from alias syntax isn't in Knex typing
976
- return knex.select(knex.raw(select)).from({
1057
+ return knex.select(select).from({
977
1058
  [toAlias]: subQuery,
978
1059
  });
979
1060
  };
@@ -982,10 +1063,10 @@ class InternalBuilder {
982
1063
  case types_1.SqlClient.SQL_LITE:
983
1064
  // need to check the junction table document is to the right column, this is just for SQS
984
1065
  subQuery = this.addJoinFieldCheck(subQuery, relationship);
985
- wrapperQuery = standardWrap(`json_group_array(json_object(${fieldList}))`);
1066
+ wrapperQuery = standardWrap(this.knex.raw(`json_group_array(json_object(${fieldList}))`));
986
1067
  break;
987
1068
  case types_1.SqlClient.POSTGRES:
988
- wrapperQuery = standardWrap(`json_agg(json_build_object(${fieldList}))`);
1069
+ wrapperQuery = standardWrap(this.knex.raw(`json_agg(json_build_object(${fieldList}))`));
989
1070
  break;
990
1071
  case types_1.SqlClient.MARIADB:
991
1072
  // can't use the standard wrap due to correlated sub-query limitations in MariaDB
@@ -993,18 +1074,20 @@ class InternalBuilder {
993
1074
  break;
994
1075
  case types_1.SqlClient.MY_SQL:
995
1076
  case types_1.SqlClient.ORACLE:
996
- wrapperQuery = standardWrap(`json_arrayagg(json_object(${fieldList}))`);
1077
+ wrapperQuery = standardWrap(this.knex.raw(`json_arrayagg(json_object(${fieldList}))`));
997
1078
  break;
998
- case types_1.SqlClient.MS_SQL:
999
- wrapperQuery = knex.raw(`(SELECT ${this.quote(toAlias)} = (${knex
1079
+ case types_1.SqlClient.MS_SQL: {
1080
+ const comparatorQuery = knex
1000
1081
  .select(`${fromAlias}.*`)
1001
1082
  // @ts-ignore - from alias syntax not TS supported
1002
1083
  .from({
1003
1084
  [fromAlias]: subQuery
1004
1085
  .select(`${toAlias}.*`)
1005
1086
  .limit(getRelationshipLimit()),
1006
- })} FOR JSON PATH))`);
1087
+ });
1088
+ wrapperQuery = knex.raw(`(SELECT ?? = (${comparatorQuery} FOR JSON PATH))`, [this.rawQuotedIdentifier(toAlias)]);
1007
1089
  break;
1090
+ }
1008
1091
  default:
1009
1092
  throw new Error(`JSON relationships not implement for ${sqlClient}`);
1010
1093
  }