typeorm 0.3.25-dev.5003aaa → 0.3.25-dev.63a3b9a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +9 -9
  2. package/browser/driver/oracle/OracleDriver.js +1 -1
  3. package/browser/driver/oracle/OracleDriver.js.map +1 -1
  4. package/browser/driver/postgres/PostgresDriver.js +2 -1
  5. package/browser/driver/postgres/PostgresDriver.js.map +1 -1
  6. package/browser/driver/postgres/PostgresQueryRunner.js +8 -0
  7. package/browser/driver/postgres/PostgresQueryRunner.js.map +1 -1
  8. package/browser/driver/sap/SapDriver.js +1 -1
  9. package/browser/driver/sap/SapDriver.js.map +1 -1
  10. package/browser/driver/spanner/SpannerConnectionCredentialsOptions.d.ts +13 -0
  11. package/browser/driver/spanner/SpannerConnectionCredentialsOptions.js.map +1 -1
  12. package/browser/driver/spanner/SpannerDriver.js +7 -0
  13. package/browser/driver/spanner/SpannerDriver.js.map +1 -1
  14. package/browser/driver/sqlserver/SqlServerDriver.js +1 -1
  15. package/browser/driver/sqlserver/SqlServerDriver.js.map +1 -1
  16. package/browser/driver/types/UpsertType.d.ts +1 -1
  17. package/browser/driver/types/UpsertType.js.map +1 -1
  18. package/browser/entity-manager/EntityManager.d.ts +1 -1
  19. package/browser/entity-manager/EntityManager.js +1 -1
  20. package/browser/entity-manager/EntityManager.js.map +1 -1
  21. package/browser/error/InitializedRelationError.d.ts +1 -1
  22. package/browser/error/InitializedRelationError.js +1 -1
  23. package/browser/error/InitializedRelationError.js.map +1 -1
  24. package/browser/metadata/ColumnMetadata.d.ts +2 -2
  25. package/browser/metadata/ColumnMetadata.js +1 -1
  26. package/browser/metadata/ColumnMetadata.js.map +1 -1
  27. package/browser/metadata-builder/ClosureJunctionEntityMetadataBuilder.js +14 -0
  28. package/browser/metadata-builder/ClosureJunctionEntityMetadataBuilder.js.map +1 -1
  29. package/browser/metadata-builder/RelationJoinColumnBuilder.js +7 -0
  30. package/browser/metadata-builder/RelationJoinColumnBuilder.js.map +1 -1
  31. package/browser/query-builder/InsertOrUpdateOptions.d.ts +6 -0
  32. package/browser/query-builder/InsertOrUpdateOptions.js.map +1 -1
  33. package/browser/query-builder/InsertQueryBuilder.d.ts +17 -0
  34. package/browser/query-builder/InsertQueryBuilder.js +483 -156
  35. package/browser/query-builder/InsertQueryBuilder.js.map +1 -1
  36. package/browser/query-builder/QueryExpressionMap.d.ts +1 -0
  37. package/browser/query-builder/QueryExpressionMap.js.map +1 -1
  38. package/browser/query-builder/SelectQueryBuilder.js +21 -18
  39. package/browser/query-builder/SelectQueryBuilder.js.map +1 -1
  40. package/browser/util/OrmUtils.d.ts +4 -0
  41. package/browser/util/OrmUtils.js +9 -0
  42. package/browser/util/OrmUtils.js.map +1 -1
  43. package/driver/oracle/OracleDriver.js +1 -1
  44. package/driver/oracle/OracleDriver.js.map +1 -1
  45. package/driver/postgres/PostgresDriver.js +2 -1
  46. package/driver/postgres/PostgresDriver.js.map +1 -1
  47. package/driver/postgres/PostgresQueryRunner.js +8 -0
  48. package/driver/postgres/PostgresQueryRunner.js.map +1 -1
  49. package/driver/sap/SapDriver.js +1 -1
  50. package/driver/sap/SapDriver.js.map +1 -1
  51. package/driver/spanner/SpannerConnectionCredentialsOptions.d.ts +13 -0
  52. package/driver/spanner/SpannerConnectionCredentialsOptions.js.map +1 -1
  53. package/driver/spanner/SpannerDriver.js +7 -0
  54. package/driver/spanner/SpannerDriver.js.map +1 -1
  55. package/driver/sqlserver/SqlServerDriver.js +1 -1
  56. package/driver/sqlserver/SqlServerDriver.js.map +1 -1
  57. package/driver/types/UpsertType.d.ts +1 -1
  58. package/driver/types/UpsertType.js.map +1 -1
  59. package/entity-manager/EntityManager.d.ts +1 -1
  60. package/entity-manager/EntityManager.js +1 -1
  61. package/entity-manager/EntityManager.js.map +1 -1
  62. package/error/InitializedRelationError.d.ts +1 -1
  63. package/error/InitializedRelationError.js +1 -1
  64. package/error/InitializedRelationError.js.map +1 -1
  65. package/metadata/ColumnMetadata.d.ts +2 -2
  66. package/metadata/ColumnMetadata.js +1 -1
  67. package/metadata/ColumnMetadata.js.map +1 -1
  68. package/metadata-builder/ClosureJunctionEntityMetadataBuilder.js +14 -0
  69. package/metadata-builder/ClosureJunctionEntityMetadataBuilder.js.map +1 -1
  70. package/metadata-builder/RelationJoinColumnBuilder.js +7 -0
  71. package/metadata-builder/RelationJoinColumnBuilder.js.map +1 -1
  72. package/package.json +1 -1
  73. package/query-builder/InsertOrUpdateOptions.d.ts +6 -0
  74. package/query-builder/InsertOrUpdateOptions.js.map +1 -1
  75. package/query-builder/InsertQueryBuilder.d.ts +17 -0
  76. package/query-builder/InsertQueryBuilder.js +483 -156
  77. package/query-builder/InsertQueryBuilder.js.map +1 -1
  78. package/query-builder/QueryExpressionMap.d.ts +1 -0
  79. package/query-builder/QueryExpressionMap.js.map +1 -1
  80. package/query-builder/SelectQueryBuilder.js +21 -18
  81. package/query-builder/SelectQueryBuilder.js.map +1 -1
  82. package/util/OrmUtils.d.ts +4 -0
  83. package/util/OrmUtils.js +9 -0
  84. package/util/OrmUtils.js.map +1 -1
@@ -215,6 +215,15 @@ export class InsertQueryBuilder extends QueryBuilder {
215
215
  * Adds additional update statement supported in databases.
216
216
  */
217
217
  orUpdate(statementOrOverwrite, conflictTarget, orUpdateOptions) {
218
+ const { where, parameters } = orUpdateOptions?.overwriteCondition ?? {};
219
+ let wheres;
220
+ if (where) {
221
+ const condition = this.getWhereCondition(where);
222
+ if (Array.isArray(condition) ? condition.length !== 0 : condition)
223
+ wheres = [{ type: "simple", condition: condition }];
224
+ }
225
+ if (parameters)
226
+ this.setParameters(parameters);
218
227
  if (!Array.isArray(statementOrOverwrite)) {
219
228
  this.expressionMap.onUpdate = {
220
229
  conflict: statementOrOverwrite?.conflict_target,
@@ -222,6 +231,7 @@ export class InsertQueryBuilder extends QueryBuilder {
222
231
  overwrite: statementOrOverwrite?.overwrite,
223
232
  skipUpdateIfNoValuesChanged: orUpdateOptions?.skipUpdateIfNoValuesChanged,
224
233
  upsertType: orUpdateOptions?.upsertType,
234
+ overwriteCondition: wheres,
225
235
  };
226
236
  return this;
227
237
  }
@@ -231,6 +241,7 @@ export class InsertQueryBuilder extends QueryBuilder {
231
241
  skipUpdateIfNoValuesChanged: orUpdateOptions?.skipUpdateIfNoValuesChanged,
232
242
  indexPredicate: orUpdateOptions?.indexPredicate,
233
243
  upsertType: orUpdateOptions?.upsertType,
244
+ overwriteCondition: wheres,
234
245
  };
235
246
  return this;
236
247
  }
@@ -241,7 +252,16 @@ export class InsertQueryBuilder extends QueryBuilder {
241
252
  * Creates INSERT express used to perform insert query.
242
253
  */
243
254
  createInsertExpression() {
255
+ if (this.expressionMap.onUpdate || this.expressionMap.onIgnore) {
256
+ if ((this.expressionMap.onUpdate?.upsertType ?? "merge-into") ===
257
+ "merge-into" &&
258
+ this.connection.driver.supportedUpsertTypes.includes("merge-into"))
259
+ return this.createMergeExpression();
260
+ }
244
261
  const tableName = this.getTableName(this.getMainTableName());
262
+ const tableOrAliasName = this.alias !== this.getMainTableName()
263
+ ? this.escape(this.alias)
264
+ : tableName;
245
265
  const valuesExpression = this.createValuesExpression(); // its important to get values before returning expression because oracle rely on native parameters and ordering of them is important
246
266
  const returningExpression = this.connection.driver.options.type === "oracle" &&
247
267
  this.getValueSets().length > 1
@@ -350,13 +370,22 @@ export class InsertQueryBuilder extends QueryBuilder {
350
370
  query += updatePart.join(", ");
351
371
  }
352
372
  if (Array.isArray(overwrite) &&
353
- skipUpdateIfNoValuesChanged &&
354
- DriverUtils.isPostgresFamily(this.connection.driver)) {
355
- query += ` WHERE (`;
356
- query += overwrite
357
- .map((column) => `${this.escape(this.alias)}.${this.escape(column)} IS DISTINCT FROM EXCLUDED.${this.escape(column)}`)
358
- .join(" OR ");
359
- query += ") ";
373
+ skipUpdateIfNoValuesChanged) {
374
+ this.expressionMap.onUpdate.overwriteCondition ??= [];
375
+ const wheres = overwrite.map((column) => ({
376
+ type: "or",
377
+ condition: `${tableOrAliasName}.${this.escape(column)} IS DISTINCT FROM EXCLUDED.${this.escape(column)}`,
378
+ }));
379
+ this.expressionMap.onUpdate.overwriteCondition.push({
380
+ type: "and",
381
+ condition: wheres,
382
+ });
383
+ }
384
+ if (DriverUtils.isPostgresFamily(this.connection.driver) &&
385
+ this.expressionMap.onUpdate.overwriteCondition &&
386
+ this.expressionMap.onUpdate.overwriteCondition.length >
387
+ 0) {
388
+ query += ` WHERE ${this.createUpsertConditionExpression()}`;
360
389
  }
361
390
  }
362
391
  }
@@ -487,165 +516,26 @@ export class InsertQueryBuilder extends QueryBuilder {
487
516
  expression += "(";
488
517
  }
489
518
  }
490
- // extract real value from the entity
491
- let value = column.getEntityValue(valueSet);
492
- // if column is relational and value is an object then get real referenced column value from this object
493
- // for example column value is { question: { id: 1 } }, value will be equal to { id: 1 }
494
- // and we extract "1" from this object
495
- /*if (column.referencedColumn && value instanceof Object && !(typeof value === "function")) { // todo: check if we still need it since getEntityValue already has similar code
496
- value = column.referencedColumn.getEntityValue(value);
497
- }*/
498
- if (!(typeof value === "function")) {
499
- // make sure our value is normalized by a driver
500
- value = this.connection.driver.preparePersistentValue(value, column);
501
- }
502
- // newly inserted entities always have a version equal to 1 (first version)
503
- // also, user-specified version must be empty
504
- if (column.isVersion && value === undefined) {
505
- expression += "1";
506
- // } else if (column.isNestedSetLeft) {
507
- // const tableName = this.connection.driver.escape(column.entityMetadata.tablePath);
508
- // const rightColumnName = this.connection.driver.escape(column.entityMetadata.nestedSetRightColumn!.databaseName);
509
- // const subQuery = `(SELECT c.max + 1 FROM (SELECT MAX(${rightColumnName}) as max from ${tableName}) c)`;
510
- // expression += subQuery;
511
- //
512
- // } else if (column.isNestedSetRight) {
513
- // const tableName = this.connection.driver.escape(column.entityMetadata.tablePath);
514
- // const rightColumnName = this.connection.driver.escape(column.entityMetadata.nestedSetRightColumn!.databaseName);
515
- // const subQuery = `(SELECT c.max + 2 FROM (SELECT MAX(${rightColumnName}) as max from ${tableName}) c)`;
516
- // expression += subQuery;
517
- }
518
- else if (column.isDiscriminator) {
519
- expression += this.createParameter(this.expressionMap.mainAlias.metadata
520
- .discriminatorValue);
521
- // return "1";
522
- // for create and update dates we insert current date
523
- // no, we don't do it because this constant is already in "default" value of the column
524
- // with extended timestamp functionality, like CURRENT_TIMESTAMP(6) for example
525
- // } else if (column.isCreateDate || column.isUpdateDate) {
526
- // return "CURRENT_TIMESTAMP";
527
- // if column is generated uuid and database does not support its generation and custom generated value was not provided by a user - we generate a new uuid value for insertion
528
- }
529
- else if (column.isGenerated &&
530
- column.generationStrategy === "uuid" &&
531
- !this.connection.driver.isUUIDGenerationSupported() &&
532
- value === undefined) {
533
- value = uuidv4();
534
- expression += this.createParameter(value);
535
- if (!(valueSetIndex in
536
- this.expressionMap.locallyGenerated)) {
537
- this.expressionMap.locallyGenerated[valueSetIndex] =
538
- {};
539
- }
540
- column.setEntityValue(this.expressionMap.locallyGenerated[valueSetIndex], value);
541
- // if value for this column was not provided then insert default value
542
- }
543
- else if (value === undefined) {
544
- if ((this.connection.driver.options.type === "oracle" &&
545
- valueSets.length > 1) ||
546
- DriverUtils.isSQLiteFamily(this.connection.driver) ||
547
- this.connection.driver.options.type === "sap" ||
548
- this.connection.driver.options.type === "spanner") {
549
- // unfortunately sqlite does not support DEFAULT expression in INSERT queries
550
- if (column.default !== undefined &&
551
- column.default !== null) {
552
- // try to use default defined in the column
553
- expression +=
554
- this.connection.driver.normalizeDefault(column);
555
- }
556
- else if (this.connection.driver.options.type ===
557
- "spanner" &&
558
- column.isGenerated &&
559
- column.generationStrategy === "uuid") {
560
- expression += "GENERATE_UUID()"; // Produces a random universally unique identifier (UUID) as a STRING value.
561
- }
562
- else {
563
- expression += "NULL"; // otherwise simply use NULL and pray if column is nullable
564
- }
565
- }
566
- else {
567
- expression += "DEFAULT";
568
- }
569
- }
570
- else if (value === null &&
571
- (this.connection.driver.options.type === "spanner" ||
572
- this.connection.driver.options.type === "oracle")) {
573
- expression += "NULL";
574
- // support for SQL expressions in queries
575
- }
576
- else if (typeof value === "function") {
577
- expression += value();
578
- // just any other regular value
579
- }
580
- else {
581
- if (this.connection.driver.options.type === "mssql")
582
- value = this.connection.driver.parametrizeValue(column, value);
583
- // we need to store array values in a special class to make sure parameter replacement will work correctly
584
- // if (value instanceof Array)
585
- // value = new ArrayParameter(value);
586
- const paramName = this.createParameter(value);
587
- if ((DriverUtils.isMySQLFamily(this.connection.driver) ||
588
- this.connection.driver.options.type ===
589
- "aurora-mysql") &&
590
- this.connection.driver.spatialTypes.indexOf(column.type) !== -1) {
591
- const useLegacy = this.connection.driver.options.legacySpatialSupport;
592
- const geomFromText = useLegacy
593
- ? "GeomFromText"
594
- : "ST_GeomFromText";
595
- if (column.srid != null) {
596
- expression += `${geomFromText}(${paramName}, ${column.srid})`;
597
- }
598
- else {
599
- expression += `${geomFromText}(${paramName})`;
600
- }
601
- }
602
- else if (DriverUtils.isPostgresFamily(this.connection.driver) &&
603
- this.connection.driver.spatialTypes.indexOf(column.type) !== -1) {
604
- if (column.srid != null) {
605
- expression += `ST_SetSRID(ST_GeomFromGeoJSON(${paramName}), ${column.srid})::${column.type}`;
606
- }
607
- else {
608
- expression += `ST_GeomFromGeoJSON(${paramName})::${column.type}`;
609
- }
610
- }
611
- else if (this.connection.driver.options.type === "mssql" &&
612
- this.connection.driver.spatialTypes.indexOf(column.type) !== -1) {
613
- expression +=
614
- column.type +
615
- "::STGeomFromText(" +
616
- paramName +
617
- ", " +
618
- (column.srid || "0") +
619
- ")";
620
- }
621
- else {
622
- expression += paramName;
623
- }
624
- }
519
+ expression += this.createColumnValueExpression(valueSets, valueSetIndex, column);
625
520
  if (columnIndex === columns.length - 1) {
626
521
  if (valueSetIndex === valueSets.length - 1) {
627
- if (this.connection.driver.options.type ===
628
- "oracle" &&
629
- valueSets.length > 1) {
630
- expression += " FROM DUAL ";
631
- }
632
- else if (this.connection.driver.options.type === "sap" &&
522
+ if (["oracle", "sap"].includes(this.connection.driver.options.type) &&
633
523
  valueSets.length > 1) {
634
- expression += " FROM dummy ";
524
+ expression +=
525
+ " FROM " +
526
+ this.connection.driver.dummyTableName;
635
527
  }
636
528
  else {
637
529
  expression += ")";
638
530
  }
639
531
  }
640
532
  else {
641
- if (this.connection.driver.options.type ===
642
- "oracle" &&
533
+ if (["oracle", "sap"].includes(this.connection.driver.options.type) &&
643
534
  valueSets.length > 1) {
644
- expression += " FROM DUAL UNION ALL ";
645
- }
646
- else if (this.connection.driver.options.type === "sap" &&
647
- valueSets.length > 1) {
648
- expression += " FROM dummy UNION ALL ";
535
+ expression +=
536
+ " FROM " +
537
+ this.connection.driver.dummyTableName +
538
+ " UNION ALL ";
649
539
  }
650
540
  else {
651
541
  expression += "), ";
@@ -736,6 +626,443 @@ export class InsertQueryBuilder extends QueryBuilder {
736
626
  this.getValueSets().some((valueSet) => column.getEntityValue(valueSet) !== undefined &&
737
627
  column.getEntityValue(valueSet) !== null));
738
628
  }
629
+ /**
630
+ * Creates MERGE express used to perform insert query.
631
+ */
632
+ createMergeExpression() {
633
+ if (!this.connection.driver.supportedUpsertTypes.includes("merge-into"))
634
+ throw new TypeORMError(`Upsert type "merge-into" is not supported by current database driver`);
635
+ if (this.expressionMap.onUpdate?.upsertType &&
636
+ this.expressionMap.onUpdate.upsertType !== "merge-into") {
637
+ throw new TypeORMError(`Upsert type "${this.expressionMap.onUpdate.upsertType}" is not supported by current database driver`);
638
+ }
639
+ // const mainAlias = this.expressionMap.mainAlias!
640
+ const tableName = this.getTableName(this.getMainTableName());
641
+ const tableAlias = this.escape(this.alias);
642
+ const columns = this.getInsertedColumns();
643
+ const columnsExpression = this.createColumnNamesExpression();
644
+ let query = `MERGE INTO ${tableName} ${this.escape(this.alias)}`;
645
+ const mergeSourceAlias = this.escape("mergeIntoSource");
646
+ const mergeSourceExpression = this.createMergeIntoSourceExpression(mergeSourceAlias);
647
+ query += ` ${mergeSourceExpression}`;
648
+ // build on condition
649
+ if (this.expressionMap.onIgnore) {
650
+ const primaryKey = columns.find((column) => column.isPrimary);
651
+ if (primaryKey) {
652
+ query += ` ON (${tableAlias}.${this.escape(primaryKey.databaseName)} = ${mergeSourceAlias}.${this.escape(primaryKey.databaseName)})`;
653
+ }
654
+ else {
655
+ query += `ON (${this.expressionMap
656
+ .mainAlias.metadata.uniques.map((unique) => {
657
+ return `(${unique.columns
658
+ .map((column) => {
659
+ return `${tableAlias}.${this.escape(column.databaseName)} = ${mergeSourceAlias}.${this.escape(column.databaseName)}`;
660
+ })
661
+ .join(" AND ")})`;
662
+ })
663
+ .join(" OR ")})`;
664
+ }
665
+ }
666
+ else if (this.expressionMap.onUpdate) {
667
+ const { conflict, indexPredicate } = this.expressionMap.onUpdate;
668
+ if (indexPredicate) {
669
+ throw new TypeORMError(`indexPredicate option is not supported by upsert type "merge-into"`);
670
+ }
671
+ if (Array.isArray(conflict)) {
672
+ query += ` ON (${conflict
673
+ .map((column) => `${tableAlias}.${this.escape(column)} = ${mergeSourceAlias}.${this.escape(column)}`)
674
+ .join(" AND ")})`;
675
+ }
676
+ else if (conflict) {
677
+ query += ` ON (${tableAlias}.${this.escape(conflict)} = ${mergeSourceAlias}.${this.escape(conflict)})`;
678
+ }
679
+ else {
680
+ query += `ON (${this.expressionMap
681
+ .mainAlias.metadata.uniques.map((unique) => {
682
+ return `(${unique.columns
683
+ .map((column) => {
684
+ return `${tableAlias}.${this.escape(column.databaseName)} = ${mergeSourceAlias}.${this.escape(column.databaseName)}`;
685
+ })
686
+ .join(" AND ")})`;
687
+ })
688
+ .join(" OR ")})`;
689
+ }
690
+ }
691
+ if (this.expressionMap.onUpdate) {
692
+ const { overwrite, columns, conflict, skipUpdateIfNoValuesChanged, } = this.expressionMap.onUpdate;
693
+ let updateExpression = "";
694
+ if (Array.isArray(overwrite)) {
695
+ updateExpression += (overwrite || columns)
696
+ ?.filter((column) => !conflict?.includes(column))
697
+ .map((column) => `${tableAlias}.${this.escape(column)} = ${mergeSourceAlias}.${this.escape(column)}`)
698
+ .join(", ");
699
+ }
700
+ if (Array.isArray(overwrite) && skipUpdateIfNoValuesChanged) {
701
+ this.expressionMap.onUpdate.overwriteCondition ??= [];
702
+ const wheres = overwrite.map((column) => ({
703
+ type: "or",
704
+ condition: {
705
+ operator: "notEqual",
706
+ parameters: [
707
+ `${tableAlias}.${this.escape(column)}`,
708
+ `${mergeSourceAlias}.${this.escape(column)}`,
709
+ ],
710
+ },
711
+ }));
712
+ this.expressionMap.onUpdate.overwriteCondition.push({
713
+ type: "and",
714
+ condition: wheres,
715
+ });
716
+ }
717
+ const mergeCondition = this.createUpsertConditionExpression();
718
+ if (updateExpression.trim()) {
719
+ if ((this.connection.driver.options.type === "mssql" ||
720
+ this.connection.driver.options.type === "sap") &&
721
+ mergeCondition != "") {
722
+ query += ` WHEN MATCHED AND ${mergeCondition} THEN UPDATE SET ${updateExpression}`;
723
+ }
724
+ else {
725
+ query += ` WHEN MATCHED THEN UPDATE SET ${updateExpression}`;
726
+ if (mergeCondition != "") {
727
+ query += ` WHERE ${mergeCondition}`;
728
+ }
729
+ }
730
+ }
731
+ }
732
+ const valuesExpression = this.createMergeIntoInsertValuesExpression(mergeSourceAlias);
733
+ const returningExpression = this.connection.driver.options.type === "mssql"
734
+ ? this.createReturningExpression("insert")
735
+ : null;
736
+ query += " WHEN NOT MATCHED THEN INSERT";
737
+ // add columns expression
738
+ if (columnsExpression) {
739
+ query += `(${columnsExpression})`;
740
+ }
741
+ // add VALUES expression
742
+ if (valuesExpression) {
743
+ query += ` VALUES ${valuesExpression}`;
744
+ }
745
+ // add OUTPUT expression
746
+ if (returningExpression &&
747
+ this.connection.driver.options.type === "mssql") {
748
+ query += ` OUTPUT ${returningExpression}`;
749
+ }
750
+ if (this.connection.driver.options.type === "mssql") {
751
+ query += `;`;
752
+ }
753
+ return query;
754
+ }
755
+ /**
756
+ * Creates list of values needs to be inserted in the VALUES expression.
757
+ */
758
+ createMergeIntoSourceExpression(mergeSourceAlias) {
759
+ const valueSets = this.getValueSets();
760
+ const columns = this.getInsertedColumns();
761
+ let expression = "USING (";
762
+ // if column metadatas are given then apply all necessary operations with values
763
+ if (columns.length > 0) {
764
+ if (this.connection.driver.options.type === "mssql") {
765
+ expression += "VALUES ";
766
+ }
767
+ valueSets.forEach((valueSet, valueSetIndex) => {
768
+ columns.forEach((column, columnIndex) => {
769
+ if (columnIndex === 0) {
770
+ if (this.connection.driver.options.type === "mssql") {
771
+ expression += "(";
772
+ }
773
+ else {
774
+ expression += "SELECT ";
775
+ }
776
+ }
777
+ const value = column.getEntityValue(valueSet);
778
+ if (value === undefined &&
779
+ !(column.isGenerated &&
780
+ column.generationStrategy === "uuid" &&
781
+ !this.connection.driver.isUUIDGenerationSupported())) {
782
+ if (column.default !== undefined &&
783
+ column.default !== null) {
784
+ // try to use default defined in the column
785
+ expression +=
786
+ this.connection.driver.normalizeDefault(column);
787
+ }
788
+ else {
789
+ expression += "NULL"; // otherwise simply use NULL and pray if column is nullable
790
+ }
791
+ }
792
+ else if (value === null) {
793
+ expression += "NULL";
794
+ }
795
+ else {
796
+ expression += this.createColumnValueExpression(valueSets, valueSetIndex, column);
797
+ }
798
+ if (this.connection.driver.options.type !== "mssql")
799
+ expression += ` AS ${this.escape(column.databaseName)}`;
800
+ if (columnIndex === columns.length - 1) {
801
+ if (valueSetIndex === valueSets.length - 1) {
802
+ if (["oracle", "sap"].includes(this.connection.driver.options.type)) {
803
+ expression +=
804
+ " FROM " +
805
+ this.connection.driver.dummyTableName;
806
+ }
807
+ else if (this.connection.driver.options.type === "mssql") {
808
+ expression += ")";
809
+ }
810
+ }
811
+ else {
812
+ if (["oracle", "sap"].includes(this.connection.driver.options.type) &&
813
+ valueSets.length > 1) {
814
+ expression +=
815
+ " FROM " +
816
+ this.connection.driver.dummyTableName +
817
+ " UNION ALL ";
818
+ }
819
+ else if (this.connection.driver.options.type === "mssql") {
820
+ expression += "), ";
821
+ }
822
+ else {
823
+ expression += " UNION ALL ";
824
+ }
825
+ }
826
+ }
827
+ else {
828
+ expression += ", ";
829
+ }
830
+ });
831
+ });
832
+ }
833
+ else {
834
+ // for tables without metadata
835
+ throw new TypeORMError('Upsert type "merge-into" is not supported without metadata tables');
836
+ }
837
+ expression += `) ${mergeSourceAlias}`;
838
+ if (this.connection.driver.options.type === "mssql")
839
+ expression += ` (${columns
840
+ .map((column) => this.escape(column.databaseName))
841
+ .join(", ")})`;
842
+ return expression;
843
+ }
844
+ /**
845
+ * Creates list of values needs to be inserted in the VALUES expression.
846
+ */
847
+ createMergeIntoInsertValuesExpression(mergeSourceAlias) {
848
+ const columns = this.getInsertedColumns();
849
+ let expression = "";
850
+ // if column metadatas are given then apply all necessary operations with values
851
+ if (columns.length > 0) {
852
+ columns.forEach((column, columnIndex) => {
853
+ if (columnIndex === 0) {
854
+ expression += "(";
855
+ }
856
+ if ((column.isGenerated &&
857
+ column.generationStrategy === "uuid" &&
858
+ this.connection.driver.isUUIDGenerationSupported()) ||
859
+ (column.isGenerated && column.generationStrategy !== "uuid")) {
860
+ expression += `DEFAULT`;
861
+ }
862
+ else {
863
+ expression += `${mergeSourceAlias}.${this.escape(column.databaseName)}`;
864
+ }
865
+ if (columnIndex === columns.length - 1) {
866
+ expression += ")";
867
+ }
868
+ else {
869
+ expression += ", ";
870
+ }
871
+ });
872
+ }
873
+ else {
874
+ // for tables without metadata
875
+ throw new TypeORMError('Upsert type "merge-into" is not supported without metadata tables');
876
+ }
877
+ if (expression === "()")
878
+ return "";
879
+ return expression;
880
+ }
881
+ /**
882
+ * Create upsert search condition expression.
883
+ */
884
+ createUpsertConditionExpression() {
885
+ if (!this.expressionMap.onUpdate.overwriteCondition)
886
+ return "";
887
+ const conditionsArray = [];
888
+ const whereExpression = this.createWhereClausesExpression(this.expressionMap.onUpdate.overwriteCondition);
889
+ if (whereExpression.length > 0 && whereExpression !== "1=1") {
890
+ conditionsArray.push(whereExpression);
891
+ }
892
+ if (this.expressionMap.mainAlias.hasMetadata) {
893
+ const metadata = this.expressionMap.mainAlias.metadata;
894
+ // Adds the global condition of "non-deleted" for the entity with delete date columns in select query.
895
+ if (this.expressionMap.queryType === "select" &&
896
+ !this.expressionMap.withDeleted &&
897
+ metadata.deleteDateColumn) {
898
+ const column = this.expressionMap.aliasNamePrefixingEnabled
899
+ ? this.expressionMap.mainAlias.name +
900
+ "." +
901
+ metadata.deleteDateColumn.propertyName
902
+ : metadata.deleteDateColumn.propertyName;
903
+ const condition = `${column} IS NULL`;
904
+ conditionsArray.push(condition);
905
+ }
906
+ if (metadata.discriminatorColumn && metadata.parentEntityMetadata) {
907
+ const column = this.expressionMap.aliasNamePrefixingEnabled
908
+ ? this.expressionMap.mainAlias.name +
909
+ "." +
910
+ metadata.discriminatorColumn.databaseName
911
+ : metadata.discriminatorColumn.databaseName;
912
+ const condition = `${column} IN (:...discriminatorColumnValues)`;
913
+ conditionsArray.push(condition);
914
+ }
915
+ }
916
+ if (this.expressionMap.extraAppendedAndWhereCondition) {
917
+ const condition = this.expressionMap.extraAppendedAndWhereCondition;
918
+ conditionsArray.push(condition);
919
+ }
920
+ let condition = "";
921
+ if (!conditionsArray.length) {
922
+ condition += "";
923
+ }
924
+ else if (conditionsArray.length === 1) {
925
+ condition += `${conditionsArray[0]}`;
926
+ }
927
+ else {
928
+ condition += `( ${conditionsArray.join(" ) AND ( ")} )`;
929
+ }
930
+ return condition;
931
+ }
932
+ createColumnValueExpression(valueSets, valueSetIndex, column) {
933
+ const valueSet = valueSets[valueSetIndex];
934
+ let expression = "";
935
+ // extract real value from the entity
936
+ let value = column.getEntityValue(valueSet);
937
+ // if column is relational and value is an object then get real referenced column value from this object
938
+ // for example column value is { question: { id: 1 } }, value will be equal to { id: 1 }
939
+ // and we extract "1" from this object
940
+ /*if (column.referencedColumn && value instanceof Object && !(typeof value === "function")) { // todo: check if we still need it since getEntityValue already has similar code
941
+ value = column.referencedColumn.getEntityValue(value);
942
+ }*/
943
+ if (!(typeof value === "function")) {
944
+ // make sure our value is normalized by a driver
945
+ value = this.connection.driver.preparePersistentValue(value, column);
946
+ }
947
+ // newly inserted entities always have a version equal to 1 (first version)
948
+ // also, user-specified version must be empty
949
+ if (column.isVersion && value === undefined) {
950
+ expression += "1";
951
+ // } else if (column.isNestedSetLeft) {
952
+ // const tableName = this.connection.driver.escape(column.entityMetadata.tablePath);
953
+ // const rightColumnName = this.connection.driver.escape(column.entityMetadata.nestedSetRightColumn!.databaseName);
954
+ // const subQuery = `(SELECT c.max + 1 FROM (SELECT MAX(${rightColumnName}) as max from ${tableName}) c)`;
955
+ // expression += subQuery;
956
+ //
957
+ // } else if (column.isNestedSetRight) {
958
+ // const tableName = this.connection.driver.escape(column.entityMetadata.tablePath);
959
+ // const rightColumnName = this.connection.driver.escape(column.entityMetadata.nestedSetRightColumn!.databaseName);
960
+ // const subQuery = `(SELECT c.max + 2 FROM (SELECT MAX(${rightColumnName}) as max from ${tableName}) c)`;
961
+ // expression += subQuery;
962
+ }
963
+ else if (column.isDiscriminator) {
964
+ expression += this.createParameter(this.expressionMap.mainAlias.metadata.discriminatorValue);
965
+ // return "1";
966
+ // for create and update dates we insert current date
967
+ // no, we don't do it because this constant is already in "default" value of the column
968
+ // with extended timestamp functionality, like CURRENT_TIMESTAMP(6) for example
969
+ // } else if (column.isCreateDate || column.isUpdateDate) {
970
+ // return "CURRENT_TIMESTAMP";
971
+ // if column is generated uuid and database does not support its generation and custom generated value was not provided by a user - we generate a new uuid value for insertion
972
+ }
973
+ else if (column.isGenerated &&
974
+ column.generationStrategy === "uuid" &&
975
+ !this.connection.driver.isUUIDGenerationSupported() &&
976
+ value === undefined) {
977
+ value = uuidv4();
978
+ expression += this.createParameter(value);
979
+ if (!(valueSetIndex in this.expressionMap.locallyGenerated)) {
980
+ this.expressionMap.locallyGenerated[valueSetIndex] = {};
981
+ }
982
+ column.setEntityValue(this.expressionMap.locallyGenerated[valueSetIndex], value);
983
+ // if value for this column was not provided then insert default value
984
+ }
985
+ else if (value === undefined) {
986
+ if ((this.connection.driver.options.type === "oracle" &&
987
+ valueSets.length > 1) ||
988
+ DriverUtils.isSQLiteFamily(this.connection.driver) ||
989
+ this.connection.driver.options.type === "sap" ||
990
+ this.connection.driver.options.type === "spanner") {
991
+ // unfortunately sqlite does not support DEFAULT expression in INSERT queries
992
+ if (column.default !== undefined && column.default !== null) {
993
+ // try to use default defined in the column
994
+ expression +=
995
+ this.connection.driver.normalizeDefault(column);
996
+ }
997
+ else if (this.connection.driver.options.type === "spanner" &&
998
+ column.isGenerated &&
999
+ column.generationStrategy === "uuid") {
1000
+ expression += "GENERATE_UUID()"; // Produces a random universally unique identifier (UUID) as a STRING value.
1001
+ }
1002
+ else {
1003
+ expression += "NULL"; // otherwise simply use NULL and pray if column is nullable
1004
+ }
1005
+ }
1006
+ else {
1007
+ expression += "DEFAULT";
1008
+ }
1009
+ }
1010
+ else if (value === null &&
1011
+ (this.connection.driver.options.type === "spanner" ||
1012
+ this.connection.driver.options.type === "oracle")) {
1013
+ expression += "NULL";
1014
+ // support for SQL expressions in queries
1015
+ }
1016
+ else if (typeof value === "function") {
1017
+ expression += value();
1018
+ // just any other regular value
1019
+ }
1020
+ else {
1021
+ if (this.connection.driver.options.type === "mssql")
1022
+ value = this.connection.driver.parametrizeValue(column, value);
1023
+ // we need to store array values in a special class to make sure parameter replacement will work correctly
1024
+ // if (value instanceof Array)
1025
+ // value = new ArrayParameter(value);
1026
+ const paramName = this.createParameter(value);
1027
+ if ((DriverUtils.isMySQLFamily(this.connection.driver) ||
1028
+ this.connection.driver.options.type === "aurora-mysql") &&
1029
+ this.connection.driver.spatialTypes.includes(column.type)) {
1030
+ const useLegacy = this.connection.driver.options.legacySpatialSupport;
1031
+ const geomFromText = useLegacy
1032
+ ? "GeomFromText"
1033
+ : "ST_GeomFromText";
1034
+ if (column.srid != null) {
1035
+ expression += `${geomFromText}(${paramName}, ${column.srid})`;
1036
+ }
1037
+ else {
1038
+ expression += `${geomFromText}(${paramName})`;
1039
+ }
1040
+ }
1041
+ else if (DriverUtils.isPostgresFamily(this.connection.driver) &&
1042
+ this.connection.driver.spatialTypes.includes(column.type)) {
1043
+ if (column.srid != null) {
1044
+ expression += `ST_SetSRID(ST_GeomFromGeoJSON(${paramName}), ${column.srid})::${column.type}`;
1045
+ }
1046
+ else {
1047
+ expression += `ST_GeomFromGeoJSON(${paramName})::${column.type}`;
1048
+ }
1049
+ }
1050
+ else if (this.connection.driver.options.type === "mssql" &&
1051
+ this.connection.driver.spatialTypes.includes(column.type)) {
1052
+ expression +=
1053
+ column.type +
1054
+ "::STGeomFromText(" +
1055
+ paramName +
1056
+ ", " +
1057
+ (column.srid || "0") +
1058
+ ")";
1059
+ }
1060
+ else {
1061
+ expression += paramName;
1062
+ }
1063
+ }
1064
+ return expression;
1065
+ }
739
1066
  }
740
1067
 
741
1068
  //# sourceMappingURL=InsertQueryBuilder.js.map