mythix-orm 1.0.1
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.
- package/.eslintrc.js +94 -0
- package/.vscode/launch.json +34 -0
- package/.vscode/settings.json +10 -0
- package/LICENSE +21 -0
- package/README.md +15 -0
- package/lib/connection/connection-base.js +877 -0
- package/lib/connection/index.js +11 -0
- package/lib/connection/literals/average-literal.js +14 -0
- package/lib/connection/literals/count-literal.js +18 -0
- package/lib/connection/literals/distinct-literal.js +14 -0
- package/lib/connection/literals/index.js +23 -0
- package/lib/connection/literals/literal-base.js +62 -0
- package/lib/connection/literals/literal-field-base.js +45 -0
- package/lib/connection/literals/literal.js +11 -0
- package/lib/connection/literals/max-literal.js +14 -0
- package/lib/connection/literals/min-literal.js +14 -0
- package/lib/connection/literals/sum-literal.js +14 -0
- package/lib/connection/query-generator-base.js +864 -0
- package/lib/errors/base-error.js +14 -0
- package/lib/errors/connection/access-denied-error.js +13 -0
- package/lib/errors/connection/connection-acquire-timeout-error.js +13 -0
- package/lib/errors/connection/connection-refused-error.js +13 -0
- package/lib/errors/connection/connection-timed-out-error.js +13 -0
- package/lib/errors/connection/host-not-found-error.js +13 -0
- package/lib/errors/connection/host-not-reachable-error.js +13 -0
- package/lib/errors/connection/index.js +19 -0
- package/lib/errors/connection/invalid-connection-error.js +13 -0
- package/lib/errors/connection-base-error.js +13 -0
- package/lib/errors/database/exclusion-constraint-error.js +13 -0
- package/lib/errors/database/foreign-key-constraint-error.js +13 -0
- package/lib/errors/database/index.js +13 -0
- package/lib/errors/database/timeout-error.js +13 -0
- package/lib/errors/database/unknown-constraint-error.js +13 -0
- package/lib/errors/database-base-error.js +17 -0
- package/lib/errors/index.js +44 -0
- package/lib/field.js +18 -0
- package/lib/index.js +43 -0
- package/lib/model.js +1374 -0
- package/lib/proxy-class/index.js +3 -0
- package/lib/proxy-class/proxy-class.js +269 -0
- package/lib/query-engine/field-scope.js +99 -0
- package/lib/query-engine/index.js +13 -0
- package/lib/query-engine/model-scope.js +198 -0
- package/lib/query-engine/query-engine-base.js +325 -0
- package/lib/query-engine/query-engine.js +212 -0
- package/lib/types/concrete/bigint-type.js +62 -0
- package/lib/types/concrete/blob-type.js +46 -0
- package/lib/types/concrete/boolean-type.js +41 -0
- package/lib/types/concrete/char-type.js +39 -0
- package/lib/types/concrete/date-type.js +87 -0
- package/lib/types/concrete/datetime-type.js +92 -0
- package/lib/types/concrete/float-type.js +47 -0
- package/lib/types/concrete/foreign-key-type.js +208 -0
- package/lib/types/concrete/index.js +53 -0
- package/lib/types/concrete/integer-type.js +51 -0
- package/lib/types/concrete/string-type.js +44 -0
- package/lib/types/concrete/text-type.js +44 -0
- package/lib/types/concrete/uuid-base.js +77 -0
- package/lib/types/concrete/uuid-v1-type.js +99 -0
- package/lib/types/concrete/uuid-v3-type.js +108 -0
- package/lib/types/concrete/uuid-v4-type.js +90 -0
- package/lib/types/concrete/uuid-v5-type.js +108 -0
- package/lib/types/concrete/xid-type.js +58 -0
- package/lib/types/helpers/default-helpers.js +127 -0
- package/lib/types/helpers/index.js +35 -0
- package/lib/types/index.js +94 -0
- package/lib/types/type.js +244 -0
- package/lib/types/virtual/index.js +14 -0
- package/lib/types/virtual/model-type.js +141 -0
- package/lib/types/virtual/models-type.js +264 -0
- package/lib/types/virtual/relational-type-base.js +323 -0
- package/lib/utils/index.js +65 -0
- package/lib/utils/misc-utils.js +73 -0
- package/lib/utils/model-utils.js +704 -0
- package/lib/utils/query-utils.js +186 -0
- package/package.json +63 -0
- package/playground/test01.js +15 -0
- package/spec/connection/connection-base-spec.js +126 -0
- package/spec/connection/literals/average-literal-spec.js +45 -0
- package/spec/connection/literals/count-literal-spec.js +42 -0
- package/spec/connection/literals/distinct-literal-spec.js +42 -0
- package/spec/connection/literals/literal-spec.js +26 -0
- package/spec/connection/literals/max-literal-spec.js +42 -0
- package/spec/connection/literals/min-literal-spec.js +42 -0
- package/spec/connection/literals/sum-literal-spec.js +42 -0
- package/spec/helpers/default-helpers-spec.js +108 -0
- package/spec/model-spec.js +736 -0
- package/spec/proxy-class/proxy-class-spec.js +173 -0
- package/spec/query-engine/__snapshots__/QueryEngine-operations-query_operations_and_chaining-can_chain_query_conditions-001.snapshot +94 -0
- package/spec/query-engine/__snapshots__/QueryEngine-operations-query_operations_and_chaining-can_construct_a_query_context_with_a_model_call-001.snapshot +35 -0
- package/spec/query-engine/__snapshots__/QueryEngine-operations-query_operations_and_chaining-can_construct_a_query_context_with_a_model_sub-001.snapshot +35 -0
- package/spec/query-engine/__snapshots__/QueryEngine-operations-query_operations_and_chaining-can_set_a_default_scope_on_a_model-001.snapshot +57 -0
- package/spec/query-engine/__snapshots__/QueryEngine-operations-query_operations_and_chaining-can_unscope_default_scope_on_a_model-001.snapshot +35 -0
- package/spec/query-engine/query-engine-spec.js +99 -0
- package/spec/support/jasmine.json +13 -0
- package/spec/support/models/blob-test-model.js +19 -0
- package/spec/support/models/extended-user-model.js +38 -0
- package/spec/support/models/index.js +27 -0
- package/spec/support/models/number-model.js +24 -0
- package/spec/support/models/role-model.js +26 -0
- package/spec/support/models/role-thing-model.js +41 -0
- package/spec/support/models/scoped-user-model.js +13 -0
- package/spec/support/models/time-model.js +36 -0
- package/spec/support/models/user-model.js +70 -0
- package/spec/support/models/user-role-model.js +36 -0
- package/spec/support/models/user-thing-model.js +46 -0
- package/spec/support/models/validation-test-model.js +40 -0
- package/spec/support/snapshots.js +293 -0
- package/spec/support/test-helpers.js +13 -0
- package/spec/types/concrete/bigint-type-spec.js +84 -0
- package/spec/types/concrete/boolean-type-spec.js +83 -0
- package/spec/types/concrete/date-type-spec.js +85 -0
- package/spec/types/concrete/datetime-type-spec.js +87 -0
- package/spec/types/concrete/float-type-spec.js +71 -0
- package/spec/types/concrete/foreign-key-type-spec.js +64 -0
- package/spec/types/concrete/integer-type-spec.js +71 -0
- package/spec/types/concrete/string-type-spec.js +91 -0
- package/spec/types/concrete/uuid-v1-type-spec.js +73 -0
- package/spec/types/concrete/uuid-v4-type-spec.js +65 -0
- package/spec/types/type-spec.js +101 -0
- package/spec/types/virtual/model-types-spec.js +401 -0
- package/spec/utils/misc-utils-spec.js +61 -0
- package/spec/utils/model-utils-spec.js +55 -0
- package/spec/utils/query-utils-spec.js +105 -0
|
@@ -0,0 +1,864 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Nife = require('nife');
|
|
4
|
+
const QueryEngine = require('../query-engine/query-engine');
|
|
5
|
+
const Literals = require('./literals');
|
|
6
|
+
const ModelBase = require('../model');
|
|
7
|
+
|
|
8
|
+
const LiteralBase = Literals.LiteralBase;
|
|
9
|
+
const LITERAL_IS_DISTINCT_RE = (/^DISTINCT[\s(]/i);
|
|
10
|
+
|
|
11
|
+
class QueryGeneratorBase {
|
|
12
|
+
constructor(connection) {
|
|
13
|
+
Object.defineProperties(this, {
|
|
14
|
+
'connection': {
|
|
15
|
+
writable: true,
|
|
16
|
+
enumberable: false,
|
|
17
|
+
configurable: true,
|
|
18
|
+
value: connection,
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
stackAssign(obj, ...args) {
|
|
24
|
+
let newObj = Object.create(obj || {});
|
|
25
|
+
|
|
26
|
+
Object.assign(newObj, ...args);
|
|
27
|
+
|
|
28
|
+
return newObj;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getOptionsCache(options, keyPath, initialValue) {
|
|
32
|
+
return Nife.get(options, `_cache.${keyPath}`, (typeof initialValue === 'function') ? initialValue() : initialValue);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
setOptionsCache(options, keyPath, value) {
|
|
36
|
+
Nife.set(options, `_cache.${keyPath}`, value);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
escape(...args) {
|
|
40
|
+
return this.connection.escape(...args);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
escapeID(...args) {
|
|
44
|
+
return this.connection.escapeID(...args);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getTableNameFromQueryPart(queryPart) {
|
|
48
|
+
let Model = queryPart.Model;
|
|
49
|
+
return Model.getTableName(this.connection);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getEscapedFieldName(_Model, field, options) {
|
|
53
|
+
let isString = Nife.instanceOf(field, 'string');
|
|
54
|
+
let fieldName = (isString) ? field : field.fieldName;
|
|
55
|
+
let Model = _Model;
|
|
56
|
+
|
|
57
|
+
if (!Model && field && !isString)
|
|
58
|
+
Model = field.Model;
|
|
59
|
+
|
|
60
|
+
if (!Model || (options && options.fieldNameOnly === true))
|
|
61
|
+
return this.escapeID(fieldName);
|
|
62
|
+
else
|
|
63
|
+
return `"${field.Model.getModelName()}:${fieldName}"`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getEscapedColumnName(_Model, field, options) {
|
|
67
|
+
let isString = Nife.instanceOf(field, 'string');
|
|
68
|
+
let columnName = (isString) ? field : (field.columnName || field.fieldName);
|
|
69
|
+
let Model = _Model;
|
|
70
|
+
|
|
71
|
+
if (!Model && field && !isString)
|
|
72
|
+
Model = field.Model;
|
|
73
|
+
|
|
74
|
+
if (!Model || (options && options.columnNameOnly === true))
|
|
75
|
+
return this.escapeID(columnName);
|
|
76
|
+
else
|
|
77
|
+
return `${this.escapeID(Model.getTableName(this.connection))}.${this.escapeID(columnName)}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// eslint-disable-next-line no-unused-vars
|
|
81
|
+
getEscapedProjectionName(Model, field, options) {
|
|
82
|
+
if (options && options.noProjectionAliases)
|
|
83
|
+
return this.getEscapedColumnName(Model, field, options);
|
|
84
|
+
else
|
|
85
|
+
return `${this.getEscapedColumnName(Model, field, options)} AS ${this.getEscapedFieldName(Model, field, options)}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// eslint-disable-next-line no-unused-vars
|
|
89
|
+
getEscapedModelFields(Model, options) {
|
|
90
|
+
let fields = {};
|
|
91
|
+
let modelName = Model.getModelName();
|
|
92
|
+
|
|
93
|
+
Model.iterateFields(({ field, fieldName }) => {
|
|
94
|
+
if (field.type.isVirtual())
|
|
95
|
+
return;
|
|
96
|
+
|
|
97
|
+
let result;
|
|
98
|
+
|
|
99
|
+
if (options && options.asProjection)
|
|
100
|
+
result = this.getEscapedProjectionName(Model, field, options);
|
|
101
|
+
else if (options && options.asColumn)
|
|
102
|
+
result = this.getEscapedColumnName(Model, field, options);
|
|
103
|
+
else
|
|
104
|
+
result = this.getEscapedFieldName(Model, field, options);
|
|
105
|
+
|
|
106
|
+
fields[`${modelName}:${fieldName}`] = result;
|
|
107
|
+
}, (options && options.fields));
|
|
108
|
+
|
|
109
|
+
return fields;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// eslint-disable-next-line no-unused-vars
|
|
113
|
+
getAllModelsUsedInQuery(queryEngine, _options) {
|
|
114
|
+
let options = _options || {};
|
|
115
|
+
let queryEngineContextID = queryEngine._getTopContextID();
|
|
116
|
+
let cache = this.getOptionsCache(_options, `getAllModelsUsedInQuery.${queryEngineContextID}`);
|
|
117
|
+
if (cache)
|
|
118
|
+
return cache;
|
|
119
|
+
|
|
120
|
+
let Models = new Map();
|
|
121
|
+
let query = queryEngine._getRawQuery();
|
|
122
|
+
|
|
123
|
+
for (let i = 0, il = query.length; i < il; i++) {
|
|
124
|
+
let queryPart = query[i];
|
|
125
|
+
|
|
126
|
+
if (Object.prototype.hasOwnProperty.call(queryPart, 'operator') && queryPart.operator === 'MODEL') {
|
|
127
|
+
let Model = queryPart.Model;
|
|
128
|
+
Models.set(Model, Model);
|
|
129
|
+
} else if (Object.prototype.hasOwnProperty.call(queryPart, 'condition') && queryPart.condition === true) {
|
|
130
|
+
let operatorValue = queryPart.value;
|
|
131
|
+
if (!QueryEngine.isQuery(operatorValue))
|
|
132
|
+
continue;
|
|
133
|
+
|
|
134
|
+
let SubModels = this.getAllModelsUsedInQuery(operatorValue, options);
|
|
135
|
+
for (let j = 0, jl = SubModels.length; j < jl; j++) {
|
|
136
|
+
let Model = SubModels[j];
|
|
137
|
+
Models.set(Model, Model);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let allModels = Array.from(Models.values());
|
|
143
|
+
|
|
144
|
+
if (_options)
|
|
145
|
+
this.setOptionsCache(_options, `getAllModelsUsedInQuery.${queryEngineContextID}`, allModels);
|
|
146
|
+
|
|
147
|
+
return allModels;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
getProjectionRequiredFields(queryEngine, options) {
|
|
151
|
+
let { order } = this.getOrderLimitOffset(queryEngine, options);
|
|
152
|
+
if (!order)
|
|
153
|
+
return new Map();
|
|
154
|
+
|
|
155
|
+
let orderFieldMap = new Map();
|
|
156
|
+
for (let i = 0, il = order.length; i < il; i++) {
|
|
157
|
+
let field = order[i];
|
|
158
|
+
if (!field)
|
|
159
|
+
continue;
|
|
160
|
+
|
|
161
|
+
if (field instanceof LiteralBase) {
|
|
162
|
+
let result = field.toString(this.connection);
|
|
163
|
+
let projectionField = this.parseFieldProjection(result, true);
|
|
164
|
+
if (projectionField === result) {
|
|
165
|
+
// not able to parse projection
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
field = projectionField;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let Field = field.Field || field;
|
|
173
|
+
let Model = Field.Model;
|
|
174
|
+
let modelName = Model.getModelName();
|
|
175
|
+
let fullFieldName = `${modelName}:${Field.fieldName}`;
|
|
176
|
+
|
|
177
|
+
orderFieldMap.set(fullFieldName, {
|
|
178
|
+
projectedName: this.getEscapedProjectionName(Model, Field, options),
|
|
179
|
+
Model: Model,
|
|
180
|
+
modelName: modelName,
|
|
181
|
+
Field: Field,
|
|
182
|
+
fieldName: Field.fieldName,
|
|
183
|
+
direction: '+',
|
|
184
|
+
fullFieldName,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return orderFieldMap;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
sortedProjectedFields(projectedFields) {
|
|
192
|
+
return projectedFields.sort((a, b) => {
|
|
193
|
+
// If either value is a distinct literal
|
|
194
|
+
// then make certain it comes first in
|
|
195
|
+
// the list of projected fields (required
|
|
196
|
+
// by some databases)
|
|
197
|
+
const distinctSortOrder = (a, b) => {
|
|
198
|
+
let x = (a instanceof LiteralBase);
|
|
199
|
+
let y = (b instanceof LiteralBase);
|
|
200
|
+
let xStr = (x) ? a.toString(this.connection) : a;
|
|
201
|
+
let yStr = (y) ? b.toString(this.connection) : b;
|
|
202
|
+
let xIsDistinct = (typeof xStr === 'string' && LITERAL_IS_DISTINCT_RE.test(xStr));
|
|
203
|
+
let yIsDistinct = (typeof yStr === 'string' && LITERAL_IS_DISTINCT_RE.test(yStr));
|
|
204
|
+
|
|
205
|
+
if (xIsDistinct || yIsDistinct) {
|
|
206
|
+
if (xIsDistinct === yIsDistinct)
|
|
207
|
+
return 0;
|
|
208
|
+
|
|
209
|
+
if (xIsDistinct && !yIsDistinct)
|
|
210
|
+
return -1;
|
|
211
|
+
|
|
212
|
+
if (yIsDistinct && !xIsDistinct)
|
|
213
|
+
return 1;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Neither is a literal, so simply
|
|
217
|
+
// return and continue processing
|
|
218
|
+
if (!x && !y)
|
|
219
|
+
return;
|
|
220
|
+
|
|
221
|
+
// We know one of them must be a literal
|
|
222
|
+
// when a literal is encountered, the
|
|
223
|
+
// sort order is not modified
|
|
224
|
+
return 0;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
let distinctOrder = distinctSortOrder(a, b);
|
|
228
|
+
if (distinctOrder != null)
|
|
229
|
+
return distinctOrder;
|
|
230
|
+
|
|
231
|
+
let x = (typeof a === 'string') ? a : `${a.modelName}:${a.fieldName}`;
|
|
232
|
+
let y = (typeof b === 'string') ? b : `${b.modelName}:${b.fieldName}`;
|
|
233
|
+
|
|
234
|
+
if (x === y)
|
|
235
|
+
return 0;
|
|
236
|
+
|
|
237
|
+
return (x < y) ? -1 : 1;
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
getProjectionFromQueryEngine(queryEngine, options) {
|
|
242
|
+
let queryEngineContextID = queryEngine._getTopContextID();
|
|
243
|
+
let cache = this.getOptionsCache(options, `getProjectionFromQueryEngine.${queryEngineContextID}`);
|
|
244
|
+
if (cache)
|
|
245
|
+
return cache;
|
|
246
|
+
|
|
247
|
+
const shouldResetProjection = (fields) => {
|
|
248
|
+
if (fields.length === 0)
|
|
249
|
+
return false;
|
|
250
|
+
|
|
251
|
+
if (fields.indexOf('+') >= 0)
|
|
252
|
+
return false;
|
|
253
|
+
|
|
254
|
+
for (let i = 0, il = fields.length; i < il; i++) {
|
|
255
|
+
let field = fields[i];
|
|
256
|
+
|
|
257
|
+
if (field instanceof LiteralBase)
|
|
258
|
+
return true;
|
|
259
|
+
|
|
260
|
+
if (ModelBase.isModelClass(field))
|
|
261
|
+
return true;
|
|
262
|
+
|
|
263
|
+
if (!Nife.instanceOf(field, 'string'))
|
|
264
|
+
true;
|
|
265
|
+
|
|
266
|
+
if (field === '-')
|
|
267
|
+
return true;
|
|
268
|
+
|
|
269
|
+
if (field === '*')
|
|
270
|
+
continue;
|
|
271
|
+
|
|
272
|
+
if (!(/^\s*[+-]/).test(field))
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return false;
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const collectProjectionValuesFromQuery = (RootModel, query) => {
|
|
280
|
+
let projections = [ RootModel ];
|
|
281
|
+
|
|
282
|
+
for (let i = 0, il = query.length; i < il; i++) {
|
|
283
|
+
let queryPart = query[i];
|
|
284
|
+
|
|
285
|
+
if (!Object.prototype.hasOwnProperty.call(queryPart, 'control'))
|
|
286
|
+
continue;
|
|
287
|
+
|
|
288
|
+
if (queryPart.control !== true)
|
|
289
|
+
continue;
|
|
290
|
+
|
|
291
|
+
if (queryPart.operator !== 'PROJECT')
|
|
292
|
+
continue;
|
|
293
|
+
|
|
294
|
+
let fields = queryPart.value;
|
|
295
|
+
if (shouldResetProjection(fields)) {
|
|
296
|
+
fields = Nife.arrayFlatten(fields);
|
|
297
|
+
|
|
298
|
+
let addIndex = fields.indexOf('+');
|
|
299
|
+
let newProjections = fields.slice();
|
|
300
|
+
|
|
301
|
+
if (addIndex >= 0)
|
|
302
|
+
newProjections[addIndex] = Nife.arrayFlatten(projections);
|
|
303
|
+
|
|
304
|
+
projections = newProjections;
|
|
305
|
+
} else {
|
|
306
|
+
projections = projections.concat(fields);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return Nife.uniq(Nife.arrayFlatten(projections));
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const modelFieldsToProjection = (fieldSet, Model, removeFromProjection) => {
|
|
314
|
+
let modelName = Model.getModelName();
|
|
315
|
+
|
|
316
|
+
Model.iterateFields(({ field, fieldName }) => {
|
|
317
|
+
if (field.type.isVirtual())
|
|
318
|
+
return;
|
|
319
|
+
|
|
320
|
+
let fullFieldName = `${modelName}:${fieldName}`;
|
|
321
|
+
|
|
322
|
+
if (removeFromProjection) {
|
|
323
|
+
fieldSet.delete(fullFieldName);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
fieldSet.set(fullFieldName, {
|
|
328
|
+
projectedName: this.getEscapedProjectionName(Model, field, options),
|
|
329
|
+
Model: Model,
|
|
330
|
+
Field: field,
|
|
331
|
+
direction: '+',
|
|
332
|
+
fullFieldName,
|
|
333
|
+
fieldName,
|
|
334
|
+
modelName,
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const addRequiredFieldsToProjection = (fieldSet) => {
|
|
340
|
+
let requiredProjectionFields = this.getProjectionRequiredFields(queryEngine, options);
|
|
341
|
+
|
|
342
|
+
for (let [ key, value ] of requiredProjectionFields) {
|
|
343
|
+
if (fieldSet.has(key))
|
|
344
|
+
continue;
|
|
345
|
+
|
|
346
|
+
fieldSet.set(key, value);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return fieldSet;
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
const finalizeProjection = (_projectedFields) => {
|
|
353
|
+
let projectedFields = _projectedFields;
|
|
354
|
+
let result;
|
|
355
|
+
|
|
356
|
+
// If we are sub-selecting then only the last
|
|
357
|
+
// field in the projection should be the projection
|
|
358
|
+
if (options && options.isSubQuery) {
|
|
359
|
+
result = result = Array.from(projectedFields.values());
|
|
360
|
+
|
|
361
|
+
let lastField = result[result.length - 1];
|
|
362
|
+
|
|
363
|
+
if (lastField instanceof LiteralBase)
|
|
364
|
+
result = [ lastField ];
|
|
365
|
+
else if (typeof lastField === 'string')
|
|
366
|
+
result = [ lastField ];
|
|
367
|
+
else
|
|
368
|
+
result = [ new Literals.DistinctLiteral(lastField.fullFieldName) ];
|
|
369
|
+
} else {
|
|
370
|
+
// If distinct specifies a field, then
|
|
371
|
+
// make sure that field isn't specified twice
|
|
372
|
+
if (hasDistinct) {
|
|
373
|
+
let distinctFieldName = hasDistinct.getFullyQualifiedFieldName();
|
|
374
|
+
if (distinctFieldName)
|
|
375
|
+
projectedFields.delete(distinctFieldName);
|
|
376
|
+
} else {
|
|
377
|
+
projectedFields = addRequiredFieldsToProjection(projectedFields);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
result = Array.from(projectedFields.values());
|
|
381
|
+
|
|
382
|
+
// Convert projection fields to array and sort
|
|
383
|
+
result = this.sortedProjectedFields(result);
|
|
384
|
+
|
|
385
|
+
// Now prefix the projection fields with the distinct
|
|
386
|
+
// literal if one exists on the query
|
|
387
|
+
if (hasDistinct)
|
|
388
|
+
result = [ hasDistinct ].concat(result);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return result;
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
let rawQueryContext = queryEngine._getRawQueryContext();
|
|
395
|
+
let RootModel = rawQueryContext.rootModel;
|
|
396
|
+
if (!RootModel)
|
|
397
|
+
throw new Error('QueryGeneratorBase::getProjectionFromQueryEngine: No root model found for query. Root model is required to generate a projection.');
|
|
398
|
+
|
|
399
|
+
let hasDistinct = rawQueryContext.distinct;
|
|
400
|
+
let projections = collectProjectionValuesFromQuery(RootModel, queryEngine._getRawQuery());
|
|
401
|
+
let projectedFields = new Map();
|
|
402
|
+
let allModels = this.getAllModelsUsedInQuery(queryEngine, options);
|
|
403
|
+
let isAdding = true;
|
|
404
|
+
|
|
405
|
+
// If projection is empty, then return
|
|
406
|
+
// the projection of the root model
|
|
407
|
+
if (Nife.isEmpty(projections)) {
|
|
408
|
+
modelFieldsToProjection(projectedFields, RootModel);
|
|
409
|
+
return finalizeProjection(projectedFields);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
for (let i = 0, il = projections.length; i < il; i++) {
|
|
413
|
+
let projectionValue = projections[i];
|
|
414
|
+
if (!projectionValue)
|
|
415
|
+
continue;
|
|
416
|
+
|
|
417
|
+
if (projectionValue === '+') {
|
|
418
|
+
isAdding = true;
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (projectionValue === '-') {
|
|
423
|
+
isAdding = false;
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (projectionValue === '*') {
|
|
428
|
+
for (let i = 0, il = allModels.length; i < il; i++) {
|
|
429
|
+
let Model = allModels[i];
|
|
430
|
+
modelFieldsToProjection(projectedFields, Model);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (projectionValue instanceof LiteralBase) {
|
|
437
|
+
// If we already have distinct specified on the query
|
|
438
|
+
// then skip any distinct values specified by the user
|
|
439
|
+
if (hasDistinct && projectionValue instanceof Literals.DistinctLiteral)
|
|
440
|
+
continue;
|
|
441
|
+
|
|
442
|
+
let key = projectionValue.toString(this.connection);
|
|
443
|
+
if (isAdding)
|
|
444
|
+
projectedFields.set(key, projectionValue);
|
|
445
|
+
else
|
|
446
|
+
projectedFields.delete(key);
|
|
447
|
+
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (ModelBase.isModelClass(projectionValue)) {
|
|
452
|
+
if (allModels.indexOf(projectionValue) >= 0)
|
|
453
|
+
modelFieldsToProjection(projectedFields, projectionValue, !isAdding);
|
|
454
|
+
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
let { fieldName, direction, hasDirection } = this.getFieldDirectionSpecifier(projectionValue);
|
|
459
|
+
if (!fieldName)
|
|
460
|
+
continue;
|
|
461
|
+
|
|
462
|
+
if (!hasDirection) {
|
|
463
|
+
direction = (isAdding) ? '+' : '-';
|
|
464
|
+
hasDirection = true;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
let def = this.connection.parseQualifiedName(fieldName);
|
|
468
|
+
if (!def.modelName) {
|
|
469
|
+
if (Nife.isNotEmpty(def.fieldNames) && allModels.length > 1)
|
|
470
|
+
throw new Error(`QueryGeneratorBase::getProjectionFromQueryEngine: "${def.fieldNames[0]}" ambiguous. You must use a fully qualified field name for an ORDER clause. Example: "+Model:id".`);
|
|
471
|
+
|
|
472
|
+
def.modelName = RootModel.getModelName();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
let ProjectionModel = this.connection.getModel(def.modelName);
|
|
476
|
+
if (!ProjectionModel) {
|
|
477
|
+
if (hasDirection && direction === '-') {
|
|
478
|
+
projectedFields.delete(projectionValue);
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Proceed blindly... as the user may be
|
|
483
|
+
// querying something we are unaware of
|
|
484
|
+
projectedFields.set(projectionValue, projectionValue);
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (allModels.indexOf(ProjectionModel) < 0)
|
|
489
|
+
continue;
|
|
490
|
+
|
|
491
|
+
if (!def.fieldNames.length) {
|
|
492
|
+
// If there are no field names, but we have a model name
|
|
493
|
+
// then let the projection generator generate all model
|
|
494
|
+
// fields
|
|
495
|
+
|
|
496
|
+
if (ProjectionModel) {
|
|
497
|
+
modelFieldsToProjection(projectedFields, ProjectionModel, (hasDirection && direction === '-'));
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
let field = this.connection.getField(def.fieldNames[0], def.modelName);
|
|
503
|
+
let modelName = (field) ? field.Model.getModelName() : ProjectionModel.getModelName();
|
|
504
|
+
|
|
505
|
+
if (!field) {
|
|
506
|
+
let projectionFieldName = def.fieldNames[0];
|
|
507
|
+
let fullFieldName = `${modelName}:${projectionFieldName}`;
|
|
508
|
+
|
|
509
|
+
if (hasDirection && direction === '-') {
|
|
510
|
+
projectedFields.delete(fullFieldName);
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Proceed blindly... as the user may be
|
|
515
|
+
// querying something we are unaware of
|
|
516
|
+
projectedFields.set(fullFieldName, {
|
|
517
|
+
projectedName: this.getEscapedProjectionName(ProjectionModel, fieldName, options),
|
|
518
|
+
Model: ProjectionModel,
|
|
519
|
+
Field: null,
|
|
520
|
+
fieldName: projectionFieldName,
|
|
521
|
+
direction: '+',
|
|
522
|
+
modelName,
|
|
523
|
+
fullFieldName,
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
let projectionFieldName = field.fieldName;
|
|
530
|
+
let fullFieldName = `${modelName}:${projectionFieldName}`;
|
|
531
|
+
|
|
532
|
+
if (hasDirection && direction === '-') {
|
|
533
|
+
projectedFields.delete(fullFieldName);
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
projectedFields.set(fullFieldName, {
|
|
538
|
+
projectedName: this.getEscapedProjectionName(field.Model, field, options),
|
|
539
|
+
Model: field.Model,
|
|
540
|
+
Field: field,
|
|
541
|
+
fieldName: field.fieldName,
|
|
542
|
+
fullFieldName,
|
|
543
|
+
modelName,
|
|
544
|
+
direction,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
let result = finalizeProjection(projectedFields);
|
|
549
|
+
|
|
550
|
+
if (options)
|
|
551
|
+
this.setOptionsCache(options, `getProjectionFromQueryEngine.${queryEngineContextID}`, result);
|
|
552
|
+
|
|
553
|
+
return result;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
getProjectedFields(queryEngine, _options, asMap) {
|
|
557
|
+
let queryProjection = this.getProjectionFromQueryEngine(queryEngine, _options);
|
|
558
|
+
let allProjectionFields = new Map();
|
|
559
|
+
|
|
560
|
+
for (let i = 0, il = queryProjection.length; i < il; i++) {
|
|
561
|
+
let projectionField = queryProjection[i];
|
|
562
|
+
if (!projectionField)
|
|
563
|
+
continue;
|
|
564
|
+
|
|
565
|
+
if (projectionField instanceof LiteralBase) {
|
|
566
|
+
let result = projectionField.toString(this.connection);
|
|
567
|
+
let fullFieldName = this.parseFieldProjection(result);
|
|
568
|
+
if (!fullFieldName)
|
|
569
|
+
fullFieldName = result;
|
|
570
|
+
|
|
571
|
+
if (fullFieldName && result)
|
|
572
|
+
allProjectionFields.set(fullFieldName, result);
|
|
573
|
+
|
|
574
|
+
if (result && !this.isFieldProjection(result))
|
|
575
|
+
allProjectionFields.set(result, result);
|
|
576
|
+
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (projectionField.direction === '-')
|
|
581
|
+
continue;
|
|
582
|
+
|
|
583
|
+
if (Nife.instanceOf(projectionField, 'string')) {
|
|
584
|
+
allProjectionFields.set(projectionField, projectionField);
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
let projectedName = projectionField.projectedName;
|
|
589
|
+
allProjectionFields.set(projectionField.fullFieldName, projectedName);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (asMap === true)
|
|
593
|
+
return allProjectionFields;
|
|
594
|
+
else
|
|
595
|
+
return Array.from(allProjectionFields.values());
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// eslint-disable-next-line no-unused-vars
|
|
599
|
+
getJoinTableInfoFromQueryContexts(leftQueryContext, rightQueryContext, joinType, options) {
|
|
600
|
+
let rootModel = leftQueryContext.rootModel;
|
|
601
|
+
let rootModelName = rootModel.getModelName();
|
|
602
|
+
let leftSideModel = leftQueryContext.Model;
|
|
603
|
+
let leftSideModelName = leftQueryContext.modelName;
|
|
604
|
+
if (!leftSideModel)
|
|
605
|
+
throw new Error(`${this.constructor.name}::getJoinTableInfoFromQueryEngine: Invalid operation: No model found for left-side of join statement.`);
|
|
606
|
+
|
|
607
|
+
let leftSideField = leftQueryContext.Field;
|
|
608
|
+
if (!leftSideField)
|
|
609
|
+
throw new Error(`${this.constructor.name}::getJoinTableInfoFromQueryEngine: Invalid operation: No left-side field found to match on for table join statement.`);
|
|
610
|
+
|
|
611
|
+
let isNot = leftQueryContext.not;
|
|
612
|
+
let operator = (isNot) ? leftQueryContext.inverseOperator : leftQueryContext.operator;
|
|
613
|
+
let rightSideModel = rightQueryContext.Model;
|
|
614
|
+
let rightSideModelName = rightQueryContext.modelName;
|
|
615
|
+
if (!rightSideModel)
|
|
616
|
+
throw new Error(`${this.constructor.name}::getJoinTableInfoFromQueryEngine: Invalid operation: No model found for right-side of join statement.`);
|
|
617
|
+
|
|
618
|
+
let rightSideField = rightQueryContext.Field;
|
|
619
|
+
if (!rightSideField)
|
|
620
|
+
throw new Error(`${this.constructor.name}::getJoinTableInfoFromQueryEngine: Invalid operation: No right-side field found to match on for table join statement.`);
|
|
621
|
+
|
|
622
|
+
let swapJoinRelation = (rightSideModelName === rootModelName);
|
|
623
|
+
let joinModel = (swapJoinRelation) ? leftSideModel : rightSideModel;
|
|
624
|
+
let joinModelName = (swapJoinRelation) ? leftSideModelName : rightSideModelName;
|
|
625
|
+
|
|
626
|
+
return {
|
|
627
|
+
operator,
|
|
628
|
+
joinType,
|
|
629
|
+
rootModelName,
|
|
630
|
+
|
|
631
|
+
joinModel,
|
|
632
|
+
joinModelName,
|
|
633
|
+
|
|
634
|
+
leftSideModel,
|
|
635
|
+
leftSideModelName,
|
|
636
|
+
leftQueryContext,
|
|
637
|
+
leftSideField,
|
|
638
|
+
|
|
639
|
+
rightSideModel,
|
|
640
|
+
rightSideModelName,
|
|
641
|
+
rightQueryContext,
|
|
642
|
+
rightSideField,
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
getFieldDirectionSpecifier(order) {
|
|
647
|
+
if (!order)
|
|
648
|
+
return order;
|
|
649
|
+
|
|
650
|
+
if (order instanceof LiteralBase)
|
|
651
|
+
return order;
|
|
652
|
+
|
|
653
|
+
// Is this a field?
|
|
654
|
+
if (order && order.Model && order.fieldName) {
|
|
655
|
+
return {
|
|
656
|
+
hasDirection: false,
|
|
657
|
+
direction: '+',
|
|
658
|
+
fieldName: order.fieldName,
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
let sign;
|
|
663
|
+
|
|
664
|
+
let fieldName = ('' + order).replace(/^[+-]+/, (m) => {
|
|
665
|
+
sign = m.charAt(0);
|
|
666
|
+
return '';
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
return {
|
|
670
|
+
hasDirection: !!sign,
|
|
671
|
+
direction: (sign === '-') ? '-' : '+',
|
|
672
|
+
fieldName,
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// eslint-disable-next-line no-unused-vars
|
|
677
|
+
getOrderLimitOffset(queryEngine, options) {
|
|
678
|
+
let queryEngineContextID = queryEngine._getTopContextID();
|
|
679
|
+
let cache = this.getOptionsCache(options, `getOrderLimitOffset.${queryEngineContextID}`);
|
|
680
|
+
if (cache)
|
|
681
|
+
return cache;
|
|
682
|
+
|
|
683
|
+
let query = queryEngine._getRawQuery();
|
|
684
|
+
let rootModel = queryEngine._getRawQueryContext().rootModel;
|
|
685
|
+
let limit;
|
|
686
|
+
let offset;
|
|
687
|
+
let order;
|
|
688
|
+
|
|
689
|
+
for (let i = 0, il = query.length; i < il; i++) {
|
|
690
|
+
let queryPart = query[i];
|
|
691
|
+
|
|
692
|
+
if (!Object.prototype.hasOwnProperty.call(queryPart, 'control'))
|
|
693
|
+
continue;
|
|
694
|
+
|
|
695
|
+
if (queryPart.control !== true)
|
|
696
|
+
continue;
|
|
697
|
+
|
|
698
|
+
let queryOperator = queryPart.operator;
|
|
699
|
+
|
|
700
|
+
if (queryOperator === 'LIMIT')
|
|
701
|
+
limit = queryPart.value;
|
|
702
|
+
else if (queryOperator === 'OFFSET')
|
|
703
|
+
offset = queryPart.value;
|
|
704
|
+
else if (queryOperator === 'ORDER')
|
|
705
|
+
order = queryPart.value;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if (Nife.isNotEmpty(order) && !(order instanceof LiteralBase)) {
|
|
709
|
+
let allModels = this.getAllModelsUsedInQuery(queryEngine, options);
|
|
710
|
+
|
|
711
|
+
order = order.map((_fieldName) => {
|
|
712
|
+
if (_fieldName instanceof LiteralBase)
|
|
713
|
+
return _fieldName;
|
|
714
|
+
|
|
715
|
+
let { fieldName, direction } = this.getFieldDirectionSpecifier(_fieldName);
|
|
716
|
+
if (!fieldName)
|
|
717
|
+
return;
|
|
718
|
+
|
|
719
|
+
let def = this.connection.parseQualifiedName(fieldName);
|
|
720
|
+
if (!def.modelName) {
|
|
721
|
+
if (allModels.length > 1)
|
|
722
|
+
throw new Error(`QueryGeneratorBase::getOrderLimitOffset: "${fieldName}" ambiguous. You must use a fully qualified field name for an ORDER clause. Example: "+Model:id".`);
|
|
723
|
+
|
|
724
|
+
def.modelName = allModels[0].getModelName();
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
if (!def.fieldNames.length)
|
|
728
|
+
throw new Error(`QueryGeneratorBase::getOrderLimitOffset: No field names found for "${fieldName}".`);
|
|
729
|
+
|
|
730
|
+
let field = this.connection.getField(def.fieldNames[0], def.modelName);
|
|
731
|
+
if (!field)
|
|
732
|
+
throw new Error(`QueryGeneratorBase::getOrderLimitOffset: Unable to locate field "${def.modelName}"."${def.fieldNames[0]}".`);
|
|
733
|
+
|
|
734
|
+
return {
|
|
735
|
+
Model: field.Model,
|
|
736
|
+
Field: field,
|
|
737
|
+
direction,
|
|
738
|
+
};
|
|
739
|
+
});
|
|
740
|
+
} else if (Nife.isEmpty(order)) {
|
|
741
|
+
if (options && options.selectStatement === true)
|
|
742
|
+
order = this.connection.getDefaultOrder(rootModel, options);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
let orderLimitOffset = { limit, order, offset };
|
|
746
|
+
|
|
747
|
+
if (options)
|
|
748
|
+
this.setOptionsCache(options, `getOrderLimitOffset.${queryEngineContextID}`, orderLimitOffset);
|
|
749
|
+
|
|
750
|
+
return orderLimitOffset;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
queryHasConditions(query) {
|
|
754
|
+
for (let i = 0, il = query.length; i < il; i++) {
|
|
755
|
+
let queryPart = query[i];
|
|
756
|
+
if (!Object.prototype.hasOwnProperty.call(queryPart, 'condition'))
|
|
757
|
+
continue;
|
|
758
|
+
|
|
759
|
+
if (queryPart.condition === true)
|
|
760
|
+
return true;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
return false;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
getQuerySliceFromQueryPart(queryPart) {
|
|
767
|
+
let queryRoot = queryPart.queryRoot;
|
|
768
|
+
let index = queryRoot.indexOf(queryPart);
|
|
769
|
+
|
|
770
|
+
return queryRoot.slice(index);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
_averageLiteralToString(literal) {
|
|
774
|
+
if (!literal || !(literal instanceof LiteralBase))
|
|
775
|
+
return;
|
|
776
|
+
|
|
777
|
+
let field = literal.definitionToField(this.connection, literal.definition);
|
|
778
|
+
let escapedFieldName;
|
|
779
|
+
|
|
780
|
+
if (field instanceof LiteralBase)
|
|
781
|
+
escapedFieldName = field.toString(this.connection);
|
|
782
|
+
else
|
|
783
|
+
escapedFieldName = this.getEscapedColumnName(field.Model, field, literal.options);
|
|
784
|
+
|
|
785
|
+
return `AVG(${escapedFieldName})`;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
_countLiteralToString(literal) {
|
|
789
|
+
if (!literal || !(literal instanceof LiteralBase))
|
|
790
|
+
return;
|
|
791
|
+
|
|
792
|
+
let field = (literal.definition) ? literal.definitionToField(this.connection, literal.definition) : null;
|
|
793
|
+
let escapedFieldName;
|
|
794
|
+
|
|
795
|
+
if (field) {
|
|
796
|
+
if (field instanceof LiteralBase)
|
|
797
|
+
escapedFieldName = field.toString(this.connection);
|
|
798
|
+
else
|
|
799
|
+
escapedFieldName = this.getEscapedColumnName(field.Model, field, literal.options);
|
|
800
|
+
} else {
|
|
801
|
+
escapedFieldName = '*';
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
return `COUNT(${escapedFieldName})`;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
_distinctLiteralToString(literal) {
|
|
808
|
+
if (!literal || !(literal instanceof LiteralBase))
|
|
809
|
+
return;
|
|
810
|
+
|
|
811
|
+
let field = literal.definitionToField(this.connection, literal.definition);
|
|
812
|
+
if (field instanceof LiteralBase)
|
|
813
|
+
return `DISTINCT ${field.toString(this.connection)}`;
|
|
814
|
+
|
|
815
|
+
return `DISTINCT ${this.getEscapedProjectionName(field.Model, field, literal.options)}`;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
_maxLiteralToString(literal) {
|
|
819
|
+
if (!literal || !(literal instanceof LiteralBase))
|
|
820
|
+
return;
|
|
821
|
+
|
|
822
|
+
let field = literal.definitionToField(this.connection, literal.definition);
|
|
823
|
+
let escapedFieldName;
|
|
824
|
+
|
|
825
|
+
if (field instanceof LiteralBase)
|
|
826
|
+
escapedFieldName = field.toString(this.connection);
|
|
827
|
+
else
|
|
828
|
+
escapedFieldName = this.getEscapedColumnName(field.Model, field, literal.options);
|
|
829
|
+
|
|
830
|
+
return `MAX(${escapedFieldName})`;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
_minLiteralToString(literal) {
|
|
834
|
+
if (!literal || !(literal instanceof LiteralBase))
|
|
835
|
+
return;
|
|
836
|
+
|
|
837
|
+
let field = literal.definitionToField(this.connection, literal.definition);
|
|
838
|
+
let escapedFieldName;
|
|
839
|
+
|
|
840
|
+
if (field instanceof LiteralBase)
|
|
841
|
+
escapedFieldName = field.toString(this.connection);
|
|
842
|
+
else
|
|
843
|
+
escapedFieldName = this.getEscapedColumnName(field.Model, field, literal.options);
|
|
844
|
+
|
|
845
|
+
return `MIN(${escapedFieldName})`;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
_sumLiteralToString(literal) {
|
|
849
|
+
if (!literal || !(literal instanceof LiteralBase))
|
|
850
|
+
return;
|
|
851
|
+
|
|
852
|
+
let field = literal.definitionToField(this.connection, literal.definition);
|
|
853
|
+
let escapedFieldName;
|
|
854
|
+
|
|
855
|
+
if (field instanceof LiteralBase)
|
|
856
|
+
escapedFieldName = field.toString(this.connection);
|
|
857
|
+
else
|
|
858
|
+
escapedFieldName = this.getEscapedColumnName(field.Model, field, literal.options);
|
|
859
|
+
|
|
860
|
+
return `SUM(${escapedFieldName})`;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
module.exports = QueryGeneratorBase;
|