mythix-orm 1.8.3 → 1.11.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.
@@ -1,11 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const Nife = require('nife');
4
- const QueryEngine = require('../query-engine/query-engine');
5
- const Literals = require('./literals');
6
- const { Model: ModelBase } = require('../model');
7
- const LiteralBase = Literals.LiteralBase;
8
- const LITERAL_IS_DISTINCT_RE = (/^DISTINCT[\s(]/i);
3
+ const Nife = require('nife');
4
+ const Literals = require('./literals');
5
+ const LiteralBase = Literals.LiteralBase;
9
6
 
10
7
  /// The base query generator class.
11
8
  ///
@@ -32,14 +29,6 @@ class QueryGeneratorBase {
32
29
  return newObj;
33
30
  }
34
31
 
35
- getOptionsCache(options, keyPath, initialValue) {
36
- return Nife.get(options, `_cache.${keyPath}`, (typeof initialValue === 'function') ? initialValue() : initialValue);
37
- }
38
-
39
- setOptionsCache(options, keyPath, value) {
40
- Nife.set(options, `_cache.${keyPath}`, value);
41
- }
42
-
43
32
  escape(...args) {
44
33
  return this.connection.escape(...args);
45
34
  }
@@ -94,7 +83,7 @@ class QueryGeneratorBase {
94
83
  if (options && options.noProjectionAliases)
95
84
  return this.getEscapedColumnName(Model, field, options);
96
85
  else
97
- return `${this.getEscapedColumnName(Model, field, options)} AS ${this.getEscapedFieldName(Model, field, options)}`;
86
+ return `${this.getEscapedColumnName(Model, field, options)} AS ${(options && options.as) ? this.escapeID(options.as) : this.getEscapedFieldName(Model, field, options)}`;
98
87
  }
99
88
 
100
89
  // eslint-disable-next-line no-unused-vars
@@ -121,504 +110,55 @@ class QueryGeneratorBase {
121
110
  return fields;
122
111
  }
123
112
 
124
- // eslint-disable-next-line no-unused-vars
125
- getAllModelsUsedInQuery(queryEngine, _options) {
126
- let options = _options || {};
127
- let queryEngineContextID = queryEngine._getTopContextID();
128
- let cache = this.getOptionsCache(_options, `getAllModelsUsedInQuery.${queryEngineContextID}`);
129
- if (cache)
130
- return cache;
131
-
132
- let Models = new Map();
133
- let query = queryEngine._getRawQuery();
134
-
135
- for (let i = 0, il = query.length; i < il; i++) {
136
- let queryPart = query[i];
137
-
138
- if (Object.prototype.hasOwnProperty.call(queryPart, 'operator') && queryPart.operator === 'MODEL') {
139
- let Model = queryPart.Model;
140
- Models.set(Model, Model);
141
- } else if (Object.prototype.hasOwnProperty.call(queryPart, 'condition') && queryPart.condition === true) {
142
- let operatorValue = queryPart.value;
143
- if (!QueryEngine.isQuery(operatorValue))
144
- continue;
145
-
146
- let SubModels = this.getAllModelsUsedInQuery(operatorValue, options);
147
- for (let j = 0, jl = SubModels.length; j < jl; j++) {
148
- let Model = SubModels[j];
149
- Models.set(Model, Model);
150
- }
151
- }
152
- }
153
-
154
- let allModels = Array.from(Models.values());
155
-
156
- if (_options)
157
- this.setOptionsCache(_options, `getAllModelsUsedInQuery.${queryEngineContextID}`, allModels);
158
-
159
- return allModels;
160
- }
161
-
162
- getProjectionRequiredFields(queryEngine, options) {
163
- let { order } = this.getOrderLimitOffset(queryEngine, options);
164
- if (!order)
165
- return new Map();
166
-
167
- let orderFieldMap = new Map();
168
- for (let i = 0, il = order.length; i < il; i++) {
169
- let field = order[i];
170
- if (!field)
171
- continue;
172
-
173
- if (LiteralBase.isLiteral(field)) {
174
- let result = field.toString(this.connection, options);
175
- let projectionField;
176
-
177
- if (typeof field.getField === 'function')
178
- projectionField = field.getField(this.connection);
179
- else
180
- projectionField = this.parseFieldProjection(result, true);
181
-
182
- if (projectionField === result) {
183
- // not able to parse projection
184
- continue;
185
- }
186
-
187
- field = projectionField;
188
- }
189
-
190
- let Field = field.Field || field;
191
- let Model = Field.Model;
192
- let modelName = Model.getModelName();
193
- let fullFieldName = `${modelName}:${Field.fieldName}`;
194
-
195
- orderFieldMap.set(fullFieldName, {
196
- projectedName: this.getEscapedProjectionName(Model, Field, options),
197
- Model: Model,
198
- modelName: modelName,
199
- Field: Field,
200
- fieldName: Field.fieldName,
201
- direction: '+',
202
- fullFieldName,
203
- });
204
- }
205
-
206
- return orderFieldMap;
113
+ isFieldIdentifier(str) {
114
+ return (/^"[^"]+"."[^"]+"|"\w+:[\w.]+"/i).test(str);
207
115
  }
208
116
 
209
- sortedProjectedFields(projectedFields, options) {
210
- return projectedFields.sort((a, b) => {
211
- // If either value is a distinct literal
212
- // then make certain it comes first in
213
- // the list of projected fields (required
214
- // by some databases)
215
- const distinctSortOrder = (a, b) => {
216
- let x = LiteralBase.isLiteral(a);
217
- let y = LiteralBase.isLiteral(b);
218
- let xStr = (x) ? a.toString(this.connection, options) : a;
219
- let yStr = (y) ? b.toString(this.connection, options) : b;
220
- let xIsDistinct = (typeof xStr === 'string' && LITERAL_IS_DISTINCT_RE.test(xStr));
221
- let yIsDistinct = (typeof yStr === 'string' && LITERAL_IS_DISTINCT_RE.test(yStr));
222
-
223
- if (xIsDistinct || yIsDistinct) {
224
- if (xIsDistinct === yIsDistinct)
225
- return 0;
226
-
227
- if (xIsDistinct && !yIsDistinct)
228
- return -1;
229
-
230
- if (yIsDistinct && !xIsDistinct)
231
- return 1;
232
- }
233
-
234
- // Neither is a literal, so simply
235
- // return and continue processing
236
- if (!x && !y)
237
- return;
238
-
239
- // We know one of them must be a literal
240
- // when a literal is encountered, the
241
- // sort order is not modified
242
- return 0;
243
- };
244
-
245
- let distinctOrder = distinctSortOrder(a, b);
246
- if (distinctOrder != null)
247
- return distinctOrder;
248
-
249
- let x = (typeof a === 'string') ? a : `${a.modelName}:${a.fieldName}`;
250
- let y = (typeof b === 'string') ? b : `${b.modelName}:${b.fieldName}`;
251
-
252
- if (x === y)
253
- return 0;
117
+ getQueryEngineOrder(queryEngine, _options) {
118
+ let options = _options || {};
119
+ let context = queryEngine.getOperationContext();
120
+ let order = context.order;
254
121
 
255
- return (x < y) ? -1 : 1;
256
- });
122
+ return (order && order.size) ? order : this.connection.getDefaultOrder(context.rootModel, options);
257
123
  }
258
124
 
259
- getProjectionFromQueryEngine(queryEngine, options) {
260
- let queryEngineContextID = queryEngine._getTopContextID();
261
- let cache = this.getOptionsCache(options, `getProjectionFromQueryEngine.${queryEngineContextID}`);
262
- if (cache)
263
- return cache;
264
-
265
- const shouldResetProjection = (fields) => {
266
- if (fields.length === 0)
267
- return false;
268
-
269
- if (fields.indexOf('+') >= 0)
270
- return false;
271
-
272
- for (let i = 0, il = fields.length; i < il; i++) {
273
- let field = fields[i];
274
-
275
- if (LiteralBase.isLiteral(field))
276
- return true;
277
-
278
- if (ModelBase.isModelClass(field))
279
- return true;
280
-
281
- if (!Nife.instanceOf(field, 'string'))
282
- true;
283
-
284
- if (field === '-')
285
- return true;
286
-
287
- if (field === '*')
288
- continue;
289
-
290
- if (!(/^\s*[+-]/).test(field))
291
- return true;
292
- }
293
-
294
- return false;
295
- };
296
-
297
- const collectProjectionValuesFromQuery = (RootModel, query) => {
298
- let projections = [ RootModel ];
299
-
300
- for (let i = 0, il = query.length; i < il; i++) {
301
- let queryPart = query[i];
302
-
303
- if (!Object.prototype.hasOwnProperty.call(queryPart, 'control'))
304
- continue;
305
-
306
- if (queryPart.control !== true)
307
- continue;
308
-
309
- if (queryPart.operator !== 'PROJECT')
310
- continue;
311
-
312
- let fields = queryPart.value;
313
- if (shouldResetProjection(fields)) {
314
- fields = Nife.arrayFlatten(fields);
315
-
316
- let addIndex = fields.indexOf('+');
317
- let newProjections = fields.slice();
318
-
319
- if (addIndex >= 0)
320
- newProjections[addIndex] = Nife.arrayFlatten(projections);
321
-
322
- projections = newProjections;
323
- } else {
324
- projections = projections.concat(fields);
325
- }
326
- }
327
-
328
- return Nife.uniq(Nife.arrayFlatten(projections));
329
- };
330
-
331
- const modelFieldsToProjection = (fieldSet, Model, removeFromProjection) => {
332
- let modelName = Model.getModelName();
333
-
334
- Model.iterateFields(({ field, fieldName }) => {
335
- if (field.type.isVirtual())
336
- return;
337
-
338
- let fullFieldName = `${modelName}:${fieldName}`;
339
-
340
- if (removeFromProjection) {
341
- fieldSet.delete(fullFieldName);
342
- return;
343
- }
344
-
345
- fieldSet.set(fullFieldName, {
346
- projectedName: this.getEscapedProjectionName(Model, field, options),
347
- Model: Model,
348
- Field: field,
349
- direction: '+',
350
- fullFieldName,
351
- fieldName,
352
- modelName,
353
- });
354
- });
355
- };
356
-
357
- const addRequiredFieldsToProjection = (fieldSet) => {
358
- let requiredProjectionFields = this.getProjectionRequiredFields(queryEngine, options);
359
-
360
- for (let [ key, value ] of requiredProjectionFields) {
361
- if (fieldSet.has(key))
362
- continue;
363
-
364
- fieldSet.set(key, value);
365
- }
366
-
367
- return fieldSet;
368
- };
369
-
370
- const finalizeProjection = (_projectedFields) => {
371
- let projectedFields = _projectedFields;
372
- let result;
373
-
374
- let subQueryOperator = (options && options.subQueryOperator);
375
- if (options && options.isSubQuery && !(subQueryOperator === 'EXISTS' || subQueryOperator === 'NOT EXISTS')) {
376
- // If we are sub-selecting then only one
377
- // field in the projection is allowed
378
-
379
- result = Array.from(projectedFields.values());
380
- if (result.length !== 1)
381
- throw new Error(`${this.constructor.name}::getProjectionFromQueryEngine: Only one field is allowed in the projection of a sub-query.`);
382
-
383
- let subQueryField = result[0];
384
-
385
- if (LiteralBase.isLiteral(subQueryField))
386
- result = [ subQueryField ];
387
- else if (typeof subQueryField === 'string')
388
- result = [ subQueryField ];
389
- else
390
- result = [ new Literals.DistinctLiteral(subQueryField.fullFieldName) ];
391
- } else {
392
- projectedFields = addRequiredFieldsToProjection(projectedFields);
393
-
394
- // If distinct specifies a field, then
395
- // make sure that field isn't specified twice
396
- if (hasDistinct) {
397
- let distinctFieldName = hasDistinct.getFullyQualifiedFieldName();
398
- if (distinctFieldName)
399
- projectedFields.delete(distinctFieldName);
400
- }
401
-
402
- result = Array.from(projectedFields.values());
403
-
404
- // Convert projection fields to array and sort
405
- result = this.sortedProjectedFields(result, options);
406
-
407
- // Now prefix the projection fields with the distinct
408
- // literal if one exists on the query
409
- if (hasDistinct)
410
- result = [ hasDistinct ].concat(result);
411
- }
412
-
413
- return result;
414
- };
415
-
416
- let rawQueryContext = queryEngine._getRawQueryContext();
417
- let RootModel = rawQueryContext.rootModel;
418
- if (!RootModel)
419
- throw new Error('QueryGeneratorBase::getProjectionFromQueryEngine: No root model found for query. Root model is required to generate a projection.');
420
-
421
- let hasDistinct = rawQueryContext.distinct;
422
- let projections = collectProjectionValuesFromQuery(RootModel, queryEngine._getRawQuery());
423
- let projectedFields = new Map();
424
- let allModels = this.getAllModelsUsedInQuery(queryEngine, options);
425
- let isAdding = true;
426
-
427
- // If projection is empty, then return
428
- // the projection of the root model
429
- if (Nife.isEmpty(projections)) {
430
- modelFieldsToProjection(projectedFields, RootModel);
431
- return finalizeProjection(projectedFields);
432
- }
433
-
434
- for (let i = 0, il = projections.length; i < il; i++) {
435
- let projectionValue = projections[i];
436
- if (!projectionValue)
437
- continue;
438
-
439
- if (projectionValue === '+') {
440
- isAdding = true;
441
- continue;
442
- }
443
-
444
- if (projectionValue === '-') {
445
- isAdding = false;
446
- continue;
447
- }
448
-
449
- if (projectionValue === '*') {
450
- for (let i = 0, il = allModels.length; i < il; i++) {
451
- let Model = allModels[i];
452
- modelFieldsToProjection(projectedFields, Model);
453
- }
454
-
455
- continue;
456
- }
457
-
458
- if (LiteralBase.isLiteral(projectionValue)) {
459
- // If we already have distinct specified on the query
460
- // then skip any distinct values specified by the user
461
- if (hasDistinct && Literals.DistinctLiteral.isLiteralType(projectionValue))
462
- continue;
463
-
464
- let key = projectionValue.toString(this.connection, options);
465
- if (isAdding)
466
- projectedFields.set(key, projectionValue);
467
- else
468
- projectedFields.delete(key);
469
-
470
- continue;
471
- }
472
-
473
- if (ModelBase.isModelClass(projectionValue)) {
474
- if (allModels.indexOf(projectionValue) >= 0)
475
- modelFieldsToProjection(projectedFields, projectionValue, !isAdding);
476
-
477
- continue;
478
- }
479
-
480
- let { fieldName, direction, hasDirection } = this.getFieldDirectionSpecifier(projectionValue);
481
- if (!fieldName)
482
- continue;
483
-
484
- if (!hasDirection) {
485
- direction = (isAdding) ? '+' : '-';
486
- hasDirection = true;
487
- }
488
-
489
- let def = this.connection.parseQualifiedName(fieldName);
490
- if (!def.modelName) {
491
- if (Nife.isNotEmpty(def.fieldNames) && allModels.length > 1)
492
- throw new Error(`QueryGeneratorBase::getProjectionFromQueryEngine: "${def.fieldNames[0]}" ambiguous. You must use a fully qualified field name for an ORDER clause. Example: "+Model:id".`);
493
-
494
- def.modelName = RootModel.getModelName();
495
- }
496
-
497
- let ProjectionModel = this.connection.getModel(def.modelName);
498
- if (!ProjectionModel) {
499
- if (hasDirection && direction === '-') {
500
- projectedFields.delete(projectionValue);
501
- continue;
502
- }
503
-
504
- // Proceed blindly... as the user may be
505
- // querying something we are unaware of
506
- projectedFields.set(projectionValue, projectionValue);
507
- continue;
508
- }
509
-
510
- if (allModels.indexOf(ProjectionModel) < 0)
511
- continue;
512
-
513
- if (!def.fieldNames.length) {
514
- // If there are no field names, but we have a model name
515
- // then let the projection generator generate all model
516
- // fields
517
-
518
- if (ProjectionModel) {
519
- modelFieldsToProjection(projectedFields, ProjectionModel, (hasDirection && direction === '-'));
520
- continue;
125
+ getProjectedFields(queryEngine, _options, asMap) {
126
+ let options = this.stackAssign(_options || {}, { isProjection: true });
127
+ let context = queryEngine.getOperationContext();
128
+ let queryProjection = new Map(context.projection);
129
+ let order = this.getQueryEngineOrder(queryEngine, options);
130
+ let allProjectionFields = new Map();
131
+ let allModelsUsedInQuery = queryEngine.getAllModelsUsedInQuery();
132
+
133
+ if (!options.isSubQuery && order && order.size) {
134
+ let contextOrderSupport = this.connection.isOrderSupportedInContext(options);
135
+ if (contextOrderSupport) {
136
+ for (let [ fullyQualifiedFieldName, orderScope ] of order) {
137
+ if (!queryProjection.has(fullyQualifiedFieldName))
138
+ queryProjection.set(fullyQualifiedFieldName, orderScope);
521
139
  }
522
140
  }
523
-
524
- let field = this.connection.getField(def.fieldNames[0], def.modelName);
525
- let modelName = (field) ? field.Model.getModelName() : ProjectionModel.getModelName();
526
-
527
- if (!field) {
528
- let projectionFieldName = def.fieldNames[0];
529
- let fullFieldName = `${modelName}:${projectionFieldName}`;
530
-
531
- if (hasDirection && direction === '-') {
532
- projectedFields.delete(fullFieldName);
533
- continue;
534
- }
535
-
536
- // Proceed blindly... as the user may be
537
- // querying something we are unaware of
538
- projectedFields.set(fullFieldName, {
539
- projectedName: this.getEscapedProjectionName(ProjectionModel, fieldName, options),
540
- Model: ProjectionModel,
541
- Field: null,
542
- fieldName: projectionFieldName,
543
- direction: '+',
544
- modelName,
545
- fullFieldName,
546
- });
547
-
548
- continue;
549
- }
550
-
551
- let projectionFieldName = field.fieldName;
552
- let fullFieldName = `${modelName}:${projectionFieldName}`;
553
-
554
- if (hasDirection && direction === '-') {
555
- projectedFields.delete(fullFieldName);
556
- continue;
557
- }
558
-
559
- projectedFields.set(fullFieldName, {
560
- projectedName: this.getEscapedProjectionName(field.Model, field, options),
561
- Model: field.Model,
562
- Field: field,
563
- fieldName: field.fieldName,
564
- fullFieldName,
565
- modelName,
566
- direction,
567
- });
568
141
  }
569
142
 
570
- let result = finalizeProjection(projectedFields);
571
-
572
- if (options)
573
- this.setOptionsCache(options, `getProjectionFromQueryEngine.${queryEngineContextID}`, result);
574
-
575
- return result;
576
- }
577
-
578
- isFieldIdentifier(str) {
579
- return (/^"[^"]+"."[^"]+"|"\w+:[\w.]+"/i).test(str);
580
- }
581
-
582
- getProjectedFields(queryEngine, _options, asMap) {
583
- let options = _options || {};
584
- let queryProjection = this.getProjectionFromQueryEngine(queryEngine, options);
585
- let allProjectionFields = new Map();
143
+ for (let [ fullyQualifiedName, projectedScope ] of queryProjection) {
144
+ let { value } = projectedScope;
586
145
 
587
- for (let i = 0, il = queryProjection.length; i < il; i++) {
588
- let projectionField = queryProjection[i];
589
- if (!projectionField)
146
+ if (Nife.instanceOf(value, 'string')) {
147
+ // Raw string is treated as a literal
148
+ allProjectionFields.set(fullyQualifiedName, value);
590
149
  continue;
591
-
592
- if (LiteralBase.isLiteral(projectionField)) {
593
- let result = projectionField.toString(this.connection, options);
594
- let fullFieldName;
595
-
596
- if (typeof projectionField.getFullyQualifiedFieldName === 'function')
597
- fullFieldName = projectionField.getFullyQualifiedFieldName();
598
- else
599
- fullFieldName = this.parseFieldProjection(result);
600
-
601
- if (!fullFieldName)
602
- fullFieldName = result;
603
-
604
- if (fullFieldName && result)
605
- allProjectionFields.set(fullFieldName, result);
606
- else
607
- allProjectionFields.set(result, result);
150
+ } else if (LiteralBase.isLiteral(value)) {
151
+ let result = value.toString(this.connection, options);
152
+ allProjectionFields.set(result || fullyQualifiedName, result || fullyQualifiedName);
608
153
 
609
154
  continue;
610
155
  }
611
156
 
612
- if (projectionField.direction === '-')
157
+ if (allModelsUsedInQuery.indexOf(value.Model) < 0)
613
158
  continue;
614
159
 
615
- if (Nife.instanceOf(projectionField, 'string')) {
616
- allProjectionFields.set(projectionField, projectionField);
617
- continue;
618
- }
619
-
620
- let projectedName = projectionField.projectedName;
621
- allProjectionFields.set(projectionField.fullFieldName, projectedName);
160
+ let escapedFieldName = this.getEscapedProjectionName(value.Model, value, options);
161
+ allProjectionFields.set(`${value.Model.getModelName()}:${value.fieldName}`, escapedFieldName);
622
162
  }
623
163
 
624
164
  if (asMap === true)
@@ -705,90 +245,19 @@ class QueryGeneratorBase {
705
245
  };
706
246
  }
707
247
 
708
- // eslint-disable-next-line no-unused-vars
709
- getOrderLimitOffset(queryEngine, options) {
710
- let queryEngineContextID = queryEngine._getTopContextID();
711
- let cache = this.getOptionsCache(options, `getOrderLimitOffset.${queryEngineContextID}`);
712
- if (cache)
713
- return cache;
714
-
715
- let query = queryEngine._getRawQuery();
716
- let rootModel = queryEngine._getRawQueryContext().rootModel;
717
- let limit;
718
- let offset;
719
- let order;
720
-
721
- for (let i = 0, il = query.length; i < il; i++) {
722
- let queryPart = query[i];
723
-
724
- if (!Object.prototype.hasOwnProperty.call(queryPart, 'control'))
725
- continue;
726
-
727
- if (queryPart.control !== true)
728
- continue;
729
-
730
- let queryOperator = queryPart.operator;
731
-
732
- if (queryOperator === 'LIMIT')
733
- limit = queryPart.value;
734
- else if (queryOperator === 'OFFSET')
735
- offset = queryPart.value;
736
- else if (queryOperator === 'ORDER')
737
- order = queryPart.value;
738
- }
739
-
740
- if (order === undefined) {
741
- if (options && options.selectStatement === true)
742
- order = this.connection.getDefaultOrder(rootModel, options);
743
- }
744
-
745
- if (Nife.isNotEmpty(order) && !LiteralBase.isLiteral(order)) {
746
- let allModels = this.getAllModelsUsedInQuery(queryEngine, options);
747
-
748
- order = order.map((_fieldName) => {
749
- if (LiteralBase.isLiteral(_fieldName))
750
- return _fieldName;
751
-
752
- let { fieldName, direction } = this.getFieldDirectionSpecifier(_fieldName);
753
- if (!fieldName)
754
- return;
755
-
756
- let def = this.connection.parseQualifiedName(fieldName);
757
- if (!def.modelName) {
758
- if (allModels.length > 1)
759
- throw new Error(`QueryGeneratorBase::getOrderLimitOffset: "${fieldName}" ambiguous. You must use a fully qualified field name for an ORDER clause. Example: "+Model:id".`);
760
-
761
- def.modelName = allModels[0].getModelName();
762
- }
763
-
764
- if (!def.fieldNames.length)
765
- throw new Error(`QueryGeneratorBase::getOrderLimitOffset: No field names found for "${fieldName}".`);
766
-
767
- let field = this.connection.getField(def.fieldNames[0], def.modelName);
768
- if (!field)
769
- throw new Error(`QueryGeneratorBase::getOrderLimitOffset: Unable to locate field "${def.modelName}"."${def.fieldNames[0]}".`);
770
-
771
- return {
772
- Model: field.Model,
773
- Field: field,
774
- direction,
775
- };
776
- });
777
- }
778
-
779
- let orderLimitOffset = { limit, order, offset };
780
-
781
- if (options)
782
- this.setOptionsCache(options, `getOrderLimitOffset.${queryEngineContextID}`, orderLimitOffset);
248
+ getQuerySliceFromQueryPart(queryPart) {
249
+ let operationStack = queryPart.operationStack;
250
+ let index = operationStack.indexOf(queryPart);
783
251
 
784
- return orderLimitOffset;
252
+ return operationStack.slice(index);
785
253
  }
786
254
 
787
- getQuerySliceFromQueryPart(queryPart) {
788
- let queryRoot = queryPart.queryRoot;
789
- let index = queryRoot.indexOf(queryPart);
255
+ _getLiteralAlias(literal, options) {
256
+ let as = (literal.options && literal.options.as) || (options && options.as);
257
+ if (Nife.isEmpty(as))
258
+ return '';
790
259
 
791
- return queryRoot.slice(index);
260
+ return ` AS ${this.escapeID(as)}`;
792
261
  }
793
262
 
794
263
  _averageLiteralToString(literal, options) {
@@ -803,7 +272,7 @@ class QueryGeneratorBase {
803
272
  else
804
273
  escapedFieldName = this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options));
805
274
 
806
- return `AVG(${escapedFieldName})`;
275
+ return `AVG(${escapedFieldName})${this._getLiteralAlias(literal, options)}`;
807
276
  }
808
277
 
809
278
  _countLiteralToString(literal, options) {
@@ -822,7 +291,7 @@ class QueryGeneratorBase {
822
291
  escapedFieldName = '*';
823
292
  }
824
293
 
825
- return `COUNT(${escapedFieldName})`;
294
+ return `COUNT(${escapedFieldName})${this._getLiteralAlias(literal, options)}`;
826
295
  }
827
296
 
828
297
  _distinctLiteralToString(literal, options) {
@@ -830,10 +299,13 @@ class QueryGeneratorBase {
830
299
  return;
831
300
 
832
301
  let field = literal.getField(this.connection);
302
+ if (!field)
303
+ return 'DISTINCT';
304
+
833
305
  if (LiteralBase.isLiteral(field))
834
- return `DISTINCT ${field.toString(this.connection, options)}`;
306
+ return `DISTINCT ON(${field.toString(this.connection, this.stackAssign(options, { noProjectionAliases: true }))})`;
835
307
 
836
- return `DISTINCT ${this.getEscapedProjectionName(field.Model, field, this.stackAssign(options, literal.options))}`;
308
+ return `DISTINCT ON(${this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options, { noProjectionAliases: true }))})`;
837
309
  }
838
310
 
839
311
  _fieldLiteralToString(literal, options) {
@@ -844,7 +316,7 @@ class QueryGeneratorBase {
844
316
  if (LiteralBase.isLiteral(field))
845
317
  return field.toString(this.connection, options);
846
318
 
847
- return this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options));
319
+ return this.getEscapedProjectionName(field.Model, field, this.stackAssign(options, { noProjectionAliases: (options && !options.isProjection) }, literal.options));
848
320
  }
849
321
 
850
322
  _maxLiteralToString(literal, options) {
@@ -859,7 +331,7 @@ class QueryGeneratorBase {
859
331
  else
860
332
  escapedFieldName = this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options));
861
333
 
862
- return `MAX(${escapedFieldName})`;
334
+ return `MAX(${escapedFieldName})${this._getLiteralAlias(literal, options)}`;
863
335
  }
864
336
 
865
337
  _minLiteralToString(literal, options) {
@@ -874,7 +346,7 @@ class QueryGeneratorBase {
874
346
  else
875
347
  escapedFieldName = this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options));
876
348
 
877
- return `MIN(${escapedFieldName})`;
349
+ return `MIN(${escapedFieldName})${this._getLiteralAlias(literal, options)}`;
878
350
  }
879
351
 
880
352
  _sumLiteralToString(literal, options) {
@@ -889,7 +361,7 @@ class QueryGeneratorBase {
889
361
  else
890
362
  escapedFieldName = this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options));
891
363
 
892
- return `SUM(${escapedFieldName})`;
364
+ return `SUM(${escapedFieldName})${this._getLiteralAlias(literal, options)}`;
893
365
  }
894
366
 
895
367
  // eslint-disable-next-line no-unused-vars