typeorm 0.3.25-dev.2bfa300 → 0.3.25-dev.a9c16ee

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