mythix-orm-sql-base 1.4.4 → 1.4.7

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.
@@ -65,14 +65,6 @@ class SQLConnectionBase extends ConnectionBase {
65
65
  }
66
66
  }
67
67
 
68
- databaseSupportsLimitInSubQuery() {
69
- return false;
70
- }
71
-
72
- databaseSupportsOrderInSubQuery() {
73
- return false;
74
- }
75
-
76
68
  prepareArrayValuesForSQL(_array) {
77
69
  let array = Nife.arrayFlatten(_array);
78
70
 
@@ -869,6 +861,7 @@ class SQLConnectionBase extends ConnectionBase {
869
861
  return (count > 0);
870
862
  }
871
863
 
864
+ // eslint-disable-next-line no-unused-vars
872
865
  async enableForeignKeyConstraints(enable) {
873
866
  throw new Error(`${this.constructor.name}::enableForeignKeyConstraints: This operation is not supported for this connection type.`);
874
867
  }
@@ -222,12 +222,11 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
222
222
  value = arrayValues;
223
223
  }
224
224
 
225
- let escapedTableName = this.escapeID(this.getTableNameFromQueryPart(queryPart));
226
- let escapedColumnName = this.escapeID(field.columnName);
225
+ let escapedColumnName = this.getEscapedColumnName(field.Model, field, options);
227
226
  let sqlOperator = this.generateSelectQueryOperatorFromQueryEngineOperator(queryPart, operator, value, false, options);
228
227
 
229
228
  if (QueryEngine.isQuery(value)) {
230
- if (!this.queryHasConditions(value._getRawQuery()))
229
+ if (!value._queryHasConditions())
231
230
  return '';
232
231
 
233
232
  if (sqlOperator === '=')
@@ -235,7 +234,7 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
235
234
  else if (sqlOperator === '!=')
236
235
  sqlOperator = 'NOT IN';
237
236
 
238
- return `${escapedTableName}.${escapedColumnName} ${sqlOperator} (${this.generateSelectStatement(value, this.stackAssign(options, { isSubQuery: true }))})`;
237
+ return `${escapedColumnName} ${sqlOperator} (${this.generateSelectStatement(value, this.stackAssign(options, { isSubQuery: true, subQueryOperator: sqlOperator }))})`;
239
238
  }
240
239
 
241
240
  let context = { queryPart, field, sqlOperator, operator, value };
@@ -244,7 +243,7 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
244
243
 
245
244
  let conditionPostfix = this.generateConditionPostfix(context);
246
245
 
247
- return `${escapedTableName}.${escapedColumnName} ${sqlOperator} ${this.escape(field, value)}${(conditionPostfix) ? ` ${conditionPostfix}` : ''}`;
246
+ return `${escapedColumnName} ${sqlOperator} ${this.escape(field, value)}${(conditionPostfix) ? ` ${conditionPostfix}` : ''}`;
248
247
  }
249
248
 
250
249
  // eslint-disable-next-line no-unused-vars
@@ -252,18 +251,16 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
252
251
  if (!Model)
253
252
  throw new Error(`${this.constructor.name}::generateFromTableOrTableJoin: No valid model provided.`);
254
253
 
255
- let escapedTableName = this.escapeID(Model.getTableName(this.connection));
254
+ let escapedTableName = this.getEscapedTableName(Model, options);
256
255
  return (joinType) ? `${joinType} ${escapedTableName}` : `FROM ${escapedTableName}`;
257
256
  }
258
257
 
259
258
  generateSelectJoinOnTableQueryCondition(leftQueryPart, rightQueryPart, leftField, rightField, operator, options) {
260
- let leftSideEscapedTableName = this.escapeID(this.getTableNameFromQueryPart(leftQueryPart));
261
- let leftSideEscapedColumnName = this.escapeID(leftField.columnName);
262
- let rightSideEscapedTableName = this.escapeID(this.getTableNameFromQueryPart(rightQueryPart));
263
- let rightSideEscapedColumnName = this.escapeID(rightField.columnName);
259
+ let leftSideEscapedColumnName = this.getEscapedColumnName(leftField.Model, leftField, options);
260
+ let rightSideEscapedColumnName = this.getEscapedColumnName(rightField.Model, rightField, options);
264
261
  let sqlOperator = this.generateSelectQueryOperatorFromQueryEngineOperator(leftQueryPart, operator, undefined, true, options);
265
262
 
266
- return `${leftSideEscapedTableName}.${leftSideEscapedColumnName} ${sqlOperator} ${rightSideEscapedTableName}.${rightSideEscapedColumnName}`;
263
+ return `${leftSideEscapedColumnName} ${sqlOperator} ${rightSideEscapedColumnName}`;
267
264
  }
268
265
 
269
266
  generateJoinOnTableQueryConditions(joinInfos, options) {
@@ -450,11 +447,6 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
450
447
  return sqlParts.join(' ');
451
448
  }
452
449
 
453
- // eslint-disable-next-line no-unused-vars
454
- allowOrderFieldWhenNotProjected(orderField, options) {
455
- return true;
456
- }
457
-
458
450
  // eslint-disable-next-line no-unused-vars
459
451
  generateOrderClause(_orders, _options) {
460
452
  if (LiteralBase.isLiteral(_orders))
@@ -464,13 +456,21 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
464
456
  if (Nife.isEmpty(orders))
465
457
  return '';
466
458
 
467
- let options = _options || {};
459
+ let options = _options || {};
460
+ let contextOrderSupport = this.connection.isOrderSupportedInContext(options);
461
+ if (contextOrderSupport === false)
462
+ return '';
463
+
468
464
  let orderByParts = [];
469
465
  for (let i = 0, il = orders.length; i < il; i++) {
470
466
  let orderField = orders[i];
471
467
 
472
468
  if (LiteralBase.isLiteral(orderField)) {
473
- orderByParts.push(orderField.toString(this.connection));
469
+ let fieldStr = orderField.toString(this.connection);
470
+ if (!options.projectionFields.has(fieldStr) && contextOrderSupport === 'PROJECTION_ONLY')
471
+ continue;
472
+
473
+ orderByParts.push(fieldStr);
474
474
  continue;
475
475
  }
476
476
 
@@ -480,12 +480,11 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
480
480
  let fieldName = orderField.Field.fieldName;
481
481
  let fqFieldName = `${modelName}:${fieldName}`;
482
482
 
483
- if (!options.projectionFields.has(fqFieldName) && !this.allowOrderFieldWhenNotProjected(orderField, options))
483
+ if (!options.projectionFields.has(fqFieldName) && contextOrderSupport === 'PROJECTION_ONLY')
484
484
  continue;
485
485
  }
486
486
 
487
- let escapedTableName = this.escapeID(orderField.Model.getTableName(this.connection));
488
- let escapedColumnName = this.escapeID(orderField.Field.columnName);
487
+ let escapedColumnName = this.getEscapedColumnName(orderField.Model, orderField.Field.columnName, options);
489
488
  let orderStr;
490
489
 
491
490
  if (options.reverseOrder !== true)
@@ -493,7 +492,7 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
493
492
  else
494
493
  orderStr = (orderField.direction === '-') ? 'ASC' : 'DESC';
495
494
 
496
- orderByParts.push(`${escapedTableName}.${escapedColumnName} ${orderStr}`);
495
+ orderByParts.push(`${escapedColumnName} ${orderStr}`);
497
496
  }
498
497
 
499
498
  if (Nife.isEmpty(orderByParts))
@@ -518,21 +517,25 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
518
517
  return `OFFSET ${offset}`;
519
518
  }
520
519
 
521
- generateSelectOrderLimitOffset(queryEngine, options) {
520
+ generateSelectOrderLimitOffset(queryEngine, _options) {
521
+ let options = _options || {};
522
522
  let {
523
523
  order,
524
524
  limit,
525
525
  offset,
526
526
  } = this.getOrderLimitOffset(queryEngine, options);
527
527
  let sqlParts = [];
528
+ let hasOrder = false;
528
529
 
529
530
  if (Nife.isNotEmpty(order)) {
530
- if (!(options && options.isSubQuery) || (options && options.isSubQuery && this.connection.databaseSupportsOrderInSubQuery())) {
531
+ if (this.connection.isOrderSupportedInContext(options)) {
531
532
  let result = this.generateOrderClause(order, options);
532
- if (result)
533
+ if (result) {
534
+ hasOrder = true;
533
535
  sqlParts.push(result);
536
+ }
534
537
 
535
- if (!(Nife.instanceOf(limit, 'number') && isFinite(limit)) && options && options.forceLimit) {
538
+ if (hasOrder && !(Nife.instanceOf(limit, 'number') && isFinite(limit)) && options && options.forceLimit) {
536
539
  limit = options.forceLimit;
537
540
  offset = 0;
538
541
  }
@@ -540,7 +543,7 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
540
543
  }
541
544
 
542
545
  if (!Object.is(limit, Infinity) && Nife.isNotEmpty(limit)) {
543
- if (!(options && options.isSubQuery) || (options && options.isSubQuery && this.connection.databaseSupportsLimitInSubQuery())) {
546
+ if (this.connection.isLimitSupportedInContext(options)) {
544
547
  let result = this.generateLimitClause(limit, options);
545
548
  if (result)
546
549
  sqlParts.push(result);
@@ -548,7 +551,7 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
548
551
  }
549
552
 
550
553
  if (Nife.isNotEmpty(offset)) {
551
- if (!(options && options.isSubQuery) || (options && options.isSubQuery && this.connection.databaseSupportsLimitInSubQuery())) {
554
+ if (this.connection.isLimitSupportedInContext(options)) {
552
555
  let result = this.generateOffsetClause(offset, options);
553
556
  if (result)
554
557
  sqlParts.push(result);
@@ -658,8 +661,10 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
658
661
 
659
662
  // eslint-disable-next-line no-unused-vars
660
663
  generateIndexName(Model, field, index, options) {
661
- let tableName = Model.getTableName(this.connection);
664
+ if (!index)
665
+ return '';
662
666
 
667
+ let tableName = Model.getTableName(this.connection);
663
668
  if (index === true)
664
669
  return this.escapeID(`idx_${tableName}_${field.columnName}`.replace(/\W+/g, '_'));
665
670
 
@@ -673,13 +678,16 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
673
678
  fieldNames.push(indexField.columnName);
674
679
  }
675
680
 
676
- return this.escapeID(`idx_${tableName}_${fieldNames.join('_')}`);
681
+ return this.escapeID(`idx_${tableName}_${fieldNames.sort().join('_')}`);
677
682
  }
678
683
 
679
- generateColumnIndex(Model, field, index, _options) {
684
+ generateColumnIndexes(Model, field, _indexes, _options) {
685
+ let indexes = Nife.toArray(_indexes).filter(Boolean);
686
+ if (Nife.isEmpty(indexes))
687
+ return [];
688
+
680
689
  let options = _options || {};
681
- let escapedTableName = this.escapeID(Model.getTableName(this.connection));
682
- let indexName = this.generateIndexName(Model, field, index, options);
690
+ let escapedTableName = this.getEscapedTableName(Model, options);
683
691
  let flags = [];
684
692
 
685
693
  if (options.concurrently)
@@ -690,25 +698,26 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
690
698
 
691
699
  flags = flags.join(' ');
692
700
 
693
- if (index === true)
694
- return `CREATE INDEX ${flags} ${indexName} ON ${escapedTableName} (${this.escapeID(field.columnName)});`;
701
+ return indexes.map((indexNames) => {
702
+ let indexName = this.generateIndexName(Model, field, indexNames, options);
703
+ if (indexNames === true)
704
+ return `CREATE INDEX ${flags} ${indexName} ON ${escapedTableName} (${this.escapeID(field.columnName)});`;
695
705
 
696
- let fieldNames = [];
697
- for (let i = 0, il = index.length; i < il; i++) {
698
- let indexFieldName = index[i];
699
- let indexField = Model.getField(indexFieldName);
700
- if (!indexField)
701
- throw new Error(`${this.constructor.name}::generateColumnIndex: Unable to find field named "${indexFieldName}".`);
706
+ let escapedColumnNames = [field.fieldName].concat(indexNames).filter(Boolean).map((fieldName) => {
707
+ let thisField = Model.getField(fieldName);
708
+ if (!thisField)
709
+ throw new Error(`${this.constructor.name}::generateColumnIndexes: Unable to find field named "${fieldName}".`);
702
710
 
703
- fieldNames.push(this.escapeID(indexField.columnName));
704
- }
711
+ return this.getEscapedColumnName(Model, thisField, options);
712
+ });
705
713
 
706
- return `CREATE INDEX ${indexName} ON ${escapedTableName} (${fieldNames.join(',')});`;
714
+ return `CREATE INDEX ${indexName} ON ${escapedTableName} (${escapedColumnNames.join(',')});`;
715
+ });
707
716
  }
708
717
 
709
718
  generateDropTableStatement(Model, _options) {
710
719
  let options = _options || {};
711
- let escapedTableName = this.escapeID(Model.getTableName(this.connection));
720
+ let escapedTableName = this.getEscapedTableName(Model, options);
712
721
  let flags = [];
713
722
 
714
723
  if (options.ifExists)
@@ -719,34 +728,34 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
719
728
  return `DROP TABLE ${flags} ${escapedTableName}${(options.cascade === true) ? ' CASCADE' : ''}`;
720
729
  }
721
730
 
722
- generateForeignKeyConstraint(field, type) {
723
- let options = type.getOptions();
731
+ generateForeignKeyConstraint(field, type, options) {
732
+ let typeOptions = type.getOptions();
724
733
  let targetModel = type.getTargetModel(this.connection);
725
734
  let targetField = type.getTargetField(this.connection);
726
735
 
727
736
  let sqlParts = [
728
737
  'FOREIGN KEY(',
729
- this.escapeID(field.columnName),
738
+ this.getEscapedColumnName(field.Model, field, { ...(options || {}), columnNameOnly: true }),
730
739
  ') REFERENCES ',
731
- this.escapeID(targetModel.getTableName(this.connection)),
740
+ this.getEscapedTableName(targetModel, options),
732
741
  '(',
733
- this.escapeID(targetField.columnName),
742
+ this.getEscapedColumnName(targetModel, targetField, { ...(options || {}), columnNameOnly: true }),
734
743
  ')',
735
744
  ];
736
745
 
737
- if (options.deferred === true) {
746
+ if (typeOptions.deferred === true) {
738
747
  sqlParts.push(' ');
739
748
  sqlParts.push('DEFERRABLE INITIALLY DEFERRED');
740
749
  }
741
750
 
742
- if (options.onDelete) {
751
+ if (typeOptions.onDelete) {
743
752
  sqlParts.push(' ');
744
- sqlParts.push(`ON DELETE ${options.onDelete.toUpperCase()}`);
753
+ sqlParts.push(`ON DELETE ${typeOptions.onDelete.toUpperCase()}`);
745
754
  }
746
755
 
747
- if (options.onUpdate) {
756
+ if (typeOptions.onUpdate) {
748
757
  sqlParts.push(' ');
749
- sqlParts.push(`ON UPDATE ${options.onUpdate.toUpperCase()}`);
758
+ sqlParts.push(`ON UPDATE ${typeOptions.onUpdate.toUpperCase()}`);
750
759
  }
751
760
 
752
761
  return sqlParts.join('');
@@ -790,9 +799,9 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
790
799
  let indexes = Nife.toArray(field.index).filter(Boolean);
791
800
  for (let i = 0, il = indexes.length; i < il; i++) {
792
801
  let index = indexes[i];
793
- let result = this.generateColumnIndex(Model, field, index, options);
794
- if (result)
795
- fieldParts.push(result);
802
+ let result = this.generateColumnIndexes(Model, field, index, options);
803
+ if (Nife.isNotEmpty(result))
804
+ fieldParts.push(result.join('\n\n'));
796
805
  }
797
806
  });
798
807
 
@@ -807,10 +816,8 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
807
816
  if (field.type.isVirtual())
808
817
  return;
809
818
 
810
- let columnName = field.columnName || fieldName;
811
819
  let constraintParts = [];
812
-
813
- let defaultValue = this.getFieldDefaultValue(field, fieldName, { remoteOnly: true });
820
+ let defaultValue = this.getFieldDefaultValue(field, fieldName, { remoteOnly: true });
814
821
 
815
822
  if (field.primaryKey) {
816
823
  if (LiteralBase.isLiteral(field.primaryKey))
@@ -839,7 +846,7 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
839
846
  if (Nife.isNotEmpty(constraintParts))
840
847
  constraintParts = ` ${constraintParts}`;
841
848
 
842
- fieldParts.push(` ${this.escapeID(columnName)} ${field.type.toConnectionType(this.connection, { createTable: true, defaultValue })}${constraintParts}`);
849
+ fieldParts.push(` ${this.getEscapedColumnName(Model, field, { columnNameOnly: true })} ${field.type.toConnectionType(this.connection, { createTable: true, defaultValue })}${constraintParts}`);
843
850
  });
844
851
 
845
852
  let ifNotExists = '';
@@ -850,7 +857,7 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
850
857
  if (Nife.isNotEmpty(trailingParts))
851
858
  fieldParts = fieldParts.concat(trailingParts.map((part) => ` ${part.trim()}`));
852
859
 
853
- let finalStatement = `CREATE TABLE ${ifNotExists}${this.escapeID(Model.getTableName(this.connection))} (${fieldParts.join(',\n')}\n);`;
860
+ let finalStatement = `CREATE TABLE ${ifNotExists}${this.getEscapedTableName(Model)} (${fieldParts.join(',\n')}\n);`;
854
861
  return finalStatement;
855
862
  }
856
863
 
@@ -949,7 +956,7 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
949
956
  if (!values)
950
957
  return '';
951
958
 
952
- let escapedTableName = this.escapeID(Model.getTableName(this.connection));
959
+ let escapedTableName = this.getEscapedTableName(Model, subOptions);
953
960
  let escapedFieldNames = Array.from(Object.values(this.getEscapedModelFields(Model, subOptions)));
954
961
 
955
962
  let insertStatementTail = this.generateInsertStatementTail(
@@ -999,7 +1006,7 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
999
1006
  if (Nife.isEmpty(dirtyFields))
1000
1007
  return '';
1001
1008
 
1002
- let escapedTableName = this.escapeID(Model.getTableName(this.connection));
1009
+ let escapedTableName = this.getEscapedTableName(Model, options);
1003
1010
  let sqlParts = [ 'UPDATE ', escapedTableName, ' SET ' ];
1004
1011
  let setParts = [];
1005
1012
  let tabs = '';
@@ -1012,7 +1019,7 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1012
1019
  for (let i = 0, il = dirtyFields.length; i < il; i++) {
1013
1020
  let dirtyField = dirtyFields[i];
1014
1021
  let fieldValue = modelChanges[dirtyField.fieldName].current;
1015
- let escapedColumnName = this.escapeID(dirtyField.columnName);
1022
+ let escapedColumnName = this.getEscapedColumnName(dirtyField.Model, dirtyField.columnName, { columnNameOnly: true });
1016
1023
  let escapedValue = (LiteralBase.isLiteral(fieldValue)) ? fieldValue.toString(this.connection) : this.escape(dirtyField, fieldValue);
1017
1024
  if (!escapedValue)
1018
1025
  continue;
@@ -1072,21 +1079,34 @@ class SQLQueryGeneratorBase extends QueryGeneratorBase {
1072
1079
  }
1073
1080
  }
1074
1081
 
1075
- let escapedTableName = this.escapeID(Model.getTableName(this.connection));
1076
- if (queryEngine) {
1077
- let pkField = Model.getPrimaryKeyField();
1078
- let where = this.generateWhereAndOrderLimitOffset(queryEngine, options);
1079
-
1080
- if (where && pkField) {
1081
- if (pkField)
1082
- queryEngine = queryEngine.PROJECT(`${Model.getModelName()}:${pkField.fieldName}`);
1083
-
1084
- let innerSelect = this.generateSelectStatement(queryEngine, this.stackAssign(options, { isSubQuery: true, noProjectionAliases: true }));
1085
- let orderLimitOffset = this.generateSelectOrderLimitOffset(queryEngine, this.stackAssign(options, { forceLimit: 4294967294 }));
1086
- let escapedColumnName = this.getEscapedColumnName(Model, pkField, options);
1087
-
1088
- return `DELETE FROM ${escapedTableName} WHERE ${escapedColumnName} IN (${innerSelect})${(orderLimitOffset) ? ` ${orderLimitOffset}` : ''}`;
1082
+ let escapedTableName = this.getEscapedTableName(Model, options);
1083
+ if (queryEngine && queryEngine._queryHasConditions()) {
1084
+ if (queryEngine._queryHasJoins()) {
1085
+ let pkField = Model.getPrimaryKeyField();
1086
+ if (!pkField)
1087
+ throw new Error(`${this.constructor.name}::generateDeleteStatement: Can not delete using table joins on a table with no primary key field.`);
1088
+
1089
+ let escapedTableNameAlias = this.getEscapedTableName(Model, { tableNamePrefix: '_' });
1090
+ let escapedFieldAlias = this.getEscapedColumnName(Model, pkField, { columnNameOnly: true });
1091
+ let innerSelect = this.generateSelectStatement(
1092
+ queryEngine
1093
+ .AND[pkField.fieldName]
1094
+ .EQ(new Literals.Literal(`${escapedTableNameAlias}.${escapedFieldAlias}`))
1095
+ .PROJECT(new Literals.Literal('1')),
1096
+ this.stackAssign(
1097
+ options,
1098
+ {
1099
+ isSubQuery: true,
1100
+ subQueryOperator: 'EXISTS',
1101
+ noProjectionAliases: true,
1102
+ forceLimit: 4294967295,
1103
+ },
1104
+ ),
1105
+ );
1106
+
1107
+ return `DELETE FROM ${escapedTableName} AS ${escapedTableNameAlias} WHERE EXISTS (${innerSelect})`;
1089
1108
  } else {
1109
+ let where = this.generateWhereAndOrderLimitOffset(queryEngine, { ...options, forceLimit: 4294967295 });
1090
1110
  return `DELETE FROM ${escapedTableName}${(where) ? ` ${where}` : ''}`;
1091
1111
  }
1092
1112
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mythix-orm-sql-base",
3
- "version": "1.4.4",
3
+ "version": "1.4.7",
4
4
  "description": "SQL base support for Mythix ORM",
5
5
  "main": "lib/index.js",
6
6
  "type": "commonjs",
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "homepage": "https://github.com/th317erd/mythix-orm-sql-base#readme",
35
35
  "peerDependencies": {
36
- "mythix-orm": "^1.5.5"
36
+ "mythix-orm": "^1.5.6"
37
37
  },
38
38
  "dependencies": {
39
39
  "nife": "^1.11.3",