@uql/core 3.1.1 → 3.1.2

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.
Files changed (171) hide show
  1. package/CHANGELOG.md +134 -187
  2. package/package.json +31 -26
  3. package/dist/CHANGELOG.md +0 -186
  4. package/dist/package.json +0 -131
  5. package/src/@types/index.d.ts +0 -1
  6. package/src/@types/jest.d.ts +0 -6
  7. package/src/browser/http/bus.spec.ts +0 -22
  8. package/src/browser/http/bus.ts +0 -17
  9. package/src/browser/http/http.spec.ts +0 -70
  10. package/src/browser/http/http.ts +0 -55
  11. package/src/browser/http/index.ts +0 -2
  12. package/src/browser/index.ts +0 -4
  13. package/src/browser/options.spec.ts +0 -37
  14. package/src/browser/options.ts +0 -18
  15. package/src/browser/querier/genericClientRepository.spec.ts +0 -105
  16. package/src/browser/querier/genericClientRepository.ts +0 -49
  17. package/src/browser/querier/httpQuerier.ts +0 -82
  18. package/src/browser/querier/index.ts +0 -3
  19. package/src/browser/querier/querier.util.spec.ts +0 -35
  20. package/src/browser/querier/querier.util.ts +0 -18
  21. package/src/browser/type/clientQuerier.ts +0 -45
  22. package/src/browser/type/clientQuerierPool.ts +0 -5
  23. package/src/browser/type/clientRepository.ts +0 -22
  24. package/src/browser/type/index.ts +0 -4
  25. package/src/browser/type/request.ts +0 -25
  26. package/src/dialect/abstractDialect.ts +0 -28
  27. package/src/dialect/abstractSqlDialect-spec.ts +0 -1309
  28. package/src/dialect/abstractSqlDialect.ts +0 -805
  29. package/src/dialect/index.ts +0 -3
  30. package/src/dialect/namingStrategy.spec.ts +0 -52
  31. package/src/dialect/queryContext.ts +0 -69
  32. package/src/entity/decorator/definition.spec.ts +0 -736
  33. package/src/entity/decorator/definition.ts +0 -265
  34. package/src/entity/decorator/entity.ts +0 -8
  35. package/src/entity/decorator/field.ts +0 -9
  36. package/src/entity/decorator/id.ts +0 -9
  37. package/src/entity/decorator/index.ts +0 -5
  38. package/src/entity/decorator/relation.spec.ts +0 -41
  39. package/src/entity/decorator/relation.ts +0 -34
  40. package/src/entity/index.ts +0 -1
  41. package/src/express/@types/express.d.ts +0 -8
  42. package/src/express/@types/index.d.ts +0 -1
  43. package/src/express/index.ts +0 -2
  44. package/src/express/querierMiddleware.ts +0 -217
  45. package/src/express/query.util.spec.ts +0 -40
  46. package/src/express/query.util.ts +0 -21
  47. package/src/index.ts +0 -9
  48. package/src/maria/index.ts +0 -3
  49. package/src/maria/mariaDialect.spec.ts +0 -207
  50. package/src/maria/mariaDialect.ts +0 -42
  51. package/src/maria/mariaQuerierPool.test.ts +0 -23
  52. package/src/maria/mariadbQuerier.test.ts +0 -23
  53. package/src/maria/mariadbQuerier.ts +0 -45
  54. package/src/maria/mariadbQuerierPool.ts +0 -21
  55. package/src/migrate/cli.ts +0 -301
  56. package/src/migrate/generator/index.ts +0 -4
  57. package/src/migrate/generator/mongoSchemaGenerator.spec.ts +0 -112
  58. package/src/migrate/generator/mongoSchemaGenerator.ts +0 -115
  59. package/src/migrate/generator/mysqlSchemaGenerator.spec.ts +0 -34
  60. package/src/migrate/generator/mysqlSchemaGenerator.ts +0 -92
  61. package/src/migrate/generator/postgresSchemaGenerator.spec.ts +0 -44
  62. package/src/migrate/generator/postgresSchemaGenerator.ts +0 -127
  63. package/src/migrate/generator/sqliteSchemaGenerator.spec.ts +0 -33
  64. package/src/migrate/generator/sqliteSchemaGenerator.ts +0 -81
  65. package/src/migrate/index.ts +0 -41
  66. package/src/migrate/introspection/index.ts +0 -4
  67. package/src/migrate/introspection/mongoIntrospector.spec.ts +0 -75
  68. package/src/migrate/introspection/mongoIntrospector.ts +0 -47
  69. package/src/migrate/introspection/mysqlIntrospector.spec.ts +0 -113
  70. package/src/migrate/introspection/mysqlIntrospector.ts +0 -278
  71. package/src/migrate/introspection/postgresIntrospector.spec.ts +0 -112
  72. package/src/migrate/introspection/postgresIntrospector.ts +0 -329
  73. package/src/migrate/introspection/sqliteIntrospector.spec.ts +0 -112
  74. package/src/migrate/introspection/sqliteIntrospector.ts +0 -296
  75. package/src/migrate/migrator-mongo.test.ts +0 -54
  76. package/src/migrate/migrator.spec.ts +0 -255
  77. package/src/migrate/migrator.test.ts +0 -94
  78. package/src/migrate/migrator.ts +0 -719
  79. package/src/migrate/namingStrategy.spec.ts +0 -22
  80. package/src/migrate/schemaGenerator-advanced.spec.ts +0 -138
  81. package/src/migrate/schemaGenerator.spec.ts +0 -190
  82. package/src/migrate/schemaGenerator.ts +0 -478
  83. package/src/migrate/storage/databaseStorage.spec.ts +0 -69
  84. package/src/migrate/storage/databaseStorage.ts +0 -100
  85. package/src/migrate/storage/index.ts +0 -2
  86. package/src/migrate/storage/jsonStorage.ts +0 -58
  87. package/src/migrate/type.ts +0 -1
  88. package/src/mongo/index.ts +0 -3
  89. package/src/mongo/mongoDialect.spec.ts +0 -251
  90. package/src/mongo/mongoDialect.ts +0 -238
  91. package/src/mongo/mongodbQuerier.test.ts +0 -45
  92. package/src/mongo/mongodbQuerier.ts +0 -256
  93. package/src/mongo/mongodbQuerierPool.test.ts +0 -25
  94. package/src/mongo/mongodbQuerierPool.ts +0 -24
  95. package/src/mysql/index.ts +0 -3
  96. package/src/mysql/mysql2Querier.test.ts +0 -20
  97. package/src/mysql/mysql2Querier.ts +0 -49
  98. package/src/mysql/mysql2QuerierPool.test.ts +0 -20
  99. package/src/mysql/mysql2QuerierPool.ts +0 -21
  100. package/src/mysql/mysqlDialect.spec.ts +0 -20
  101. package/src/mysql/mysqlDialect.ts +0 -16
  102. package/src/namingStrategy/defaultNamingStrategy.ts +0 -18
  103. package/src/namingStrategy/index.spec.ts +0 -36
  104. package/src/namingStrategy/index.ts +0 -2
  105. package/src/namingStrategy/snakeCaseNamingStrategy.ts +0 -15
  106. package/src/options.spec.ts +0 -41
  107. package/src/options.ts +0 -18
  108. package/src/postgres/index.ts +0 -3
  109. package/src/postgres/manual-types.d.ts +0 -4
  110. package/src/postgres/pgQuerier.test.ts +0 -25
  111. package/src/postgres/pgQuerier.ts +0 -45
  112. package/src/postgres/pgQuerierPool.test.ts +0 -28
  113. package/src/postgres/pgQuerierPool.ts +0 -21
  114. package/src/postgres/postgresDialect.spec.ts +0 -428
  115. package/src/postgres/postgresDialect.ts +0 -144
  116. package/src/querier/abstractQuerier-test.ts +0 -584
  117. package/src/querier/abstractQuerier.ts +0 -353
  118. package/src/querier/abstractQuerierPool-test.ts +0 -20
  119. package/src/querier/abstractQuerierPool.ts +0 -18
  120. package/src/querier/abstractSqlQuerier-spec.ts +0 -979
  121. package/src/querier/abstractSqlQuerier-test.ts +0 -21
  122. package/src/querier/abstractSqlQuerier.ts +0 -138
  123. package/src/querier/decorator/index.ts +0 -3
  124. package/src/querier/decorator/injectQuerier.spec.ts +0 -74
  125. package/src/querier/decorator/injectQuerier.ts +0 -45
  126. package/src/querier/decorator/serialized.spec.ts +0 -98
  127. package/src/querier/decorator/serialized.ts +0 -13
  128. package/src/querier/decorator/transactional.spec.ts +0 -240
  129. package/src/querier/decorator/transactional.ts +0 -56
  130. package/src/querier/index.ts +0 -4
  131. package/src/repository/genericRepository.spec.ts +0 -111
  132. package/src/repository/genericRepository.ts +0 -74
  133. package/src/repository/index.ts +0 -1
  134. package/src/sqlite/index.ts +0 -3
  135. package/src/sqlite/manual-types.d.ts +0 -4
  136. package/src/sqlite/sqliteDialect.spec.ts +0 -155
  137. package/src/sqlite/sqliteDialect.ts +0 -76
  138. package/src/sqlite/sqliteQuerier.spec.ts +0 -36
  139. package/src/sqlite/sqliteQuerier.test.ts +0 -21
  140. package/src/sqlite/sqliteQuerier.ts +0 -37
  141. package/src/sqlite/sqliteQuerierPool.test.ts +0 -12
  142. package/src/sqlite/sqliteQuerierPool.ts +0 -38
  143. package/src/test/entityMock.ts +0 -375
  144. package/src/test/index.ts +0 -3
  145. package/src/test/it.util.ts +0 -69
  146. package/src/test/spec.util.ts +0 -57
  147. package/src/type/entity.ts +0 -218
  148. package/src/type/index.ts +0 -9
  149. package/src/type/migration.ts +0 -241
  150. package/src/type/namingStrategy.ts +0 -17
  151. package/src/type/querier.ts +0 -143
  152. package/src/type/querierPool.ts +0 -26
  153. package/src/type/query.ts +0 -506
  154. package/src/type/repository.ts +0 -142
  155. package/src/type/universalQuerier.ts +0 -133
  156. package/src/type/utility.ts +0 -21
  157. package/src/util/dialect.util-extra.spec.ts +0 -96
  158. package/src/util/dialect.util.spec.ts +0 -23
  159. package/src/util/dialect.util.ts +0 -134
  160. package/src/util/index.ts +0 -5
  161. package/src/util/object.util.spec.ts +0 -29
  162. package/src/util/object.util.ts +0 -27
  163. package/src/util/raw.ts +0 -11
  164. package/src/util/sql.util-extra.spec.ts +0 -17
  165. package/src/util/sql.util.spec.ts +0 -208
  166. package/src/util/sql.util.ts +0 -104
  167. package/src/util/string.util.spec.ts +0 -46
  168. package/src/util/string.util.ts +0 -35
  169. package/tsconfig.build.json +0 -5
  170. package/tsconfig.json +0 -8
  171. /package/{dist/README.md → README.md} +0 -0
@@ -1,805 +0,0 @@
1
- import { getMeta } from '../entity/index.js';
2
- import {
3
- type EntityMeta,
4
- type FieldKey,
5
- type FieldOptions,
6
- type NamingStrategy,
7
- type Query,
8
- type QueryComparisonOptions,
9
- type QueryConflictPaths,
10
- type QueryContext,
11
- type QueryDialect,
12
- type QueryOptions,
13
- type QueryPager,
14
- QueryRaw,
15
- type QueryRawFnOptions,
16
- type QuerySearch,
17
- type QuerySelect,
18
- type QuerySelectArray,
19
- type QuerySelectOptions,
20
- type QuerySort,
21
- type QuerySortDirection,
22
- type QueryTextSearchOptions,
23
- type QueryWhere,
24
- type QueryWhereArray,
25
- type QueryWhereFieldOperatorMap,
26
- type QueryWhereMap,
27
- type QueryWhereOptions,
28
- type SqlQueryDialect,
29
- type Type,
30
- } from '../type/index.js';
31
-
32
- import {
33
- buildSortMap,
34
- buldQueryWhereAsMap,
35
- type CallbackKey,
36
- escapeSqlId,
37
- fillOnFields,
38
- filterFieldKeys,
39
- filterRelationKeys,
40
- flatObject,
41
- getFieldCallbackValue,
42
- getFieldKeys,
43
- getKeys,
44
- hasKeys,
45
- isSelectingRelations,
46
- raw,
47
- } from '../util/index.js';
48
-
49
- import { AbstractDialect } from './abstractDialect.js';
50
- import { SqlQueryContext } from './queryContext.js';
51
-
52
- export abstract class AbstractSqlDialect extends AbstractDialect implements QueryDialect, SqlQueryDialect {
53
- constructor(
54
- namingStrategy?: NamingStrategy,
55
- readonly escapeIdChar: '`' | '"' = '`',
56
- readonly beginTransactionCommand: string = 'START TRANSACTION',
57
- readonly commitTransactionCommand: string = 'COMMIT',
58
- readonly rollbackTransactionCommand: string = 'ROLLBACK',
59
- ) {
60
- super(namingStrategy);
61
- }
62
-
63
- createContext(): QueryContext {
64
- return new SqlQueryContext(this);
65
- }
66
-
67
- addValue(values: unknown[], value: unknown): string {
68
- values.push(value ?? null);
69
- return this.placeholder(values.length);
70
- }
71
-
72
- placeholder(_index: number): string {
73
- return '?';
74
- }
75
-
76
- returningId<E>(entity: Type<E>): string {
77
- const meta = getMeta(entity);
78
- const idName = this.resolveColumnName(meta.id, meta.fields[meta.id]);
79
- return `RETURNING ${this.escapeId(idName)} ${this.escapeId('id')}`;
80
- }
81
-
82
- search<E>(ctx: QueryContext, entity: Type<E>, q: Query<E> = {}, opts: QueryOptions = {}): void {
83
- const meta = getMeta(entity);
84
- const tableName = this.resolveTableName(entity, meta);
85
- const prefix = (opts.prefix ?? (opts.autoPrefix || isSelectingRelations(meta, q.$select))) ? tableName : undefined;
86
- opts = { ...opts, prefix };
87
- this.where<E>(ctx, entity, q.$where, opts);
88
- this.sort<E>(ctx, entity, q.$sort, opts);
89
- this.pager(ctx, q);
90
- }
91
-
92
- selectFields<E>(ctx: QueryContext, entity: Type<E>, select: QuerySelect<E>, opts: QuerySelectOptions = {}): void {
93
- const meta = getMeta(entity);
94
- const prefix = opts.prefix ? opts.prefix + '.' : '';
95
- const escapedPrefix = this.escapeId(opts.prefix, true, true);
96
-
97
- let selectArr: QuerySelectArray<E>;
98
-
99
- if (select) {
100
- if (Array.isArray(select)) {
101
- selectArr = select;
102
- } else {
103
- const selectPositive = getKeys(select).filter((it) => select[it]) as FieldKey<E>[];
104
- selectArr = selectPositive.length
105
- ? selectPositive
106
- : (getFieldKeys(meta.fields).filter((it) => !(it in select)) as FieldKey<E>[]);
107
- }
108
- selectArr = selectArr.filter((it) => it instanceof QueryRaw || it in meta.fields);
109
- if (opts.prefix && !selectArr.includes(meta.id)) {
110
- selectArr = [meta.id, ...selectArr];
111
- }
112
- } else {
113
- selectArr = getFieldKeys(meta.fields) as FieldKey<E>[];
114
- }
115
-
116
- if (!selectArr.length) {
117
- ctx.append(escapedPrefix + '*');
118
- return;
119
- }
120
-
121
- selectArr.forEach((key, index) => {
122
- if (index > 0) ctx.append(', ');
123
- if (key instanceof QueryRaw) {
124
- this.getRawValue(ctx, {
125
- value: key,
126
- prefix: opts.prefix,
127
- escapedPrefix,
128
- autoPrefixAlias: opts.autoPrefixAlias,
129
- });
130
- } else {
131
- const field = meta.fields[key as FieldKey<E>];
132
- const columnName = this.resolveColumnName(key, field);
133
- if (field.virtual) {
134
- this.getRawValue(ctx, {
135
- value: raw(field.virtual.value, key as string),
136
- prefix: opts.prefix,
137
- escapedPrefix,
138
- autoPrefixAlias: opts.autoPrefixAlias,
139
- });
140
- } else {
141
- ctx.append(escapedPrefix + this.escapeId(columnName));
142
- }
143
- if (!field.virtual && (columnName !== key || opts.autoPrefixAlias)) {
144
- const aliasStr = (prefix + key) as string;
145
- // Replace dots with underscores for alias to avoid syntax errors
146
- const safeAlias = aliasStr.replace(/\./g, '_');
147
- ctx.append(' ' + this.escapeId(safeAlias, true));
148
- }
149
- }
150
- });
151
- }
152
-
153
- select<E>(ctx: QueryContext, entity: Type<E>, select: QuerySelect<E>, opts: QueryOptions = {}): void {
154
- const meta = getMeta(entity);
155
- const tableName = this.resolveTableName(entity, meta);
156
- const prefix = (opts.prefix ?? (opts.autoPrefix || isSelectingRelations(meta, select))) ? tableName : undefined;
157
-
158
- ctx.append('SELECT ');
159
- this.selectFields(ctx, entity, select, { prefix });
160
- // Add related fields BEFORE FROM clause
161
- this.selectRelationFields(ctx, entity, select, { prefix });
162
- ctx.append(` FROM ${this.escapeId(tableName)}`);
163
- // Add JOINs AFTER FROM clause
164
- this.selectRelationJoins(ctx, entity, select, { prefix });
165
- }
166
-
167
- protected selectRelationFields<E>(
168
- ctx: QueryContext,
169
- entity: Type<E>,
170
- select: QuerySelect<E>,
171
- opts: { prefix?: string } = {},
172
- ): void {
173
- if (Array.isArray(select)) {
174
- return;
175
- }
176
-
177
- const meta = getMeta(entity);
178
- const tableName = this.resolveTableName(entity, meta);
179
- const relKeys = filterRelationKeys(meta, select);
180
- const isSelectArray = Array.isArray(select);
181
- const prefix = opts.prefix;
182
-
183
- for (const relKey of relKeys) {
184
- const relOpts = meta.relations[relKey];
185
-
186
- if (relOpts.cardinality === '1m' || relOpts.cardinality === 'mm') {
187
- continue;
188
- }
189
-
190
- const isFirstLevel = prefix === tableName;
191
- const joinRelAlias = isFirstLevel ? relKey : prefix ? prefix + '.' + relKey : relKey;
192
- const relEntity = relOpts.entity();
193
- const relSelect = select[relKey as string];
194
- const relQuery = isSelectArray ? {} : Array.isArray(relSelect) ? { $select: relSelect } : relSelect;
195
-
196
- ctx.append(', ');
197
- this.selectFields(ctx, relEntity, relQuery.$select, {
198
- prefix: joinRelAlias,
199
- autoPrefixAlias: true,
200
- });
201
-
202
- // Recursively add nested relation fields
203
- this.selectRelationFields(ctx, relEntity, relQuery.$select, {
204
- prefix: joinRelAlias,
205
- });
206
- }
207
- }
208
-
209
- protected selectRelationJoins<E>(
210
- ctx: QueryContext,
211
- entity: Type<E>,
212
- select: QuerySelect<E>,
213
- opts: { prefix?: string } = {},
214
- ): void {
215
- if (Array.isArray(select)) {
216
- return;
217
- }
218
-
219
- const meta = getMeta(entity);
220
- const tableName = this.resolveTableName(entity, meta);
221
- const relKeys = filterRelationKeys(meta, select);
222
- const isSelectArray = Array.isArray(select);
223
- const prefix = opts.prefix;
224
-
225
- for (const relKey of relKeys) {
226
- const relOpts = meta.relations[relKey];
227
-
228
- if (relOpts.cardinality === '1m' || relOpts.cardinality === 'mm') {
229
- continue;
230
- }
231
-
232
- const isFirstLevel = prefix === tableName;
233
- const joinRelAlias = isFirstLevel ? (relKey as string) : prefix ? prefix + '.' + relKey : (relKey as string);
234
- const relEntity = relOpts.entity();
235
- const relSelect = select[relKey as string];
236
- const relQuery = isSelectArray ? {} : Array.isArray(relSelect) ? { $select: relSelect } : relSelect;
237
-
238
- const relMeta = getMeta(relEntity);
239
- const relTableName = this.resolveTableName(relEntity, relMeta);
240
- const relEntityName = this.escapeId(relTableName);
241
- const relPath = prefix ? this.escapeId(prefix, true) : this.escapeId(tableName);
242
- const joinType = relQuery.$required ? 'INNER' : 'LEFT';
243
- const joinAlias = this.escapeId(joinRelAlias, true);
244
-
245
- ctx.append(` ${joinType} JOIN ${relEntityName} ${joinAlias} ON `);
246
- ctx.append(
247
- relOpts.references
248
- .map((it) => {
249
- const foreignColumnName = this.resolveColumnName(it.foreign, relMeta.fields[it.foreign]);
250
- const localColumnName = this.resolveColumnName(it.local, meta.fields[it.local]);
251
- return `${joinAlias}.${this.escapeId(foreignColumnName)} = ${relPath}.${this.escapeId(localColumnName)}`;
252
- })
253
- .join(' AND '),
254
- );
255
-
256
- if (relQuery.$where) {
257
- ctx.append(' AND ');
258
- this.where(ctx, relEntity, relQuery.$where, { prefix: joinRelAlias, clause: false });
259
- }
260
-
261
- // Recursively add nested relation JOINs
262
- this.selectRelationJoins(ctx, relEntity, relQuery.$select, {
263
- prefix: joinRelAlias,
264
- });
265
- }
266
- }
267
-
268
- where<E>(ctx: QueryContext, entity: Type<E>, where: QueryWhere<E> = {}, opts: QueryWhereOptions = {}): void {
269
- const meta = getMeta(entity);
270
- const { usePrecedence, clause = 'WHERE', softDelete } = opts;
271
-
272
- where = buldQueryWhereAsMap(meta, where);
273
-
274
- if (meta.softDelete && (softDelete || softDelete === undefined) && !where[meta.softDelete as string]) {
275
- where[meta.softDelete as string] = null;
276
- }
277
-
278
- const entries = Object.entries(where);
279
-
280
- if (!entries.length) {
281
- return;
282
- }
283
-
284
- if (clause) {
285
- ctx.append(` ${clause} `);
286
- }
287
-
288
- if (usePrecedence) {
289
- ctx.append('(');
290
- }
291
-
292
- entries.forEach(([key, val], index) => {
293
- if (index > 0) {
294
- ctx.append(' AND ');
295
- }
296
- this.compare(ctx, entity, key as keyof QueryWhereMap<E>, val as any, {
297
- ...opts,
298
- usePrecedence: entries.length > 1,
299
- });
300
- });
301
-
302
- if (usePrecedence) {
303
- ctx.append(')');
304
- }
305
- }
306
-
307
- compare<E, K extends keyof QueryWhereMap<E>>(
308
- ctx: QueryContext,
309
- entity: Type<E>,
310
- key: K,
311
- val: QueryWhereMap<E>[K],
312
- opts: QueryComparisonOptions = {},
313
- ): void {
314
- const meta = getMeta(entity);
315
-
316
- if (val instanceof QueryRaw) {
317
- if (key === '$exists' || key === '$nexists') {
318
- ctx.append(key === '$exists' ? 'EXISTS (' : 'NOT EXISTS (');
319
- const tableName = this.resolveTableName(entity, meta);
320
- this.getRawValue(ctx, {
321
- value: val,
322
- prefix: tableName,
323
- escapedPrefix: this.escapeId(tableName, false, true),
324
- });
325
- ctx.append(')');
326
- return;
327
- }
328
- this.getComparisonKey(ctx, entity, key as FieldKey<E>, opts);
329
- ctx.append(' = ');
330
- this.getRawValue(ctx, { value: val });
331
- return;
332
- }
333
-
334
- if (key === '$text') {
335
- const search = val as QueryTextSearchOptions<E>;
336
- const fields = search.$fields.map((fKey) => {
337
- const field = meta.fields[fKey];
338
- const columnName = this.resolveColumnName(fKey, field);
339
- return this.escapeId(columnName);
340
- });
341
- ctx.append(`MATCH(${fields.join(', ')}) AGAINST(`);
342
- ctx.addValue(search.$value);
343
- ctx.append(')');
344
- return;
345
- }
346
-
347
- if (key === '$and' || key === '$or' || key === '$not' || key === '$nor') {
348
- const negateOperatorMap = {
349
- $not: '$and',
350
- $nor: '$or',
351
- } as const;
352
-
353
- const op: '$and' | '$or' = negateOperatorMap[key as string] ?? key;
354
- const negate = key in negateOperatorMap ? 'NOT' : '';
355
-
356
- const valArr = val as QueryWhereArray<E>;
357
- const hasManyItems = valArr.length > 1;
358
-
359
- if ((opts.usePrecedence || negate) && hasManyItems) {
360
- ctx.append((negate ? negate + ' ' : '') + '(');
361
- } else if (negate) {
362
- ctx.append(negate + ' ');
363
- }
364
-
365
- valArr.forEach((whereEntry, index) => {
366
- if (index > 0) {
367
- ctx.append(op === '$or' ? ' OR ' : ' AND ');
368
- }
369
- if (whereEntry instanceof QueryRaw) {
370
- this.getRawValue(ctx, {
371
- value: whereEntry,
372
- prefix: opts.prefix,
373
- escapedPrefix: this.escapeId(opts.prefix, true, true),
374
- });
375
- } else {
376
- this.where(ctx, entity, whereEntry, {
377
- prefix: opts.prefix,
378
- usePrecedence: hasManyItems && !Array.isArray(whereEntry) && getKeys(whereEntry).length > 1,
379
- clause: false,
380
- });
381
- }
382
- });
383
-
384
- if ((opts.usePrecedence || negate) && hasManyItems) {
385
- ctx.append(')');
386
- }
387
- return;
388
- }
389
-
390
- const value = Array.isArray(val) ? { $in: val } : typeof val === 'object' && val !== null ? val : { $eq: val };
391
- const operators = getKeys(value) as (keyof QueryWhereFieldOperatorMap<E>)[];
392
-
393
- if (operators.length > 1) {
394
- ctx.append('(');
395
- }
396
-
397
- operators.forEach((op, index) => {
398
- if (index > 0) {
399
- ctx.append(' AND ');
400
- }
401
- this.compareFieldOperator(ctx, entity, key as FieldKey<E>, op, value[op], opts);
402
- });
403
-
404
- if (operators.length > 1) {
405
- ctx.append(')');
406
- }
407
- }
408
-
409
- compareFieldOperator<E, K extends keyof QueryWhereFieldOperatorMap<E>>(
410
- ctx: QueryContext,
411
- entity: Type<E>,
412
- key: FieldKey<E>,
413
- op: K,
414
- val: QueryWhereFieldOperatorMap<E>[K],
415
- opts: QueryOptions = {},
416
- ): void {
417
- switch (op) {
418
- case '$eq':
419
- this.getComparisonKey(ctx, entity, key, opts);
420
- if (val === null) {
421
- ctx.append(' IS NULL');
422
- } else {
423
- ctx.append(' = ');
424
- ctx.addValue(val);
425
- }
426
- break;
427
- case '$ne':
428
- this.getComparisonKey(ctx, entity, key, opts);
429
- if (val === null) {
430
- ctx.append(' IS NOT NULL');
431
- } else {
432
- ctx.append(' <> ');
433
- ctx.addValue(val);
434
- }
435
- break;
436
- case '$not':
437
- ctx.append('NOT (');
438
- this.compare(ctx, entity, key as any, val as any, opts);
439
- ctx.append(')');
440
- break;
441
- case '$gt':
442
- this.getComparisonKey(ctx, entity, key, opts);
443
- ctx.append(' > ');
444
- ctx.addValue(val);
445
- break;
446
- case '$gte':
447
- this.getComparisonKey(ctx, entity, key, opts);
448
- ctx.append(' >= ');
449
- ctx.addValue(val);
450
- break;
451
- case '$lt':
452
- this.getComparisonKey(ctx, entity, key, opts);
453
- ctx.append(' < ');
454
- ctx.addValue(val);
455
- break;
456
- case '$lte':
457
- this.getComparisonKey(ctx, entity, key, opts);
458
- ctx.append(' <= ');
459
- ctx.addValue(val);
460
- break;
461
- case '$startsWith':
462
- this.getComparisonKey(ctx, entity, key, opts);
463
- ctx.append(' LIKE ');
464
- ctx.addValue(`${val}%`);
465
- break;
466
- case '$istartsWith':
467
- this.getComparisonKey(ctx, entity, key, opts);
468
- ctx.append(' LIKE ');
469
- ctx.addValue(`${(val as string).toLowerCase()}%`);
470
- break;
471
- case '$endsWith':
472
- this.getComparisonKey(ctx, entity, key, opts);
473
- ctx.append(' LIKE ');
474
- ctx.addValue(`%${val}`);
475
- break;
476
- case '$iendsWith':
477
- this.getComparisonKey(ctx, entity, key, opts);
478
- ctx.append(' LIKE ');
479
- ctx.addValue(`%${(val as string).toLowerCase()}`);
480
- break;
481
- case '$includes':
482
- this.getComparisonKey(ctx, entity, key, opts);
483
- ctx.append(' LIKE ');
484
- ctx.addValue(`%${val}%`);
485
- break;
486
- case '$iincludes':
487
- this.getComparisonKey(ctx, entity, key, opts);
488
- ctx.append(' LIKE ');
489
- ctx.addValue(`%${(val as string).toLowerCase()}%`);
490
- break;
491
- case '$ilike':
492
- this.getComparisonKey(ctx, entity, key, opts);
493
- ctx.append(' LIKE ');
494
- ctx.addValue((val as string).toLowerCase());
495
- break;
496
- case '$like':
497
- this.getComparisonKey(ctx, entity, key, opts);
498
- ctx.append(' LIKE ');
499
- ctx.addValue(val);
500
- break;
501
- case '$in':
502
- this.getComparisonKey(ctx, entity, key, opts);
503
- if (Array.isArray(val) && val.length > 0) {
504
- ctx.append(' IN (');
505
- this.addValues(ctx, val as any[]);
506
- ctx.append(')');
507
- } else {
508
- ctx.append(' IN (NULL)');
509
- }
510
- break;
511
- case '$nin':
512
- this.getComparisonKey(ctx, entity, key, opts);
513
- if (Array.isArray(val) && val.length > 0) {
514
- ctx.append(' NOT IN (');
515
- this.addValues(ctx, val as any[]);
516
- ctx.append(')');
517
- } else {
518
- ctx.append(' NOT IN (NULL)');
519
- }
520
- break;
521
- case '$regex':
522
- this.getComparisonKey(ctx, entity, key, opts);
523
- ctx.append(' REGEXP ');
524
- ctx.addValue(val);
525
- break;
526
- default:
527
- throw TypeError(`unknown operator: ${op}`);
528
- }
529
- }
530
-
531
- protected addValues(ctx: QueryContext, vals: unknown[]): void {
532
- vals.forEach((val, index) => {
533
- if (index > 0) {
534
- ctx.append(', ');
535
- }
536
- ctx.addValue(val);
537
- });
538
- }
539
-
540
- getComparisonKey<E>(ctx: QueryContext, entity: Type<E>, key: FieldKey<E>, { prefix }: QueryOptions = {}): void {
541
- const meta = getMeta(entity);
542
- const escapedPrefix = this.escapeId(prefix, true, true);
543
- const field = meta.fields[key];
544
-
545
- if (field?.virtual) {
546
- this.getRawValue(ctx, {
547
- value: field.virtual,
548
- prefix,
549
- escapedPrefix,
550
- });
551
- return;
552
- }
553
-
554
- const columnName = this.resolveColumnName(key, field);
555
- ctx.append(escapedPrefix + this.escapeId(columnName));
556
- }
557
-
558
- sort<E>(ctx: QueryContext, entity: Type<E>, sort: QuerySort<E>, { prefix }: QueryOptions): void {
559
- const sortMap = buildSortMap(sort);
560
- if (!hasKeys(sortMap)) {
561
- return;
562
- }
563
- const meta = getMeta(entity);
564
- const flattenedSort = flatObject(sortMap, prefix);
565
- const directionMap = { 1: '', asc: '', '-1': ' DESC', desc: ' DESC' } as const;
566
-
567
- ctx.append(' ORDER BY ');
568
-
569
- Object.entries(flattenedSort).forEach(([key, sort], index) => {
570
- if (index > 0) {
571
- ctx.append(', ');
572
- }
573
- const field = meta.fields[key];
574
- const name = this.resolveColumnName(key, field);
575
- const direction = directionMap[sort as QuerySortDirection];
576
- ctx.append(this.escapeId(name) + direction);
577
- });
578
- }
579
-
580
- pager(ctx: QueryContext, opts: QueryPager): void {
581
- if (opts.$limit) {
582
- ctx.append(` LIMIT ${Number(opts.$limit)}`);
583
- }
584
- if (opts.$skip !== undefined) {
585
- ctx.append(` OFFSET ${Number(opts.$skip)}`);
586
- }
587
- }
588
-
589
- count<E>(ctx: QueryContext, entity: Type<E>, q: QuerySearch<E>, opts?: QueryOptions): void {
590
- const search: Query<E> = { ...q };
591
- delete search.$sort;
592
- this.select<E>(ctx, entity, [raw('COUNT(*)', 'count')], undefined);
593
- this.search(ctx, entity, search, opts);
594
- }
595
-
596
- find<E>(ctx: QueryContext, entity: Type<E>, q: Query<E> = {}, opts?: QueryOptions): void {
597
- this.select(ctx, entity, q.$select, opts);
598
- this.search(ctx, entity, q, opts);
599
- }
600
-
601
- insert<E>(ctx: QueryContext, entity: Type<E>, payload: E | E[], opts?: QueryOptions): void {
602
- const meta = getMeta(entity);
603
- const payloads = fillOnFields(meta, payload, 'onInsert');
604
- const keys = filterFieldKeys(meta, payloads[0], 'onInsert');
605
-
606
- const columns = keys.map((key) => {
607
- const field = meta.fields[key];
608
- return this.escapeId(this.resolveColumnName(key, field));
609
- });
610
- const tableName = this.resolveTableName(entity, meta);
611
- ctx.append(`INSERT INTO ${this.escapeId(tableName)} (${columns.join(', ')}) VALUES (`);
612
-
613
- payloads.forEach((it, recordIndex) => {
614
- if (recordIndex > 0) {
615
- ctx.append('), (');
616
- }
617
- keys.forEach((key, keyIndex) => {
618
- if (keyIndex > 0) {
619
- ctx.append(', ');
620
- }
621
- const field = meta.fields[key];
622
- this.formatPersistableValue(ctx, field, it[key]);
623
- });
624
- });
625
- ctx.append(')');
626
- }
627
-
628
- update<E>(ctx: QueryContext, entity: Type<E>, q: QuerySearch<E>, payload: E, opts?: QueryOptions): void {
629
- const meta = getMeta(entity);
630
- const [filledPayload] = fillOnFields(meta, payload, 'onUpdate');
631
- const keys = filterFieldKeys(meta, filledPayload, 'onUpdate');
632
-
633
- const tableName = this.resolveTableName(entity, meta);
634
- ctx.append(`UPDATE ${this.escapeId(tableName)} SET `);
635
- keys.forEach((key, index) => {
636
- if (index > 0) {
637
- ctx.append(', ');
638
- }
639
- const field = meta.fields[key];
640
- const columnName = this.resolveColumnName(key, field);
641
- ctx.append(`${this.escapeId(columnName)} = `);
642
- this.formatPersistableValue(ctx, field, filledPayload[key]);
643
- });
644
-
645
- this.search(ctx, entity, q, opts);
646
- }
647
-
648
- upsert<E>(ctx: QueryContext, entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: E): void {
649
- const meta = getMeta(entity);
650
- const update = this.getUpsertUpdateAssignments(ctx, meta, conflictPaths, payload, (name) => `VALUES(${name})`);
651
-
652
- if (update) {
653
- this.insert(ctx, entity, payload);
654
- ctx.append(` ON DUPLICATE KEY UPDATE ${update}`);
655
- } else {
656
- const insertCtx = this.createContext();
657
- this.insert(insertCtx, entity, payload);
658
- ctx.append(insertCtx.sql.replace(/^INSERT/, 'INSERT IGNORE'));
659
- insertCtx.values.forEach((val) => {
660
- ctx.pushValue(val);
661
- });
662
- }
663
- }
664
-
665
- protected getUpsertUpdateAssignments<E>(
666
- ctx: QueryContext,
667
- meta: EntityMeta<E>,
668
- conflictPaths: QueryConflictPaths<E>,
669
- payload: E,
670
- callback?: (columnName: string) => string,
671
- ): string {
672
- const [filledPayload] = fillOnFields(meta, payload, 'onUpdate');
673
- const fields = filterFieldKeys(meta, filledPayload, 'onUpdate');
674
- return fields
675
- .filter((col) => !conflictPaths[col])
676
- .map((col) => {
677
- const field = meta.fields[col];
678
- const columnName = this.resolveColumnName(col, field);
679
- if (callback) {
680
- return `${this.escapeId(columnName)} = ${callback(this.escapeId(columnName))}`;
681
- }
682
- const valCtx = this.createContext();
683
- this.formatPersistableValue(valCtx, field, filledPayload[col]);
684
- valCtx.values.forEach((val) => {
685
- ctx.pushValue(val);
686
- });
687
- return `${this.escapeId(columnName)} = ${valCtx.sql}`;
688
- })
689
- .join(', ');
690
- }
691
-
692
- protected getUpsertConflictPathsStr<E>(meta: EntityMeta<E>, conflictPaths: QueryConflictPaths<E>): string {
693
- return getKeys(conflictPaths)
694
- .map((key) => {
695
- const field = meta.fields[key];
696
- const columnName = this.resolveColumnName(key, field);
697
- return this.escapeId(columnName);
698
- })
699
- .join(', ');
700
- }
701
-
702
- delete<E>(ctx: QueryContext, entity: Type<E>, q: QuerySearch<E>, opts: QueryOptions = {}): void {
703
- const meta = getMeta(entity);
704
- const tableName = this.resolveTableName(entity, meta);
705
-
706
- if (opts.softDelete || opts.softDelete === undefined) {
707
- if (meta.softDelete) {
708
- const field = meta.fields[meta.softDelete];
709
- const value = getFieldCallbackValue(field.onDelete);
710
- const columnName = this.resolveColumnName(meta.softDelete, field);
711
- ctx.append(`UPDATE ${this.escapeId(tableName)} SET ${this.escapeId(columnName)} = `);
712
- ctx.addValue(value);
713
- this.search(ctx, entity, q, opts);
714
- return;
715
- }
716
- if (opts.softDelete) {
717
- throw TypeError(`'${tableName}' has not enabled 'softDelete'`);
718
- }
719
- }
720
-
721
- ctx.append(`DELETE FROM ${this.escapeId(tableName)}`);
722
- this.search(ctx, entity, q, opts);
723
- }
724
-
725
- escapeId(val: string, forbidQualified?: boolean, addDot?: boolean): string {
726
- return escapeSqlId(val, this.escapeIdChar, forbidQualified, addDot);
727
- }
728
-
729
- protected getPersistables<E>(
730
- ctx: QueryContext,
731
- meta: EntityMeta<E>,
732
- payload: E | E[],
733
- callbackKey: CallbackKey,
734
- ): Record<string, unknown>[] {
735
- const payloads = fillOnFields(meta, payload, callbackKey);
736
- return payloads.map((it) => this.getPersistable(ctx, meta, it, callbackKey));
737
- }
738
-
739
- protected getPersistable<E>(
740
- ctx: QueryContext,
741
- meta: EntityMeta<E>,
742
- payload: E,
743
- callbackKey: CallbackKey,
744
- ): Record<string, unknown> {
745
- const filledPayload = fillOnFields(meta, payload, callbackKey)[0];
746
- const keys = filterFieldKeys(meta, filledPayload, callbackKey);
747
- return keys.reduce(
748
- (acc, key) => {
749
- const field = meta.fields[key];
750
- const valCtx = this.createContext();
751
- this.formatPersistableValue(valCtx, field, filledPayload[key]);
752
- valCtx.values.forEach((val) => {
753
- ctx.pushValue(val);
754
- });
755
- acc[key as string] = valCtx.sql;
756
- return acc;
757
- },
758
- {} as Record<string, unknown>,
759
- );
760
- }
761
-
762
- protected formatPersistableValue<E>(ctx: QueryContext, field: FieldOptions, value: unknown): void {
763
- if (value instanceof QueryRaw) {
764
- this.getRawValue(ctx, { value });
765
- return;
766
- }
767
- if (field?.type === 'json' || field?.type === 'jsonb') {
768
- ctx.addValue(value ? JSON.stringify(value) : null);
769
- return;
770
- }
771
- if (field?.type === 'vector' && Array.isArray(value)) {
772
- ctx.addValue(`[${value.join(',')}]`);
773
- return;
774
- }
775
- ctx.addValue(value);
776
- }
777
-
778
- getRawValue(ctx: QueryContext, opts: QueryRawFnOptions & { value: QueryRaw; autoPrefixAlias?: boolean }) {
779
- const { value, prefix = '', escapedPrefix, autoPrefixAlias } = opts;
780
- if (typeof value.value === 'function') {
781
- const res = value.value({
782
- ...opts,
783
- ctx,
784
- dialect: this,
785
- prefix,
786
- escapedPrefix: escapedPrefix ?? this.escapeId(prefix, true, true),
787
- });
788
- if (typeof res === 'string' || (typeof res === 'number' && !Number.isNaN(res))) {
789
- ctx.append(String(res));
790
- }
791
- } else {
792
- ctx.append(prefix + String(value.value));
793
- }
794
- const alias = value.alias;
795
- if (alias) {
796
- const fullAlias = autoPrefixAlias ? prefix + alias : alias;
797
- // Replace dots with underscores for alias to avoid syntax errors
798
- const safeAlias = fullAlias.replace(/\./g, '_');
799
- const escapedFullAlias = this.escapeId(safeAlias, true);
800
- ctx.append(' ' + escapedFullAlias);
801
- }
802
- }
803
-
804
- abstract escape(value: unknown): string;
805
- }