mythix-orm-sql-base 1.8.0 → 1.9.0
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.
|
@@ -2,7 +2,7 @@ import { ConnectionBase, Field, ModelClass, QueryEngine, QueryResults, Model } f
|
|
|
2
2
|
import { GenericObject } from 'mythix-orm/lib/interfaces/common';
|
|
3
3
|
|
|
4
4
|
export declare interface ModelDataFromQueryResults {
|
|
5
|
-
[
|
|
5
|
+
[key: string]: Array<GenericObject>;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
declare class SQLConnectionBase extends ConnectionBase {
|
|
@@ -20,17 +20,19 @@ declare class SQLConnectionBase extends ConnectionBase {
|
|
|
20
20
|
|
|
21
21
|
public enableForeignKeyConstraints(enable: boolean): Promise<void>;
|
|
22
22
|
|
|
23
|
-
buildModelsFromModelDataMap(
|
|
23
|
+
public buildModelsFromModelDataMap(
|
|
24
24
|
queryEngine: QueryEngine,
|
|
25
25
|
modelDataMap: ModelDataFromQueryResults,
|
|
26
26
|
callback: (Model: ModelClass, model: Model) => Model,
|
|
27
27
|
): Array<Model>;
|
|
28
28
|
|
|
29
|
-
updateModelsFromResults(
|
|
29
|
+
public updateModelsFromResults(
|
|
30
30
|
Model: ModelClass,
|
|
31
31
|
storedModels: Array<Model>,
|
|
32
32
|
results: QueryResults
|
|
33
33
|
): Array<Model>;
|
|
34
|
+
|
|
35
|
+
public getUpdateOrDeleteChangeCount(queryResult: any): number;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export default SQLConnectionBase;
|
|
@@ -106,6 +106,9 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
106
106
|
let alreadyVisited = {};
|
|
107
107
|
|
|
108
108
|
let fieldInfo = fields.map((field) => {
|
|
109
|
+
if (Nife.instanceOf(field, 'string'))
|
|
110
|
+
return field;
|
|
111
|
+
|
|
109
112
|
let Model = field.Model;
|
|
110
113
|
let modelName = Model.getModelName();
|
|
111
114
|
let pkFieldName = Model.getPrimaryKeyFieldName();
|
|
@@ -119,6 +122,9 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
119
122
|
});
|
|
120
123
|
|
|
121
124
|
let modelInfo = fieldInfo.reduce((obj, info) => {
|
|
125
|
+
if (Nife.instanceOf(info, 'string'))
|
|
126
|
+
return obj;
|
|
127
|
+
|
|
122
128
|
obj[info.modelName] = info;
|
|
123
129
|
return obj;
|
|
124
130
|
}, {});
|
|
@@ -143,10 +149,24 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
143
149
|
|
|
144
150
|
// Collect row
|
|
145
151
|
for (let j = 0, jl = fieldInfo.length; j < jl; j++) {
|
|
146
|
-
let
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
152
|
+
let thisFieldInfo = fieldInfo[j];
|
|
153
|
+
let fieldName;
|
|
154
|
+
let modelName;
|
|
155
|
+
|
|
156
|
+
if (Nife.instanceOf(thisFieldInfo, 'string')) {
|
|
157
|
+
let def = Utils.parseQualifiedName(thisFieldInfo);
|
|
158
|
+
if (!def.modelName)
|
|
159
|
+
continue;
|
|
160
|
+
|
|
161
|
+
if (Nife.isEmpty(def.fieldNames))
|
|
162
|
+
continue;
|
|
163
|
+
|
|
164
|
+
modelName = def.modelName;
|
|
165
|
+
fieldName = def.fieldNames[0];
|
|
166
|
+
} else {
|
|
167
|
+
modelName = thisFieldInfo.modelName;
|
|
168
|
+
fieldName = thisFieldInfo.field.fieldName;
|
|
169
|
+
}
|
|
150
170
|
|
|
151
171
|
let dataContext = data[modelName];
|
|
152
172
|
let remoteValue = row[j];
|
|
@@ -166,7 +186,7 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
166
186
|
});
|
|
167
187
|
}
|
|
168
188
|
|
|
169
|
-
dataContext[
|
|
189
|
+
dataContext[fieldName] = remoteValue;
|
|
170
190
|
}
|
|
171
191
|
|
|
172
192
|
let rootModel;
|
|
@@ -308,6 +328,19 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
308
328
|
return storedModels;
|
|
309
329
|
}
|
|
310
330
|
|
|
331
|
+
getUpdateOrDeleteChangeCount(queryResult) {
|
|
332
|
+
if (!queryResult)
|
|
333
|
+
return 0;
|
|
334
|
+
|
|
335
|
+
if ('rows' in queryResult && Array.isArray(queryResult.rows))
|
|
336
|
+
return queryResult.rows.length;
|
|
337
|
+
|
|
338
|
+
if ('changes' in queryResult)
|
|
339
|
+
return queryResult.changes;
|
|
340
|
+
|
|
341
|
+
return 0;
|
|
342
|
+
}
|
|
343
|
+
|
|
311
344
|
// --------------------------------------------- //
|
|
312
345
|
|
|
313
346
|
async dropTable(Model, options) {
|
|
@@ -382,7 +415,7 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
382
415
|
if (Nife.isEmpty(primaryKeyFieldName))
|
|
383
416
|
throw new Error(`${this.constructor.name}::update: Model has no primary key field.`);
|
|
384
417
|
|
|
385
|
-
|
|
418
|
+
let result = await this.bulkModelOperation(
|
|
386
419
|
Model,
|
|
387
420
|
models,
|
|
388
421
|
Object.assign({}, options || {}, { isUpdateOperation: true }),
|
|
@@ -417,6 +450,8 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
417
450
|
await this.runSaveHooks(Model, models, 'onAfterUpdate', 'onAfterSave', options);
|
|
418
451
|
},
|
|
419
452
|
);
|
|
453
|
+
|
|
454
|
+
return (Array.isArray(result)) ? result.length : 1;
|
|
420
455
|
}
|
|
421
456
|
|
|
422
457
|
async updateAll(_queryEngine, model, _options) {
|
|
@@ -433,10 +468,7 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
433
468
|
|
|
434
469
|
let queryGenerator = this.getQueryGenerator();
|
|
435
470
|
let sqlStr = queryGenerator.generateUpdateStatement(rootModel, model, queryEngine, options);
|
|
436
|
-
|
|
437
|
-
// TODO: Use "RETURNING" to return pks of of updated rows
|
|
438
|
-
|
|
439
|
-
return await this.query(sqlStr, options);
|
|
471
|
+
return this.getUpdateOrDeleteChangeCount(await this.query(sqlStr, options));
|
|
440
472
|
}
|
|
441
473
|
|
|
442
474
|
async destroyModels(Model, _models, _options) {
|
|
@@ -446,7 +478,7 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
446
478
|
let options = _options || {};
|
|
447
479
|
if (_models == null) {
|
|
448
480
|
if (options.truncate !== true)
|
|
449
|
-
return;
|
|
481
|
+
return 0;
|
|
450
482
|
|
|
451
483
|
let query = await this.finalizeQuery('delete', Model.where(this).unscoped(), options);
|
|
452
484
|
let queryGenerator = this.getQueryGenerator();
|
|
@@ -457,13 +489,13 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
457
489
|
|
|
458
490
|
let models = Nife.toArray(_models).filter(Boolean);
|
|
459
491
|
if (Nife.isEmpty(models))
|
|
460
|
-
return;
|
|
492
|
+
return 0;
|
|
461
493
|
|
|
462
494
|
let primaryKeyFieldName = Model.getPrimaryKeyFieldName();
|
|
463
495
|
if (Nife.isEmpty(primaryKeyFieldName))
|
|
464
496
|
throw new Error(`${this.constructor.name}::destroyModels: Model has no primary key field. You must supply a query to delete models with no primary key.`);
|
|
465
497
|
|
|
466
|
-
|
|
498
|
+
let result = await this.bulkModelOperation(
|
|
467
499
|
Model,
|
|
468
500
|
models,
|
|
469
501
|
Object.assign({}, options, { isDeleteOperation: true }),
|
|
@@ -494,6 +526,8 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
494
526
|
await this.query(sqlStr, options);
|
|
495
527
|
},
|
|
496
528
|
);
|
|
529
|
+
|
|
530
|
+
return (Array.isArray(result)) ? result.length : 1;
|
|
497
531
|
}
|
|
498
532
|
|
|
499
533
|
async destroy(_queryEngineOrModel, modelsOrOptions, _options) {
|
|
@@ -521,7 +555,31 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
521
555
|
|
|
522
556
|
let queryGenerator = this.getQueryGenerator();
|
|
523
557
|
let sqlStr = queryGenerator.generateDeleteStatement(rootModel, queryEngine, options);
|
|
524
|
-
return await this.query(sqlStr, options);
|
|
558
|
+
return this.getUpdateOrDeleteChangeCount(await this.query(sqlStr, options));
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
queryResultRowsToRawData(result) {
|
|
562
|
+
if (!result)
|
|
563
|
+
return [];
|
|
564
|
+
|
|
565
|
+
let { columns, rows } = result;
|
|
566
|
+
if (Nife.isEmpty(columns) || Nife.isEmpty(rows))
|
|
567
|
+
return [];
|
|
568
|
+
|
|
569
|
+
let finalData = [];
|
|
570
|
+
for (let i = 0, il = rows.length; i < il; i++) {
|
|
571
|
+
let row = rows[i];
|
|
572
|
+
let data = {};
|
|
573
|
+
|
|
574
|
+
for (let j = 0, jl = columns.length; j < jl; j++) {
|
|
575
|
+
let column = columns[j];
|
|
576
|
+
data[column] = row[j];
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
finalData.push(data);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
return finalData;
|
|
525
583
|
}
|
|
526
584
|
|
|
527
585
|
async *select(_queryEngine, _options) {
|
|
@@ -536,13 +594,26 @@ class SQLConnectionBase extends ConnectionBase {
|
|
|
536
594
|
throw new TypeError(`${this.constructor.name}::select: First argument must be a model class or a query.`);
|
|
537
595
|
}
|
|
538
596
|
|
|
539
|
-
let options
|
|
597
|
+
let options = _options || {};
|
|
598
|
+
let queryGenerator = this.getQueryGenerator();
|
|
599
|
+
|
|
540
600
|
queryEngine = await this.finalizeQuery('read', queryEngine, options);
|
|
541
601
|
|
|
542
|
-
let queryContext
|
|
543
|
-
let
|
|
544
|
-
|
|
545
|
-
|
|
602
|
+
let queryContext = queryEngine.getOperationContext();
|
|
603
|
+
let groupBy = queryContext.groupBy;
|
|
604
|
+
if (groupBy && groupBy.size > 0) {
|
|
605
|
+
let sqlStatement = queryGenerator.generateSelectStatement(queryEngine, options);
|
|
606
|
+
let result = await this.query(sqlStatement, options);
|
|
607
|
+
let rows = this.queryResultRowsToRawData(result);
|
|
608
|
+
|
|
609
|
+
for (let i = 0, il = rows.length; i < il; i++)
|
|
610
|
+
yield rows[i];
|
|
611
|
+
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
let batchSize = options.batchSize || 500;
|
|
616
|
+
let startIndex = queryContext.offset || 0;
|
|
546
617
|
|
|
547
618
|
while (true) {
|
|
548
619
|
let query = queryEngine.clone().LIMIT(batchSize).OFFSET(startIndex);
|
|
@@ -70,10 +70,8 @@ declare class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
70
70
|
public sortJoinRelationOrder(joins: Map<string, Array<JoinTableInfo>>): Array<string>;
|
|
71
71
|
public generateSelectQueryJoinTables(queryEngine: QueryEngine, options?: GenericObject): string;
|
|
72
72
|
public generateSelectWhereConditions(queryEngine: QueryEngine, options?: GenericObject): string;
|
|
73
|
-
public generateOrderClause(
|
|
74
|
-
|
|
75
|
-
options?: GenericObject
|
|
76
|
-
): string;
|
|
73
|
+
public generateOrderClause(queryEngine: QueryEngine, options?: GenericObject): string;
|
|
74
|
+
public generateGroupByClause(queryEngine: QueryEngine, options?: GenericObject): string;
|
|
77
75
|
|
|
78
76
|
public generateLimitClause(limit: LiteralBase | number | string, options?: GenericObject): string;
|
|
79
77
|
public generateOffsetClause(offset: LiteralBase | number | string, options?: GenericObject): string;
|
|
@@ -158,6 +156,7 @@ declare class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
158
156
|
options?: GenericObject,
|
|
159
157
|
): string;
|
|
160
158
|
|
|
159
|
+
public generateDeleteStatementReturningClause(Model: ModelClass, queryEngine: QueryEngine, pkField: Field | null, escapedColumnName: string | null, options: GenericObject): string;
|
|
161
160
|
public generateDeleteStatement(Model: ModelClass, queryEngine: QueryEngine, options?: GenericObject): string;
|
|
162
161
|
public generateTruncateTableStatement(Model: ModelClass, options?: GenericObject): string;
|
|
163
162
|
public generateAlterTableStatement(Model: ModelClass, newModelAttributes, options?: GenericObject): string;
|
|
@@ -90,27 +90,25 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
90
90
|
projectionFieldMap.set(fieldPart, fieldPart);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
let sortedFieldNames = this.sortedProjectedFields(projectedFieldNames);
|
|
95
|
-
let sortedProjectionFieldMap = new Map();
|
|
96
|
-
|
|
97
|
-
for (let i = 0, il = sortedFieldNames.length; i < il; i++) {
|
|
98
|
-
let sortedFieldName = sortedFieldNames[i];
|
|
99
|
-
let value = projectionFieldMap.get(sortedFieldName);
|
|
100
|
-
|
|
101
|
-
sortedProjectionFieldMap.set(sortedFieldName, value);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return sortedProjectionFieldMap;
|
|
93
|
+
return projectionFieldMap;
|
|
105
94
|
}
|
|
106
95
|
|
|
107
96
|
generateSelectQueryFieldProjection(queryEngine, options, asMap) {
|
|
108
97
|
let projectedFields = this.getProjectedFields(queryEngine, options, asMap);
|
|
109
98
|
|
|
110
|
-
if (asMap === true)
|
|
99
|
+
if (asMap === true) {
|
|
111
100
|
return projectedFields;
|
|
112
|
-
else
|
|
113
|
-
|
|
101
|
+
} else {
|
|
102
|
+
let projectedFieldList = Array.from(projectedFields.values()).join(',');
|
|
103
|
+
|
|
104
|
+
let distinct = queryEngine.getOperationContext().distinct;
|
|
105
|
+
if (distinct) {
|
|
106
|
+
let result = distinct.toString(this.connection, { isProjection: true });
|
|
107
|
+
return `${result} ${projectedFieldList}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return projectedFieldList;
|
|
111
|
+
}
|
|
114
112
|
}
|
|
115
113
|
|
|
116
114
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -120,23 +118,25 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
120
118
|
|
|
121
119
|
switch (operator) {
|
|
122
120
|
case 'EQ':
|
|
123
|
-
if (
|
|
124
|
-
|
|
125
|
-
return 'IS';
|
|
126
|
-
else if (Array.isArray(value))
|
|
127
|
-
return 'IN';
|
|
128
|
-
}
|
|
121
|
+
if (valueIsReference)
|
|
122
|
+
return '=';
|
|
129
123
|
|
|
130
|
-
|
|
124
|
+
if (value === null || value === true || value === false)
|
|
125
|
+
return 'IS';
|
|
126
|
+
else if (Array.isArray(value))
|
|
127
|
+
return 'IN';
|
|
128
|
+
else
|
|
129
|
+
return '=';
|
|
131
130
|
case 'NEQ':
|
|
132
|
-
if (
|
|
133
|
-
|
|
134
|
-
return 'IS NOT';
|
|
135
|
-
else if (Array.isArray(value))
|
|
136
|
-
return 'NOT IN';
|
|
137
|
-
}
|
|
131
|
+
if (valueIsReference)
|
|
132
|
+
return '!=';
|
|
138
133
|
|
|
139
|
-
|
|
134
|
+
if (value === null || value === true || value === false)
|
|
135
|
+
return 'IS NOT';
|
|
136
|
+
else if (Array.isArray(value))
|
|
137
|
+
return 'NOT IN';
|
|
138
|
+
else
|
|
139
|
+
return '!=';
|
|
140
140
|
case 'GT':
|
|
141
141
|
return '>';
|
|
142
142
|
case 'GTE':
|
|
@@ -181,6 +181,9 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
181
181
|
let isNot = queryPart.not;
|
|
182
182
|
let operator = (isNot) ? queryPart.inverseOperator : queryPart.operator;
|
|
183
183
|
|
|
184
|
+
if (operator === 'EXISTS' || operator === 'NOT EXISTS')
|
|
185
|
+
return `${operator}(${this.generateSelectStatement(value.clone().PROJECT(new Literals.Literal('1')).LIMIT(1).OFFSET(0), this.stackAssign(options, { isSubQuery: true, subQueryOperator: operator }))})`;
|
|
186
|
+
|
|
184
187
|
// If the value is an array, then handle the
|
|
185
188
|
// special "IN" case for an array
|
|
186
189
|
if (Array.isArray(value)) {
|
|
@@ -230,6 +233,9 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
230
233
|
if (!value.queryHasConditions())
|
|
231
234
|
return '';
|
|
232
235
|
|
|
236
|
+
if (Object.prototype.hasOwnProperty.call(queryPart, 'subType') && (queryPart.subType === 'ANY' || queryPart.subType === 'ALL'))
|
|
237
|
+
return `${escapedColumnName} ${sqlOperator} ${queryPart.subType}(${this.generateSelectStatement(value, this.stackAssign(options, { isSubQuery: true, subQueryOperator: queryPart.subType }))})`;
|
|
238
|
+
|
|
233
239
|
if (sqlOperator === '=')
|
|
234
240
|
sqlOperator = 'IN';
|
|
235
241
|
else if (sqlOperator === '!=')
|
|
@@ -452,13 +458,15 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
452
458
|
return sqlParts.join(' ');
|
|
453
459
|
}
|
|
454
460
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
return _orders.toString(this.connection);
|
|
461
|
+
generateOrderClause(queryEngine, _options) {
|
|
462
|
+
if (!queryEngine)
|
|
463
|
+
return '';
|
|
459
464
|
|
|
460
|
-
|
|
461
|
-
|
|
465
|
+
if (typeof queryEngine.getOperationContext !== 'function')
|
|
466
|
+
return '';
|
|
467
|
+
|
|
468
|
+
let order = this.getQueryEngineOrder(queryEngine, _options);
|
|
469
|
+
if (!order || !order.size)
|
|
462
470
|
return '';
|
|
463
471
|
|
|
464
472
|
let options = _options || {};
|
|
@@ -466,38 +474,44 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
466
474
|
if (contextOrderSupport === false)
|
|
467
475
|
return '';
|
|
468
476
|
|
|
469
|
-
let
|
|
470
|
-
|
|
471
|
-
let orderField = orders[i];
|
|
477
|
+
let allModelsUsedInQuery = queryEngine.getAllModelsUsedInQuery();
|
|
478
|
+
let orderByParts = [];
|
|
472
479
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
if (!options.projectionFields.has(fieldStr) && contextOrderSupport === 'PROJECTION_ONLY')
|
|
476
|
-
continue;
|
|
480
|
+
for (let [ fullyQualifiedFieldName, orderScope ] of order) {
|
|
481
|
+
let { value, direction } = orderScope;
|
|
477
482
|
|
|
478
|
-
|
|
479
|
-
|
|
483
|
+
// Only allow fields that are in our projection
|
|
484
|
+
if (options.projectionFields && options.onlyProjectedFields !== false) {
|
|
485
|
+
if (!options.projectionFields.has(fullyQualifiedFieldName) && contextOrderSupport === 'PROJECTION_ONLY')
|
|
486
|
+
continue;
|
|
480
487
|
}
|
|
481
488
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
489
|
+
let finalResult;
|
|
490
|
+
|
|
491
|
+
if (Nife.instanceOf(value, 'string')) {
|
|
492
|
+
// Raw string is treated as a literal
|
|
493
|
+
finalResult = value;
|
|
494
|
+
} else if (LiteralBase.isLiteral(value)) {
|
|
495
|
+
finalResult = value.toString(this.connection, options);
|
|
487
496
|
|
|
488
|
-
|
|
497
|
+
// fullyQualifiedFieldName is the stringified
|
|
498
|
+
// literal here.
|
|
499
|
+
if (options.projectionFields && !options.projectionFields.has(finalResult) && contextOrderSupport === 'PROJECTION_ONLY')
|
|
489
500
|
continue;
|
|
501
|
+
} else {
|
|
502
|
+
if (allModelsUsedInQuery.indexOf(value.Model) < 0)
|
|
503
|
+
continue;
|
|
504
|
+
|
|
505
|
+
finalResult = this.getEscapedColumnName(value.Model, value.columnName, options);
|
|
490
506
|
}
|
|
491
507
|
|
|
492
|
-
let escapedColumnName = this.getEscapedColumnName(orderField.Model, orderField.Field.columnName, options);
|
|
493
508
|
let orderStr;
|
|
494
|
-
|
|
495
509
|
if (options.reverseOrder !== true)
|
|
496
|
-
orderStr = (
|
|
510
|
+
orderStr = (direction === '-') ? 'DESC' : 'ASC';
|
|
497
511
|
else
|
|
498
|
-
orderStr = (
|
|
512
|
+
orderStr = (direction === '-') ? 'ASC' : 'DESC';
|
|
499
513
|
|
|
500
|
-
orderByParts.push(`${
|
|
514
|
+
orderByParts.push(`${finalResult} ${orderStr}`);
|
|
501
515
|
}
|
|
502
516
|
|
|
503
517
|
if (Nife.isEmpty(orderByParts))
|
|
@@ -506,6 +520,68 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
506
520
|
return `ORDER BY ${orderByParts.join(',')}`;
|
|
507
521
|
}
|
|
508
522
|
|
|
523
|
+
generateGroupByClause(queryEngine, _options) {
|
|
524
|
+
if (!queryEngine)
|
|
525
|
+
return '';
|
|
526
|
+
|
|
527
|
+
if (typeof queryEngine.getOperationContext !== 'function')
|
|
528
|
+
return '';
|
|
529
|
+
|
|
530
|
+
let groupBy = queryEngine.getOperationContext().groupBy;
|
|
531
|
+
if (!groupBy || !groupBy.size)
|
|
532
|
+
return '';
|
|
533
|
+
|
|
534
|
+
let options = _options || {};
|
|
535
|
+
|
|
536
|
+
let groupByParts = [];
|
|
537
|
+
for (let groupByScope of groupBy.values()) {
|
|
538
|
+
let { value } = groupByScope;
|
|
539
|
+
let finalResult;
|
|
540
|
+
|
|
541
|
+
if (Nife.instanceOf(value, 'string')) {
|
|
542
|
+
// Raw string is treated as a literal
|
|
543
|
+
finalResult = value;
|
|
544
|
+
} else if (LiteralBase.isLiteral(value)) {
|
|
545
|
+
finalResult = value.toString(this.connection, options);
|
|
546
|
+
} else {
|
|
547
|
+
finalResult = this.getEscapedColumnName(value.Model, value.columnName, options);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
groupByParts.push(finalResult);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (Nife.isEmpty(groupByParts))
|
|
554
|
+
return '';
|
|
555
|
+
|
|
556
|
+
return `GROUP BY ${groupByParts.join(',')}`;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
generateHavingClause(queryEngine, options) {
|
|
560
|
+
let where = this.generateSelectWhereConditions(queryEngine, options);
|
|
561
|
+
return (where) ? `HAVING (${where})` : '';
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
generateGroupByAndHavingClause(queryEngine, options) {
|
|
565
|
+
if (!queryEngine)
|
|
566
|
+
return '';
|
|
567
|
+
|
|
568
|
+
let sqlParts = [];
|
|
569
|
+
let groupByStatement = this.generateGroupByClause(queryEngine, options);
|
|
570
|
+
if (groupByStatement)
|
|
571
|
+
sqlParts.push(groupByStatement);
|
|
572
|
+
else
|
|
573
|
+
return '';
|
|
574
|
+
|
|
575
|
+
let having = queryEngine.getOperationContext().having;
|
|
576
|
+
if (having) {
|
|
577
|
+
let havingStatement = this.generateHavingClause(having, options);
|
|
578
|
+
if (havingStatement)
|
|
579
|
+
sqlParts.push(havingStatement);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
return sqlParts.join(' ');
|
|
583
|
+
}
|
|
584
|
+
|
|
509
585
|
// eslint-disable-next-line no-unused-vars
|
|
510
586
|
generateLimitClause(limit, options) {
|
|
511
587
|
if (LiteralBase.isLiteral(limit))
|
|
@@ -523,27 +599,31 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
523
599
|
}
|
|
524
600
|
|
|
525
601
|
generateSelectOrderLimitOffset(queryEngine, _options) {
|
|
602
|
+
if (!queryEngine)
|
|
603
|
+
return '';
|
|
604
|
+
|
|
526
605
|
let options = _options || {};
|
|
606
|
+
let context = queryEngine.getOperationContext();
|
|
527
607
|
let {
|
|
528
608
|
order,
|
|
529
609
|
limit,
|
|
530
610
|
offset,
|
|
531
|
-
} =
|
|
611
|
+
} = context;
|
|
612
|
+
|
|
532
613
|
let sqlParts = [];
|
|
533
614
|
let hasOrder = false;
|
|
615
|
+
let hasLimit = (Nife.instanceOf(limit, 'number') && isFinite(limit));
|
|
534
616
|
|
|
535
|
-
if (
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
617
|
+
if (options.orderClause !== false && !(options.orderClauseOnlyIfLimited === true && !hasLimit) && this.connection.isOrderSupportedInContext(options)) {
|
|
618
|
+
let result = this.generateOrderClause(queryEngine, options);
|
|
619
|
+
if (result) {
|
|
620
|
+
hasOrder = true;
|
|
621
|
+
sqlParts.push(result);
|
|
622
|
+
}
|
|
542
623
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
624
|
+
if (hasOrder && !hasLimit && options && options.forceLimit) {
|
|
625
|
+
limit = options.forceLimit;
|
|
626
|
+
offset = 0;
|
|
547
627
|
}
|
|
548
628
|
}
|
|
549
629
|
|
|
@@ -578,6 +658,9 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
578
658
|
if (orderLimitOffset)
|
|
579
659
|
sqlParts.push(orderLimitOffset);
|
|
580
660
|
|
|
661
|
+
if (options.separateWhereAndOrder)
|
|
662
|
+
return { where, orderLimitOffset };
|
|
663
|
+
|
|
581
664
|
return sqlParts.join(' ');
|
|
582
665
|
}
|
|
583
666
|
|
|
@@ -600,11 +683,18 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
600
683
|
options.selectStatement = true;
|
|
601
684
|
|
|
602
685
|
projectionFields = this.generateSelectQueryFieldProjection(queryEngine, options, true);
|
|
603
|
-
sqlParts.push(
|
|
686
|
+
sqlParts.push(this.generateSelectQueryFieldProjection(queryEngine, options));
|
|
604
687
|
|
|
605
688
|
sqlParts.push(this.generateFromTableOrTableJoin(rootModel, undefined, options));
|
|
606
689
|
sqlParts.push(this.generateSelectQueryJoinTables(queryEngine, options));
|
|
607
|
-
|
|
690
|
+
let { where, orderLimitOffset } = this.generateWhereAndOrderLimitOffset(queryEngine, this.stackAssign(options, { projectionFields, separateWhereAndOrder: true }));
|
|
691
|
+
if (where)
|
|
692
|
+
sqlParts.push(`WHERE ${where}`);
|
|
693
|
+
|
|
694
|
+
sqlParts.push(this.generateGroupByAndHavingClause(queryEngine, options));
|
|
695
|
+
|
|
696
|
+
if (orderLimitOffset)
|
|
697
|
+
sqlParts.push(orderLimitOffset);
|
|
608
698
|
|
|
609
699
|
let sql = sqlParts.filter(Boolean).join(' ');
|
|
610
700
|
|
|
@@ -1092,7 +1182,7 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
1092
1182
|
|
|
1093
1183
|
let where;
|
|
1094
1184
|
if (queryEngine) {
|
|
1095
|
-
where = this.generateWhereAndOrderLimitOffset(queryEngine, options);
|
|
1185
|
+
where = this.generateWhereAndOrderLimitOffset(queryEngine, this.stackAssign(options, { orderClauseOnlyIfLimited: true }));
|
|
1096
1186
|
if (where) {
|
|
1097
1187
|
if (options.newlines !== false)
|
|
1098
1188
|
sqlParts.push('\n');
|
|
@@ -1122,6 +1212,13 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
1122
1212
|
return sqlParts.join('');
|
|
1123
1213
|
}
|
|
1124
1214
|
|
|
1215
|
+
generateDeleteStatementReturningClause(Model, queryEngine, pkField, escapedColumnName, options) {
|
|
1216
|
+
if (!escapedColumnName)
|
|
1217
|
+
return '';
|
|
1218
|
+
|
|
1219
|
+
return `RETURNING ${escapedColumnName}`;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1125
1222
|
generateDeleteStatement(Model, _queryEngine, _options) {
|
|
1126
1223
|
let queryEngine = _queryEngine;
|
|
1127
1224
|
let options = _options;
|
|
@@ -1137,10 +1234,11 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
1137
1234
|
}
|
|
1138
1235
|
}
|
|
1139
1236
|
|
|
1140
|
-
let escapedTableName
|
|
1237
|
+
let escapedTableName = this.getEscapedTableName(Model, options);
|
|
1238
|
+
let pkField = Model.getPrimaryKeyField();
|
|
1239
|
+
|
|
1141
1240
|
if (queryEngine && queryEngine.queryHasConditions()) {
|
|
1142
1241
|
if (queryEngine.queryHasJoins()) {
|
|
1143
|
-
let pkField = Model.getPrimaryKeyField();
|
|
1144
1242
|
if (!pkField)
|
|
1145
1243
|
throw new Error(`${this.constructor.name}::generateDeleteStatement: Can not delete using table joins on a table with no primary key field.`);
|
|
1146
1244
|
|
|
@@ -1150,22 +1248,34 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
|
|
|
1150
1248
|
queryEngine
|
|
1151
1249
|
.AND[Model.getModelName(this.connection)][pkField.fieldName]
|
|
1152
1250
|
.EQ(new Literals.Literal(`${escapedTableNameAlias}.${escapedFieldAlias}`))
|
|
1153
|
-
.PROJECT(new Literals.Literal('1'))
|
|
1251
|
+
.PROJECT(new Literals.Literal('1'))
|
|
1252
|
+
.LIMIT(1)
|
|
1253
|
+
.OFFSET(0),
|
|
1154
1254
|
this.stackAssign(
|
|
1155
1255
|
options,
|
|
1156
1256
|
{
|
|
1157
|
-
isSubQuery:
|
|
1158
|
-
subQueryOperator:
|
|
1159
|
-
noProjectionAliases:
|
|
1160
|
-
forceLimit:
|
|
1257
|
+
isSubQuery: true,
|
|
1258
|
+
subQueryOperator: 'EXISTS',
|
|
1259
|
+
noProjectionAliases: true,
|
|
1260
|
+
forceLimit: 4294967295,
|
|
1161
1261
|
},
|
|
1162
1262
|
),
|
|
1163
1263
|
);
|
|
1164
1264
|
|
|
1165
|
-
|
|
1265
|
+
let returningField = `${escapedTableNameAlias}.${this.getEscapedColumnName(pkField.Model, pkField, this.stackAssign(options, { columnNameOnly: true }))}`;
|
|
1266
|
+
let returningClause = this.generateDeleteStatementReturningClause(Model, queryEngine, pkField, returningField, options);
|
|
1267
|
+
|
|
1268
|
+
return `DELETE FROM ${escapedTableName} AS ${escapedTableNameAlias} WHERE EXISTS (${innerSelect})${(returningClause) ? ` ${returningClause}` : ''}`;
|
|
1166
1269
|
} else {
|
|
1167
|
-
let
|
|
1168
|
-
|
|
1270
|
+
let returningField = (pkField) ? this.getEscapedColumnName(pkField.Model, pkField, options) : '*';
|
|
1271
|
+
let returningClause = this.generateDeleteStatementReturningClause(Model, queryEngine, pkField, returningField, options);
|
|
1272
|
+
|
|
1273
|
+
let {
|
|
1274
|
+
where,
|
|
1275
|
+
orderLimitOffset,
|
|
1276
|
+
} = this.generateWhereAndOrderLimitOffset(queryEngine, { ...options, forceLimit: 4294967295, separateWhereAndOrder: true });
|
|
1277
|
+
|
|
1278
|
+
return `DELETE FROM ${escapedTableName}${(where) ? ` WHERE ${where}` : ''}${(returningClause) ? ` ${returningClause}` : ''}${(orderLimitOffset) ? ` ${orderLimitOffset}` : ''}`;
|
|
1169
1279
|
}
|
|
1170
1280
|
} else {
|
|
1171
1281
|
return `DELETE FROM ${escapedTableName}`;
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mythix-orm-sql-base",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "SQL base support for Mythix ORM",
|
|
5
5
|
"main": "lib/index",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"coverage": "clear ; node ./node_modules/.bin/nyc ./node_modules/.bin/jasmine",
|
|
9
9
|
"test": "node ./node_modules/.bin/jasmine",
|
|
10
|
+
"test-fast": "node ./node_modules/.bin/jasmine --fail-fast",
|
|
10
11
|
"test-debug": "node --inspect-brk ./node_modules/.bin/jasmine",
|
|
11
12
|
"test-watch": "watch 'clear ; node ./node_modules/.bin/jasmine' . --wait=2 --interval=1"
|
|
12
13
|
},
|
|
@@ -33,7 +34,7 @@
|
|
|
33
34
|
},
|
|
34
35
|
"homepage": "https://github.com/th317erd/mythix-orm-sql-base#readme",
|
|
35
36
|
"peerDependencies": {
|
|
36
|
-
"mythix-orm": "^1.
|
|
37
|
+
"mythix-orm": "^1.11.0"
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
40
|
"luxon": "^3.1.0",
|