typeorm 0.3.25-dev.5003aaa → 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 +483 -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 +483 -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,7 +255,16 @@ 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());
265
+ const tableOrAliasName = this.alias !== this.getMainTableName()
266
+ ? this.escape(this.alias)
267
+ : tableName;
248
268
  const valuesExpression = this.createValuesExpression(); // its important to get values before returning expression because oracle rely on native parameters and ordering of them is important
249
269
  const returningExpression = this.connection.driver.options.type === "oracle" &&
250
270
  this.getValueSets().length > 1
@@ -353,13 +373,22 @@ class InsertQueryBuilder extends QueryBuilder_1.QueryBuilder {
353
373
  query += updatePart.join(", ");
354
374
  }
355
375
  if (Array.isArray(overwrite) &&
356
- skipUpdateIfNoValuesChanged &&
357
- DriverUtils_1.DriverUtils.isPostgresFamily(this.connection.driver)) {
358
- query += ` WHERE (`;
359
- query += overwrite
360
- .map((column) => `${this.escape(this.alias)}.${this.escape(column)} IS DISTINCT FROM EXCLUDED.${this.escape(column)}`)
361
- .join(" OR ");
362
- 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()}`;
363
392
  }
364
393
  }
365
394
  }
@@ -490,165 +519,26 @@ class InsertQueryBuilder extends QueryBuilder_1.QueryBuilder {
490
519
  expression += "(";
491
520
  }
492
521
  }
493
- // extract real value from the entity
494
- let value = column.getEntityValue(valueSet);
495
- // if column is relational and value is an object then get real referenced column value from this object
496
- // for example column value is { question: { id: 1 } }, value will be equal to { id: 1 }
497
- // and we extract "1" from this object
498
- /*if (column.referencedColumn && value instanceof Object && !(typeof value === "function")) { // todo: check if we still need it since getEntityValue already has similar code
499
- value = column.referencedColumn.getEntityValue(value);
500
- }*/
501
- if (!(typeof value === "function")) {
502
- // make sure our value is normalized by a driver
503
- value = this.connection.driver.preparePersistentValue(value, column);
504
- }
505
- // newly inserted entities always have a version equal to 1 (first version)
506
- // also, user-specified version must be empty
507
- if (column.isVersion && value === undefined) {
508
- expression += "1";
509
- // } else if (column.isNestedSetLeft) {
510
- // const tableName = this.connection.driver.escape(column.entityMetadata.tablePath);
511
- // const rightColumnName = this.connection.driver.escape(column.entityMetadata.nestedSetRightColumn!.databaseName);
512
- // const subQuery = `(SELECT c.max + 1 FROM (SELECT MAX(${rightColumnName}) as max from ${tableName}) c)`;
513
- // expression += subQuery;
514
- //
515
- // } else if (column.isNestedSetRight) {
516
- // const tableName = this.connection.driver.escape(column.entityMetadata.tablePath);
517
- // const rightColumnName = this.connection.driver.escape(column.entityMetadata.nestedSetRightColumn!.databaseName);
518
- // const subQuery = `(SELECT c.max + 2 FROM (SELECT MAX(${rightColumnName}) as max from ${tableName}) c)`;
519
- // expression += subQuery;
520
- }
521
- else if (column.isDiscriminator) {
522
- expression += this.createParameter(this.expressionMap.mainAlias.metadata
523
- .discriminatorValue);
524
- // return "1";
525
- // for create and update dates we insert current date
526
- // no, we don't do it because this constant is already in "default" value of the column
527
- // with extended timestamp functionality, like CURRENT_TIMESTAMP(6) for example
528
- // } else if (column.isCreateDate || column.isUpdateDate) {
529
- // return "CURRENT_TIMESTAMP";
530
- // 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
531
- }
532
- else if (column.isGenerated &&
533
- column.generationStrategy === "uuid" &&
534
- !this.connection.driver.isUUIDGenerationSupported() &&
535
- value === undefined) {
536
- value = (0, uuid_1.v4)();
537
- expression += this.createParameter(value);
538
- if (!(valueSetIndex in
539
- this.expressionMap.locallyGenerated)) {
540
- this.expressionMap.locallyGenerated[valueSetIndex] =
541
- {};
542
- }
543
- column.setEntityValue(this.expressionMap.locallyGenerated[valueSetIndex], value);
544
- // if value for this column was not provided then insert default value
545
- }
546
- else if (value === undefined) {
547
- if ((this.connection.driver.options.type === "oracle" &&
548
- valueSets.length > 1) ||
549
- DriverUtils_1.DriverUtils.isSQLiteFamily(this.connection.driver) ||
550
- this.connection.driver.options.type === "sap" ||
551
- this.connection.driver.options.type === "spanner") {
552
- // unfortunately sqlite does not support DEFAULT expression in INSERT queries
553
- if (column.default !== undefined &&
554
- column.default !== null) {
555
- // try to use default defined in the column
556
- expression +=
557
- this.connection.driver.normalizeDefault(column);
558
- }
559
- else if (this.connection.driver.options.type ===
560
- "spanner" &&
561
- column.isGenerated &&
562
- column.generationStrategy === "uuid") {
563
- expression += "GENERATE_UUID()"; // Produces a random universally unique identifier (UUID) as a STRING value.
564
- }
565
- else {
566
- expression += "NULL"; // otherwise simply use NULL and pray if column is nullable
567
- }
568
- }
569
- else {
570
- expression += "DEFAULT";
571
- }
572
- }
573
- else if (value === null &&
574
- (this.connection.driver.options.type === "spanner" ||
575
- this.connection.driver.options.type === "oracle")) {
576
- expression += "NULL";
577
- // support for SQL expressions in queries
578
- }
579
- else if (typeof value === "function") {
580
- expression += value();
581
- // just any other regular value
582
- }
583
- else {
584
- if (this.connection.driver.options.type === "mssql")
585
- value = this.connection.driver.parametrizeValue(column, value);
586
- // we need to store array values in a special class to make sure parameter replacement will work correctly
587
- // if (value instanceof Array)
588
- // value = new ArrayParameter(value);
589
- const paramName = this.createParameter(value);
590
- if ((DriverUtils_1.DriverUtils.isMySQLFamily(this.connection.driver) ||
591
- this.connection.driver.options.type ===
592
- "aurora-mysql") &&
593
- this.connection.driver.spatialTypes.indexOf(column.type) !== -1) {
594
- const useLegacy = this.connection.driver.options.legacySpatialSupport;
595
- const geomFromText = useLegacy
596
- ? "GeomFromText"
597
- : "ST_GeomFromText";
598
- if (column.srid != null) {
599
- expression += `${geomFromText}(${paramName}, ${column.srid})`;
600
- }
601
- else {
602
- expression += `${geomFromText}(${paramName})`;
603
- }
604
- }
605
- else if (DriverUtils_1.DriverUtils.isPostgresFamily(this.connection.driver) &&
606
- this.connection.driver.spatialTypes.indexOf(column.type) !== -1) {
607
- if (column.srid != null) {
608
- expression += `ST_SetSRID(ST_GeomFromGeoJSON(${paramName}), ${column.srid})::${column.type}`;
609
- }
610
- else {
611
- expression += `ST_GeomFromGeoJSON(${paramName})::${column.type}`;
612
- }
613
- }
614
- else if (this.connection.driver.options.type === "mssql" &&
615
- this.connection.driver.spatialTypes.indexOf(column.type) !== -1) {
616
- expression +=
617
- column.type +
618
- "::STGeomFromText(" +
619
- paramName +
620
- ", " +
621
- (column.srid || "0") +
622
- ")";
623
- }
624
- else {
625
- expression += paramName;
626
- }
627
- }
522
+ expression += this.createColumnValueExpression(valueSets, valueSetIndex, column);
628
523
  if (columnIndex === columns.length - 1) {
629
524
  if (valueSetIndex === valueSets.length - 1) {
630
- if (this.connection.driver.options.type ===
631
- "oracle" &&
632
- valueSets.length > 1) {
633
- expression += " FROM DUAL ";
634
- }
635
- else if (this.connection.driver.options.type === "sap" &&
525
+ if (["oracle", "sap"].includes(this.connection.driver.options.type) &&
636
526
  valueSets.length > 1) {
637
- expression += " FROM dummy ";
527
+ expression +=
528
+ " FROM " +
529
+ this.connection.driver.dummyTableName;
638
530
  }
639
531
  else {
640
532
  expression += ")";
641
533
  }
642
534
  }
643
535
  else {
644
- if (this.connection.driver.options.type ===
645
- "oracle" &&
536
+ if (["oracle", "sap"].includes(this.connection.driver.options.type) &&
646
537
  valueSets.length > 1) {
647
- expression += " FROM DUAL UNION ALL ";
648
- }
649
- else if (this.connection.driver.options.type === "sap" &&
650
- valueSets.length > 1) {
651
- expression += " FROM dummy UNION ALL ";
538
+ expression +=
539
+ " FROM " +
540
+ this.connection.driver.dummyTableName +
541
+ " UNION ALL ";
652
542
  }
653
543
  else {
654
544
  expression += "), ";
@@ -739,6 +629,443 @@ class InsertQueryBuilder extends QueryBuilder_1.QueryBuilder {
739
629
  this.getValueSets().some((valueSet) => column.getEntityValue(valueSet) !== undefined &&
740
630
  column.getEntityValue(valueSet) !== null));
741
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
+ }
742
1069
  }
743
1070
  exports.InsertQueryBuilder = InsertQueryBuilder;
744
1071