@mikro-orm/knex 6.0.0-dev.50 → 6.0.0-dev.51

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.
@@ -638,11 +638,11 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
638
638
  const aliased = qb.ref(tableAlias ? `${tableAlias}__${prop.fieldNames[0]}` : prop.fieldNames[0]).toString();
639
639
  if (prop.customType?.convertToJSValueSQL && tableAlias) {
640
640
  const prefixed = qb.ref(prop.fieldNames[0]).withSchema(tableAlias).toString();
641
- return [prop.customType.convertToJSValueSQL(prefixed, this.platform) + ' as ' + aliased];
641
+ return [(0, core_1.raw)(`${prop.customType.convertToJSValueSQL(prefixed, this.platform)} as ${aliased}`)];
642
642
  }
643
643
  if (prop.formula) {
644
644
  const alias = qb.ref(tableAlias ?? qb.alias).toString();
645
- return [`${prop.formula(alias)} as ${aliased}`];
645
+ return [(0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`)];
646
646
  }
647
647
  if (tableAlias) {
648
648
  return prop.fieldNames.map(fieldName => qb.ref(fieldName).withSchema(tableAlias).as(`${tableAlias}__${fieldName}`));
@@ -739,7 +739,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
739
739
  const meta = (0, core_1.helper)(entity).__meta;
740
740
  const qb = this.createQueryBuilder(entity.constructor.name, options.ctx).unsetFlag(core_1.QueryFlag.CONVERT_CUSTOM_TYPES).withSchema(options.schema ?? meta.schema);
741
741
  const cond = core_1.Utils.getPrimaryKeyCond(entity, meta.primaryKeys);
742
- qb.select('1').where(cond).setLockMode(options.lockMode, options.lockTableAliases);
742
+ qb.select((0, core_1.raw)('1')).where(cond).setLockMode(options.lockMode, options.lockTableAliases);
743
743
  await this.rethrow(qb.execute());
744
744
  }
745
745
  buildJoinedPropsOrderBy(entityName, qb, meta, populate, parentPath) {
@@ -805,6 +805,7 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
805
805
  const requiresSQLConversion = meta.props.some(p => p.customType?.convertToJSValueSQL);
806
806
  const hasExplicitFields = !!fields;
807
807
  const ret = [];
808
+ let addFormulas = false;
808
809
  if (joinedProps.length > 0) {
809
810
  ret.push(...this.getFieldsForJoinedLoad(qb, meta, fields, populate));
810
811
  }
@@ -830,17 +831,19 @@ class AbstractSqlDriver extends core_1.DatabaseDriver {
830
831
  else if (lazyProps.filter(p => !p.formula).length > 0) {
831
832
  const props = meta.props.filter(prop => this.platform.shouldHaveColumn(prop, populate, false));
832
833
  ret.push(...core_1.Utils.flatten(props.filter(p => !lazyProps.includes(p)).map(p => p.fieldNames)));
834
+ addFormulas = true;
833
835
  }
834
836
  else if (hasLazyFormulas || requiresSQLConversion) {
835
837
  ret.push('*');
838
+ addFormulas = true;
836
839
  }
837
- if (ret.length > 0 && !hasExplicitFields) {
840
+ if (ret.length > 0 && !hasExplicitFields && addFormulas) {
838
841
  meta.props
839
842
  .filter(prop => prop.formula && !lazyProps.includes(prop))
840
843
  .forEach(prop => {
841
844
  const alias = qb.ref(qb.alias).toString();
842
845
  const aliased = qb.ref(prop.fieldNames[0]).toString();
843
- ret.push(`${prop.formula(alias)} as ${aliased}`);
846
+ ret.push((0, core_1.raw)(`${prop.formula(alias)} as ${aliased}`));
844
847
  });
845
848
  meta.props
846
849
  .filter(prop => prop.customType?.convertToDatabaseValueSQL || prop.customType?.convertToJSValueSQL)
@@ -48,22 +48,26 @@ class AbstractSqlPlatform extends core_1.Platform {
48
48
  let j = 0;
49
49
  let pos = 0;
50
50
  let ret = '';
51
+ if (sql[0] === '?' && sql[1] !== '?') {
52
+ ret += this.quoteValue(params[j++]);
53
+ pos = 1;
54
+ }
51
55
  while (pos < sql.length) {
52
56
  const idx = sql.indexOf('?', pos + 1);
53
57
  if (idx === -1) {
54
58
  ret += sql.substring(pos, sql.length);
55
59
  break;
56
60
  }
57
- if (sql.substr(idx - 1, 2) === '\\?') {
58
- ret += sql.substr(pos, idx - pos - 1) + '?';
61
+ if (sql.substring(idx - 1, idx + 1) === '\\?') {
62
+ ret += sql.substring(pos, idx - 1) + '?';
59
63
  pos = idx + 1;
60
64
  }
61
- else if (sql.substr(idx, 2) === '??') {
62
- ret += sql.substr(pos, idx - pos) + this.quoteIdentifier(params[j++]);
65
+ else if (sql.substring(idx, idx + 2) === '??') {
66
+ ret += sql.substring(pos, idx) + this.quoteIdentifier(params[j++]);
63
67
  pos = idx + 2;
64
68
  }
65
69
  else {
66
- ret += sql.substr(pos, idx - pos) + this.quoteValue(params[j++]);
70
+ ret += sql.substring(pos, idx) + this.quoteValue(params[j++]);
67
71
  pos = idx + 1;
68
72
  }
69
73
  }
@@ -3,6 +3,7 @@ export declare const MonkeyPatchable: {
3
3
  QueryExecutioner: any;
4
4
  MySqlDialect: any;
5
5
  MySqlColumnCompiler: any;
6
+ MySqlQueryCompiler: any;
6
7
  PostgresDialectTableCompiler: any;
7
8
  Sqlite3Dialect: any;
8
9
  Sqlite3DialectTableCompiler: any;
@@ -13,6 +13,8 @@ const mysql_1 = __importDefault(require("knex/lib/dialects/mysql"));
13
13
  // @ts-ignore
14
14
  const mysql_columncompiler_1 = __importDefault(require("knex/lib/dialects/mysql/schema/mysql-columncompiler"));
15
15
  // @ts-ignore
16
+ const mysql_querycompiler_1 = __importDefault(require("knex/lib/dialects/mysql/query/mysql-querycompiler"));
17
+ // @ts-ignore
16
18
  const pg_tablecompiler_1 = __importDefault(require("knex/lib/dialects/postgres/schema/pg-tablecompiler"));
17
19
  // @ts-ignore
18
20
  const sqlite3_1 = __importDefault(require("knex/lib/dialects/sqlite3"));
@@ -29,6 +31,7 @@ exports.MonkeyPatchable = {
29
31
  QueryExecutioner: query_executioner_1.default,
30
32
  MySqlDialect: mysql_1.default,
31
33
  MySqlColumnCompiler: mysql_columncompiler_1.default,
34
+ MySqlQueryCompiler: mysql_querycompiler_1.default,
32
35
  PostgresDialectTableCompiler: pg_tablecompiler_1.default,
33
36
  Sqlite3Dialect: sqlite3_1.default,
34
37
  Sqlite3DialectTableCompiler: sqlite_tablecompiler_1.default,
package/index.mjs CHANGED
@@ -1,6 +1,8 @@
1
1
  import mod from "./index.js";
2
2
 
3
3
  export default mod;
4
+ export const ALIAS_REPLACEMENT = mod.ALIAS_REPLACEMENT;
5
+ export const ALIAS_REPLACEMENT_RE = mod.ALIAS_REPLACEMENT_RE;
4
6
  export const ARRAY_OPERATORS = mod.ARRAY_OPERATORS;
5
7
  export const AbstractNamingStrategy = mod.AbstractNamingStrategy;
6
8
  export const AbstractSchemaGenerator = mod.AbstractSchemaGenerator;
@@ -186,7 +188,6 @@ export const compareBuffers = mod.compareBuffers;
186
188
  export const compareObjects = mod.compareObjects;
187
189
  export const defineConfig = mod.defineConfig;
188
190
  export const equals = mod.equals;
189
- export const expr = mod.expr;
190
191
  export const helper = mod.helper;
191
192
  export const knex = mod.knex;
192
193
  export const parseJsonSafe = mod.parseJsonSafe;
@@ -194,6 +195,7 @@ export const raw = mod.raw;
194
195
  export const ref = mod.ref;
195
196
  export const rel = mod.rel;
196
197
  export const serialize = mod.serialize;
198
+ export const sql = mod.sql;
197
199
  export const t = mod.t;
198
200
  export const types = mod.types;
199
201
  export const wrap = mod.wrap;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/knex",
3
- "version": "6.0.0-dev.50",
3
+ "version": "6.0.0-dev.51",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -66,6 +66,6 @@
66
66
  "@mikro-orm/core": "^5.6.16"
67
67
  },
68
68
  "peerDependencies": {
69
- "@mikro-orm/core": "~6.0.0-dev.50"
69
+ "@mikro-orm/core": "~6.0.0-dev.51"
70
70
  }
71
71
  }
@@ -21,7 +21,7 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
21
21
  const childNode = this.payload[field];
22
22
  const payload = childNode.process(qb, this.prop ? alias : ownerAlias);
23
23
  const operator = core_1.Utils.isOperator(field);
24
- const customExpression = ObjectCriteriaNode.isCustomExpression(field);
24
+ const isRawField = core_1.RawQueryFragment.isKnownFragment(field);
25
25
  // we need to keep the prefixing for formulas otherwise we would lose aliasing context when nesting inside group operators
26
26
  const virtual = childNode.prop?.persist === false && !childNode.prop?.formula;
27
27
  // if key is missing, we are inside group operator and we need to prefix with alias
@@ -33,8 +33,12 @@ class ObjectCriteriaNode extends CriteriaNode_1.CriteriaNode {
33
33
  else if (childNode.shouldRename(payload)) {
34
34
  o[childNode.renameFieldToPK(qb)] = payload;
35
35
  }
36
- else if (primaryKey || virtual || operator || customExpression || field.includes('.') || ![enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(qb.type ?? enums_1.QueryType.SELECT)) {
37
- o[field.replace(/\[::alias::]/g, alias)] = payload;
36
+ else if (isRawField) {
37
+ const rawField = core_1.RawQueryFragment.getKnownFragment(field);
38
+ o[(0, core_1.raw)(rawField.sql.replaceAll(core_1.ALIAS_REPLACEMENT, alias), rawField.params)] = payload;
39
+ }
40
+ else if (primaryKey || virtual || operator || field.includes('.') || ![enums_1.QueryType.SELECT, enums_1.QueryType.COUNT].includes(qb.type ?? enums_1.QueryType.SELECT)) {
41
+ o[field.replaceAll(core_1.ALIAS_REPLACEMENT, alias)] = payload;
38
42
  }
39
43
  else {
40
44
  o[`${alias}.${field}`] = payload;
@@ -29,6 +29,7 @@ import type { Field } from '../typings';
29
29
  * ```
30
30
  */
31
31
  export declare class QueryBuilder<T extends object = AnyEntity> {
32
+ #private;
32
33
  private readonly metadata;
33
34
  private readonly driver;
34
35
  private readonly context?;
@@ -139,6 +140,11 @@ export declare class QueryBuilder<T extends object = AnyEntity> {
139
140
  * Returns the query with parameters as wildcards.
140
141
  */
141
142
  getQuery(): string;
143
+ toQuery(): {
144
+ sql: string;
145
+ _sql: Knex.Sql;
146
+ params: readonly unknown[];
147
+ };
142
148
  /**
143
149
  * Returns the list of all parameters for this query.
144
150
  */
@@ -1,4 +1,16 @@
1
1
  "use strict";
2
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
+ };
7
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
+ };
13
+ var _QueryBuilder_query;
2
14
  Object.defineProperty(exports, "__esModule", { value: true });
3
15
  exports.QueryBuilder = void 0;
4
16
  const util_1 = require("util");
@@ -64,6 +76,7 @@ class QueryBuilder {
64
76
  this._aliases = {};
65
77
  this.platform = this.driver.getPlatform();
66
78
  this.knex = this.driver.getConnection(this.connectionType).getKnex();
79
+ _QueryBuilder_query.set(this, void 0);
67
80
  if (alias) {
68
81
  this.aliasCounter++;
69
82
  this._explicitAlias = true;
@@ -165,9 +178,15 @@ class QueryBuilder {
165
178
  }
166
179
  where(cond, params, operator) {
167
180
  this.ensureNotFinalized();
168
- if (core_1.Utils.isString(cond)) {
169
- cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
170
- operator = operator || '$and';
181
+ const rawField = core_1.RawQueryFragment.getKnownFragment(cond);
182
+ if (rawField) {
183
+ const sql = this.platform.formatQuery(rawField.sql, rawField.params);
184
+ cond = { [(0, core_1.raw)(`(${sql})`)]: core_1.Utils.asArray(params) };
185
+ operator ??= '$and';
186
+ }
187
+ else if (core_1.Utils.isString(cond)) {
188
+ cond = { [(0, core_1.raw)(`(${cond})`, core_1.Utils.asArray(params))]: [] };
189
+ operator ??= '$and';
171
190
  }
172
191
  else {
173
192
  cond = core_1.QueryHelper.processWhere({
@@ -235,7 +254,7 @@ class QueryBuilder {
235
254
  having(cond = {}, params) {
236
255
  this.ensureNotFinalized();
237
256
  if (core_1.Utils.isString(cond)) {
238
- cond = { [`(${cond})`]: core_1.Utils.asArray(params) };
257
+ cond = { [(0, core_1.raw)(`(${cond})`, params)]: [] };
239
258
  }
240
259
  this._having = CriteriaNodeFactory_1.CriteriaNodeFactory.createNode(this.metadata, this.mainAlias.entityName, cond).process(this);
241
260
  return this;
@@ -376,19 +395,27 @@ class QueryBuilder {
376
395
  * Returns the query with parameters as wildcards.
377
396
  */
378
397
  getQuery() {
379
- return this.getKnexQuery().toSQL().toNative().sql;
398
+ return this.toQuery().sql;
399
+ }
400
+ toQuery() {
401
+ if (__classPrivateFieldGet(this, _QueryBuilder_query, "f")) {
402
+ return __classPrivateFieldGet(this, _QueryBuilder_query, "f");
403
+ }
404
+ const sql = this.getKnexQuery().toSQL();
405
+ const query = sql.toNative();
406
+ return __classPrivateFieldSet(this, _QueryBuilder_query, { sql: query.sql, params: query.bindings ?? [], _sql: sql }, "f");
380
407
  }
381
408
  /**
382
409
  * Returns the list of all parameters for this query.
383
410
  */
384
411
  getParams() {
385
- return this.getKnexQuery().toSQL().toNative().bindings;
412
+ return this.toQuery().params;
386
413
  }
387
414
  /**
388
415
  * Returns raw interpolated query string with all the parameters inlined.
389
416
  */
390
417
  getFormattedQuery() {
391
- const query = this.getKnexQuery().toSQL();
418
+ const query = this.toQuery()._sql;
392
419
  return this.platform.formatQuery(query.sql, query.bindings);
393
420
  }
394
421
  /**
@@ -632,6 +659,12 @@ class QueryBuilder {
632
659
  prepareFields(fields, type = 'where') {
633
660
  const ret = [];
634
661
  fields.forEach(field => {
662
+ const rawField = core_1.RawQueryFragment.getKnownFragment(field);
663
+ if (rawField) {
664
+ const sql = this.platform.formatQuery(rawField.sql, rawField.params);
665
+ ret.push(this.knex.raw(sql));
666
+ return;
667
+ }
635
668
  if (!core_1.Utils.isString(field)) {
636
669
  ret.push(field);
637
670
  return;
@@ -799,8 +832,13 @@ class QueryBuilder {
799
832
  const aliased = this.knex.ref(prop.fieldNames[0]).toString();
800
833
  return `${prop.formula(alias)} as ${aliased}`;
801
834
  })
802
- .filter(field => !this._fields.includes(field))
803
- .forEach(field => this.addSelect(field));
835
+ .filter(field => !this._fields.some(f => {
836
+ if (f instanceof core_1.RawQueryFragment) {
837
+ return f.sql === field && f.params.length === 0;
838
+ }
839
+ return f === field;
840
+ }))
841
+ .forEach(field => this._fields.push((0, core_1.raw)(field)));
804
842
  }
805
843
  this.processPopulateWhere();
806
844
  core_1.QueryHelper.processObjectParams(this._data);
@@ -877,7 +915,7 @@ class QueryBuilder {
877
915
  if (!prop?.persist && !prop?.formula) {
878
916
  addToSelect.push(fieldName);
879
917
  }
880
- orderBy.push({ [`min(${this.ref(fieldName)}${type})`]: direction });
918
+ orderBy.push({ [(0, core_1.raw)(`min(${this.ref(fieldName)}${type})`)]: direction });
881
919
  }
882
920
  }
883
921
  subQuery.orderBy(orderBy);
@@ -969,7 +1007,7 @@ class QueryBuilder {
969
1007
  }
970
1008
  }
971
1009
  /* istanbul ignore next */
972
- [util_1.inspect.custom](depth) {
1010
+ [(_QueryBuilder_query = new WeakMap(), util_1.inspect.custom)](depth) {
973
1011
  const object = { ...this };
974
1012
  const hidden = ['metadata', 'driver', 'context', 'platform', 'knex', 'type'];
975
1013
  Object.keys(object).filter(k => k.startsWith('_')).forEach(k => delete object[k]);
@@ -43,7 +43,6 @@ export declare class QueryBuilderHelper {
43
43
  }[], qb: Knex.QueryBuilder): void;
44
44
  appendQueryCondition(type: QueryType, cond: any, qb: Knex.QueryBuilder, operator?: '$and' | '$or', method?: 'where' | 'having'): void;
45
45
  private appendQuerySubCondition;
46
- private processCustomExpression;
47
46
  private processObjectSubCondition;
48
47
  private getOperatorReplacement;
49
48
  getQueryOrder(type: QueryType, orderBy: FlatQueryOrderMap | FlatQueryOrderMap[], populate: Dictionary<string>): string;
@@ -52,7 +51,6 @@ export declare class QueryBuilderHelper {
52
51
  splitField<T>(field: EntityKey<T>, greedyAlias?: boolean): [string, EntityKey<T>];
53
52
  getLockSQL(qb: Knex.QueryBuilder, lockMode: LockMode, lockTables?: string[]): void;
54
53
  updateVersionProperty(qb: Knex.QueryBuilder, data: Dictionary): void;
55
- static isCustomExpression(field: string, hasAlias?: boolean): boolean;
56
54
  private prefix;
57
55
  private appendGroupCondition;
58
56
  private isPrefixed;
@@ -20,7 +20,7 @@ class QueryBuilderHelper {
20
20
  }
21
21
  mapper(field, type = enums_1.QueryType.SELECT, value, alias) {
22
22
  if (core_1.Utils.isRawSql(field)) {
23
- return this.platform.formatQuery(field.sql, field.params ?? []);
23
+ return this.knex.raw(field.sql, field.params);
24
24
  }
25
25
  if (typeof field !== 'string') {
26
26
  return field;
@@ -52,10 +52,13 @@ class QueryBuilderHelper {
52
52
  }
53
53
  return this.knex.raw('(' + parts.map(part => this.knex.ref(part)).join(', ') + ')');
54
54
  }
55
- let ret = field;
56
- const customExpression = QueryBuilderHelper.isCustomExpression(field, !!alias);
55
+ const rawField = core_1.RawQueryFragment.getKnownFragment(field);
56
+ if (rawField) {
57
+ return this.knex.raw(rawField.sql, rawField.params);
58
+ }
57
59
  const [a, f] = this.splitField(field);
58
60
  const prop = this.getProperty(f, a);
61
+ let ret = field;
59
62
  // embeddable nested path instead of a regular property with table alias, reset alias
60
63
  if (prop?.name === a && prop.embeddedProps[f]) {
61
64
  return this.alias + '.' + prop.fieldNames[0];
@@ -79,16 +82,12 @@ class QueryBuilderHelper {
79
82
  return this.knex.raw(`${valueSQL} as ${this.platform.quoteIdentifier(alias ?? prop.fieldNames[0])}`);
80
83
  }
81
84
  // do not wrap custom expressions
82
- if (!customExpression) {
85
+ if (!rawField) {
83
86
  ret = this.prefix(field);
84
87
  }
85
88
  if (alias) {
86
89
  ret += ' as ' + alias;
87
90
  }
88
- if (customExpression) {
89
- const bindings = core_1.Utils.asArray(value).slice(0, ret.match(/\?/g)?.length ?? 0);
90
- return this.knex.raw(ret, bindings);
91
- }
92
91
  if (!isTableNameAliasRequired || this.isPrefixed(ret) || noPrefix) {
93
92
  return ret;
94
93
  }
@@ -227,20 +226,14 @@ class QueryBuilderHelper {
227
226
  if (operator === '$exists') {
228
227
  value = null;
229
228
  }
230
- if (QueryBuilderHelper.isCustomExpression(key)) {
231
- // unwind parameters when ? found in field name
232
- const count = key.concat('?').match(/\?/g).length - 1;
233
- const values = core_1.Utils.asArray(value);
234
- const params1 = values.slice(0, count).map((c) => core_1.Utils.isObject(c) ? JSON.stringify(c) : c);
235
- const params2 = values.slice(count);
236
- const k = this.mapper(key, enums_1.QueryType.SELECT, params1);
237
- const { sql, bindings } = k.toSQL().toNative();
238
- if (params2.length > 0) {
239
- params.push(...bindings);
240
- params.push(...params2);
241
- return sql + ' = ?';
229
+ const rawField = core_1.RawQueryFragment.getKnownFragment(key);
230
+ if (rawField) {
231
+ let sql = rawField.sql;
232
+ params.push(...rawField.params);
233
+ params.push(...core_1.Utils.asArray(value));
234
+ if (core_1.Utils.asArray(value).length > 0) {
235
+ sql += ' = ?';
242
236
  }
243
- params.push(...bindings);
244
237
  return sql;
245
238
  }
246
239
  const sql = this.mapper(key);
@@ -351,28 +344,20 @@ class QueryBuilderHelper {
351
344
  if (core_1.Utils.isPlainObject(cond[key]) || cond[key] instanceof RegExp) {
352
345
  return this.processObjectSubCondition(cond, key, qb, method, m, type);
353
346
  }
354
- if (QueryBuilderHelper.isCustomExpression(key)) {
355
- return this.processCustomExpression(qb, m, key, cond, type);
356
- }
357
347
  const op = cond[key] === null ? 'is' : '=';
348
+ const raw = core_1.RawQueryFragment.getKnownFragment(key);
349
+ if (raw) {
350
+ const value = core_1.Utils.asArray(cond[key]);
351
+ if (value.length > 0) {
352
+ return void qb[m](this.knex.raw(raw.sql, raw.params), op, value[0]);
353
+ }
354
+ return void qb[m](this.knex.raw(raw.sql, raw.params));
355
+ }
358
356
  if (this.subQueries[key]) {
359
357
  return void qb[m](this.knex.raw(`(${this.subQueries[key]})`), op, cond[key]);
360
358
  }
361
359
  qb[m](this.mapper(key, type, cond[key], null), op, cond[key]);
362
360
  }
363
- processCustomExpression(clause, m, key, cond, type) {
364
- // unwind parameters when ? found in field name
365
- const count = key.concat('?').match(/\?/g).length - 1;
366
- const value = core_1.Utils.asArray(cond[key]);
367
- const params1 = value.slice(0, count).map((c) => core_1.Utils.isObject(c) ? JSON.stringify(c) : c);
368
- const params2 = value.slice(count);
369
- const k = this.mapper(key, type, params1);
370
- if (params2.length > 0) {
371
- const val = params2.length === 1 && params2[0] === null ? null : this.knex.raw('?', params2);
372
- return void clause[m](k, val);
373
- }
374
- clause[m](k);
375
- }
376
361
  processObjectSubCondition(cond, key, qb, method, m, type) {
377
362
  // grouped condition for one field
378
363
  let value = cond[key];
@@ -438,19 +423,20 @@ class QueryBuilderHelper {
438
423
  }
439
424
  getQueryOrderFromObject(type, orderBy, populate) {
440
425
  const ret = [];
441
- Object.keys(orderBy).forEach(k => {
442
- const direction = orderBy[k];
426
+ Object.keys(orderBy).forEach(key => {
427
+ const direction = orderBy[key];
443
428
  const order = core_1.Utils.isNumber(direction) ? core_1.QueryOrderNumeric[direction] : direction;
444
- if (QueryBuilderHelper.isCustomExpression(k)) {
445
- ret.push(`${k} ${order.toLowerCase()}`);
429
+ const raw = core_1.RawQueryFragment.getKnownFragment(key);
430
+ if (raw) {
431
+ ret.push(`${this.platform.formatQuery(raw.sql, raw.params)} ${order.toLowerCase()}`);
446
432
  return;
447
433
  }
448
- core_1.Utils.splitPrimaryKeys(k).forEach(f => {
434
+ core_1.Utils.splitPrimaryKeys(key).forEach(f => {
449
435
  // eslint-disable-next-line prefer-const
450
436
  let [alias, field] = this.splitField(f, true);
451
437
  alias = populate[alias] || alias;
452
438
  const prop = this.getProperty(field, alias);
453
- const noPrefix = (prop && prop.persist === false && !prop.formula) || QueryBuilderHelper.isCustomExpression(f);
439
+ const noPrefix = (prop && prop.persist === false && !prop.formula) || core_1.RawQueryFragment.isKnownFragment(f);
454
440
  const column = this.mapper(noPrefix ? field : `${alias}.${field}`, type, undefined, null);
455
441
  /* istanbul ignore next */
456
442
  const rawColumn = core_1.Utils.isString(column) ? column.split('.').map(e => this.knex.ref(e)).join('.') : column;
@@ -522,20 +508,20 @@ class QueryBuilderHelper {
522
508
  }
523
509
  qb.update(versionProperty.fieldNames[0], this.knex.raw(sql));
524
510
  }
525
- static isCustomExpression(field, hasAlias = false) {
526
- // if we do not have alias, we don't consider spaces as custom expressions
527
- const re = hasAlias ? /[ ?<>=()'"`:]|^\d/ : /[?<>=()'"`:]|^\d/;
528
- return !!field.match(re);
529
- }
530
511
  prefix(field, always = false, quote = false) {
531
512
  let ret;
532
513
  if (!this.isPrefixed(field)) {
533
514
  const alias = always ? (quote ? this.alias : this.platform.quoteIdentifier(this.alias)) + '.' : '';
534
515
  const fieldName = this.fieldName(field, this.alias, always);
535
- if (QueryBuilderHelper.isCustomExpression(fieldName)) {
536
- return fieldName;
516
+ if (fieldName instanceof core_1.RawQueryFragment) {
517
+ return fieldName.sql;
518
+ }
519
+ if (this.isPrefixed(fieldName)) {
520
+ ret = fieldName;
521
+ }
522
+ else {
523
+ ret = alias + fieldName;
537
524
  }
538
- ret = alias + fieldName;
539
525
  }
540
526
  else {
541
527
  const [a, ...rest] = field.split('.');
@@ -573,15 +559,15 @@ class QueryBuilderHelper {
573
559
  }
574
560
  if (prop.fieldNameRaw) {
575
561
  if (!always) {
576
- return prop.fieldNameRaw
577
- .replace(/\[::alias::]\.?/g, '')
578
- .replace(this.platform.quoteIdentifier('') + '.', '');
562
+ return (0, core_1.raw)(prop.fieldNameRaw
563
+ .replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE + '\\.?', 'g'), '')
564
+ .replace(this.platform.quoteIdentifier('') + '.', ''));
579
565
  }
580
566
  if (alias) {
581
- return prop.fieldNameRaw.replace(/\[::alias::]/g, alias);
567
+ return (0, core_1.raw)(prop.fieldNameRaw.replace(new RegExp(core_1.ALIAS_REPLACEMENT_RE, 'g'), alias));
582
568
  }
583
569
  /* istanbul ignore next */
584
- return prop.fieldNameRaw;
570
+ return (0, core_1.raw)(prop.fieldNameRaw);
585
571
  }
586
572
  /* istanbul ignore next */
587
573
  return prop.fieldNames?.[0] ?? field;