typeorm 0.3.24-dev.e9eaf79 → 0.3.25-dev.03faa78

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 (104) hide show
  1. package/README.md +9 -9
  2. package/browser/driver/capacitor/CapacitorDriver.js +2 -2
  3. package/browser/driver/capacitor/CapacitorDriver.js.map +1 -1
  4. package/browser/driver/capacitor/CapacitorQueryRunner.js +1 -1
  5. package/browser/driver/capacitor/CapacitorQueryRunner.js.map +1 -1
  6. package/browser/driver/oracle/OracleDriver.js +1 -1
  7. package/browser/driver/oracle/OracleDriver.js.map +1 -1
  8. package/browser/driver/postgres/PostgresDriver.js +2 -1
  9. package/browser/driver/postgres/PostgresDriver.js.map +1 -1
  10. package/browser/driver/postgres/PostgresQueryRunner.js +8 -0
  11. package/browser/driver/postgres/PostgresQueryRunner.js.map +1 -1
  12. package/browser/driver/sap/SapDriver.js +1 -1
  13. package/browser/driver/sap/SapDriver.js.map +1 -1
  14. package/browser/driver/spanner/SpannerConnectionCredentialsOptions.d.ts +13 -0
  15. package/browser/driver/spanner/SpannerConnectionCredentialsOptions.js.map +1 -1
  16. package/browser/driver/spanner/SpannerDriver.d.ts +2 -6
  17. package/browser/driver/spanner/SpannerDriver.js +11 -12
  18. package/browser/driver/spanner/SpannerDriver.js.map +1 -1
  19. package/browser/driver/sqlserver/SqlServerDriver.d.ts +2 -1
  20. package/browser/driver/sqlserver/SqlServerDriver.js +5 -12
  21. package/browser/driver/sqlserver/SqlServerDriver.js.map +1 -1
  22. package/browser/driver/types/UpsertType.d.ts +1 -1
  23. package/browser/driver/types/UpsertType.js.map +1 -1
  24. package/browser/entity-manager/EntityManager.d.ts +2 -2
  25. package/browser/entity-manager/EntityManager.js +2 -2
  26. package/browser/entity-manager/EntityManager.js.map +1 -1
  27. package/browser/error/InitializedRelationError.d.ts +1 -1
  28. package/browser/error/InitializedRelationError.js +1 -1
  29. package/browser/error/InitializedRelationError.js.map +1 -1
  30. package/browser/error/ReturningStatementNotSupportedError.js +1 -1
  31. package/browser/error/ReturningStatementNotSupportedError.js.map +1 -1
  32. package/browser/metadata/ColumnMetadata.d.ts +2 -2
  33. package/browser/metadata/ColumnMetadata.js +1 -1
  34. package/browser/metadata/ColumnMetadata.js.map +1 -1
  35. package/browser/query-builder/DeleteQueryBuilder.js +3 -0
  36. package/browser/query-builder/DeleteQueryBuilder.js.map +1 -1
  37. package/browser/query-builder/InsertOrUpdateOptions.d.ts +6 -0
  38. package/browser/query-builder/InsertOrUpdateOptions.js.map +1 -1
  39. package/browser/query-builder/InsertQueryBuilder.d.ts +17 -0
  40. package/browser/query-builder/InsertQueryBuilder.js +487 -150
  41. package/browser/query-builder/InsertQueryBuilder.js.map +1 -1
  42. package/browser/query-builder/QueryExpressionMap.d.ts +1 -0
  43. package/browser/query-builder/QueryExpressionMap.js.map +1 -1
  44. package/browser/query-builder/ReturningResultsEntityUpdator.js +11 -6
  45. package/browser/query-builder/ReturningResultsEntityUpdator.js.map +1 -1
  46. package/browser/query-builder/SelectQueryBuilder.js +21 -18
  47. package/browser/query-builder/SelectQueryBuilder.js.map +1 -1
  48. package/browser/query-builder/UpdateQueryBuilder.js +3 -0
  49. package/browser/query-builder/UpdateQueryBuilder.js.map +1 -1
  50. package/browser/repository/Repository.d.ts +2 -2
  51. package/browser/repository/Repository.js +2 -2
  52. package/browser/repository/Repository.js.map +1 -1
  53. package/driver/capacitor/CapacitorDriver.js +2 -2
  54. package/driver/capacitor/CapacitorDriver.js.map +1 -1
  55. package/driver/capacitor/CapacitorQueryRunner.js +1 -1
  56. package/driver/capacitor/CapacitorQueryRunner.js.map +1 -1
  57. package/driver/oracle/OracleDriver.js +1 -1
  58. package/driver/oracle/OracleDriver.js.map +1 -1
  59. package/driver/postgres/PostgresDriver.js +2 -1
  60. package/driver/postgres/PostgresDriver.js.map +1 -1
  61. package/driver/postgres/PostgresQueryRunner.js +8 -0
  62. package/driver/postgres/PostgresQueryRunner.js.map +1 -1
  63. package/driver/sap/SapDriver.js +1 -1
  64. package/driver/sap/SapDriver.js.map +1 -1
  65. package/driver/spanner/SpannerConnectionCredentialsOptions.d.ts +13 -0
  66. package/driver/spanner/SpannerConnectionCredentialsOptions.js.map +1 -1
  67. package/driver/spanner/SpannerDriver.d.ts +2 -6
  68. package/driver/spanner/SpannerDriver.js +11 -12
  69. package/driver/spanner/SpannerDriver.js.map +1 -1
  70. package/driver/sqlserver/SqlServerDriver.d.ts +2 -1
  71. package/driver/sqlserver/SqlServerDriver.js +5 -12
  72. package/driver/sqlserver/SqlServerDriver.js.map +1 -1
  73. package/driver/types/UpsertType.d.ts +1 -1
  74. package/driver/types/UpsertType.js.map +1 -1
  75. package/entity-manager/EntityManager.d.ts +2 -2
  76. package/entity-manager/EntityManager.js +2 -2
  77. package/entity-manager/EntityManager.js.map +1 -1
  78. package/error/InitializedRelationError.d.ts +1 -1
  79. package/error/InitializedRelationError.js +1 -1
  80. package/error/InitializedRelationError.js.map +1 -1
  81. package/error/ReturningStatementNotSupportedError.js +1 -1
  82. package/error/ReturningStatementNotSupportedError.js.map +1 -1
  83. package/metadata/ColumnMetadata.d.ts +2 -2
  84. package/metadata/ColumnMetadata.js +1 -1
  85. package/metadata/ColumnMetadata.js.map +1 -1
  86. package/package.json +1 -1
  87. package/query-builder/DeleteQueryBuilder.js +3 -0
  88. package/query-builder/DeleteQueryBuilder.js.map +1 -1
  89. package/query-builder/InsertOrUpdateOptions.d.ts +6 -0
  90. package/query-builder/InsertOrUpdateOptions.js.map +1 -1
  91. package/query-builder/InsertQueryBuilder.d.ts +17 -0
  92. package/query-builder/InsertQueryBuilder.js +487 -150
  93. package/query-builder/InsertQueryBuilder.js.map +1 -1
  94. package/query-builder/QueryExpressionMap.d.ts +1 -0
  95. package/query-builder/QueryExpressionMap.js.map +1 -1
  96. package/query-builder/ReturningResultsEntityUpdator.js +11 -6
  97. package/query-builder/ReturningResultsEntityUpdator.js.map +1 -1
  98. package/query-builder/SelectQueryBuilder.js +21 -18
  99. package/query-builder/SelectQueryBuilder.js.map +1 -1
  100. package/query-builder/UpdateQueryBuilder.js +3 -0
  101. package/query-builder/UpdateQueryBuilder.js.map +1 -1
  102. package/repository/Repository.d.ts +2 -2
  103. package/repository/Repository.js +2 -2
  104. package/repository/Repository.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
  }
@@ -396,6 +425,10 @@ class InsertQueryBuilder extends QueryBuilder_1.QueryBuilder {
396
425
  DriverUtils_1.DriverUtils.isMySQLFamily(this.connection.driver))) {
397
426
  query += ` RETURNING ${returningExpression}`;
398
427
  }
428
+ if (returningExpression &&
429
+ this.connection.driver.options.type === "spanner") {
430
+ query += ` THEN RETURN ${returningExpression}`;
431
+ }
399
432
  // Inserting a specific value for an auto-increment primary key in mssql requires enabling IDENTITY_INSERT
400
433
  // IDENTITY_INSERT can only be enabled for tables where there is an IDENTITY column and only if there is a value to be inserted (i.e. supplying DEFAULT is prohibited if IDENTITY_INSERT is enabled)
401
434
  if (this.connection.driver.options.type === "mssql" &&
@@ -486,159 +519,26 @@ class InsertQueryBuilder extends QueryBuilder_1.QueryBuilder {
486
519
  expression += "(";
487
520
  }
488
521
  }
489
- // extract real value from the entity
490
- let value = column.getEntityValue(valueSet);
491
- // if column is relational and value is an object then get real referenced column value from this object
492
- // for example column value is { question: { id: 1 } }, value will be equal to { id: 1 }
493
- // and we extract "1" from this object
494
- /*if (column.referencedColumn && value instanceof Object && !(typeof value === "function")) { // todo: check if we still need it since getEntityValue already has similar code
495
- value = column.referencedColumn.getEntityValue(value);
496
- }*/
497
- if (!(typeof value === "function")) {
498
- // make sure our value is normalized by a driver
499
- value = this.connection.driver.preparePersistentValue(value, column);
500
- }
501
- // newly inserted entities always have a version equal to 1 (first version)
502
- // also, user-specified version must be empty
503
- if (column.isVersion && value === undefined) {
504
- expression += "1";
505
- // } else if (column.isNestedSetLeft) {
506
- // const tableName = this.connection.driver.escape(column.entityMetadata.tablePath);
507
- // const rightColumnName = this.connection.driver.escape(column.entityMetadata.nestedSetRightColumn!.databaseName);
508
- // const subQuery = `(SELECT c.max + 1 FROM (SELECT MAX(${rightColumnName}) as max from ${tableName}) c)`;
509
- // expression += subQuery;
510
- //
511
- // } else if (column.isNestedSetRight) {
512
- // const tableName = this.connection.driver.escape(column.entityMetadata.tablePath);
513
- // const rightColumnName = this.connection.driver.escape(column.entityMetadata.nestedSetRightColumn!.databaseName);
514
- // const subQuery = `(SELECT c.max + 2 FROM (SELECT MAX(${rightColumnName}) as max from ${tableName}) c)`;
515
- // expression += subQuery;
516
- }
517
- else if (column.isDiscriminator) {
518
- expression += this.createParameter(this.expressionMap.mainAlias.metadata
519
- .discriminatorValue);
520
- // return "1";
521
- // for create and update dates we insert current date
522
- // no, we don't do it because this constant is already in "default" value of the column
523
- // with extended timestamp functionality, like CURRENT_TIMESTAMP(6) for example
524
- // } else if (column.isCreateDate || column.isUpdateDate) {
525
- // return "CURRENT_TIMESTAMP";
526
- // 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
527
- }
528
- else if (column.isGenerated &&
529
- column.generationStrategy === "uuid" &&
530
- !this.connection.driver.isUUIDGenerationSupported() &&
531
- value === undefined) {
532
- value = (0, uuid_1.v4)();
533
- expression += this.createParameter(value);
534
- if (!(valueSetIndex in
535
- this.expressionMap.locallyGenerated)) {
536
- this.expressionMap.locallyGenerated[valueSetIndex] =
537
- {};
538
- }
539
- column.setEntityValue(this.expressionMap.locallyGenerated[valueSetIndex], value);
540
- // if value for this column was not provided then insert default value
541
- }
542
- else if (value === undefined) {
543
- if ((this.connection.driver.options.type === "oracle" &&
544
- valueSets.length > 1) ||
545
- DriverUtils_1.DriverUtils.isSQLiteFamily(this.connection.driver) ||
546
- this.connection.driver.options.type === "sap" ||
547
- this.connection.driver.options.type === "spanner") {
548
- // unfortunately sqlite does not support DEFAULT expression in INSERT queries
549
- if (column.default !== undefined &&
550
- column.default !== null) {
551
- // try to use default defined in the column
552
- expression +=
553
- this.connection.driver.normalizeDefault(column);
554
- }
555
- else {
556
- expression += "NULL"; // otherwise simply use NULL and pray if column is nullable
557
- }
558
- }
559
- else {
560
- expression += "DEFAULT";
561
- }
562
- }
563
- else if (value === null &&
564
- (this.connection.driver.options.type === "spanner" ||
565
- this.connection.driver.options.type === "oracle")) {
566
- expression += "NULL";
567
- // support for SQL expressions in queries
568
- }
569
- else if (typeof value === "function") {
570
- expression += value();
571
- // just any other regular value
572
- }
573
- else {
574
- if (this.connection.driver.options.type === "mssql")
575
- value = this.connection.driver.parametrizeValue(column, value);
576
- // we need to store array values in a special class to make sure parameter replacement will work correctly
577
- // if (value instanceof Array)
578
- // value = new ArrayParameter(value);
579
- const paramName = this.createParameter(value);
580
- if ((DriverUtils_1.DriverUtils.isMySQLFamily(this.connection.driver) ||
581
- this.connection.driver.options.type ===
582
- "aurora-mysql") &&
583
- this.connection.driver.spatialTypes.indexOf(column.type) !== -1) {
584
- const useLegacy = this.connection.driver.options.legacySpatialSupport;
585
- const geomFromText = useLegacy
586
- ? "GeomFromText"
587
- : "ST_GeomFromText";
588
- if (column.srid != null) {
589
- expression += `${geomFromText}(${paramName}, ${column.srid})`;
590
- }
591
- else {
592
- expression += `${geomFromText}(${paramName})`;
593
- }
594
- }
595
- else if (DriverUtils_1.DriverUtils.isPostgresFamily(this.connection.driver) &&
596
- this.connection.driver.spatialTypes.indexOf(column.type) !== -1) {
597
- if (column.srid != null) {
598
- expression += `ST_SetSRID(ST_GeomFromGeoJSON(${paramName}), ${column.srid})::${column.type}`;
599
- }
600
- else {
601
- expression += `ST_GeomFromGeoJSON(${paramName})::${column.type}`;
602
- }
603
- }
604
- else if (this.connection.driver.options.type === "mssql" &&
605
- this.connection.driver.spatialTypes.indexOf(column.type) !== -1) {
606
- expression +=
607
- column.type +
608
- "::STGeomFromText(" +
609
- paramName +
610
- ", " +
611
- (column.srid || "0") +
612
- ")";
613
- }
614
- else {
615
- expression += paramName;
616
- }
617
- }
522
+ expression += this.createColumnValueExpression(valueSets, valueSetIndex, column);
618
523
  if (columnIndex === columns.length - 1) {
619
524
  if (valueSetIndex === valueSets.length - 1) {
620
- if (this.connection.driver.options.type ===
621
- "oracle" &&
525
+ if (["oracle", "sap"].includes(this.connection.driver.options.type) &&
622
526
  valueSets.length > 1) {
623
- expression += " FROM DUAL ";
624
- }
625
- else if (this.connection.driver.options.type === "sap" &&
626
- valueSets.length > 1) {
627
- expression += " FROM dummy ";
527
+ expression +=
528
+ " FROM " +
529
+ this.connection.driver.dummyTableName;
628
530
  }
629
531
  else {
630
532
  expression += ")";
631
533
  }
632
534
  }
633
535
  else {
634
- if (this.connection.driver.options.type ===
635
- "oracle" &&
536
+ if (["oracle", "sap"].includes(this.connection.driver.options.type) &&
636
537
  valueSets.length > 1) {
637
- expression += " FROM DUAL UNION ALL ";
638
- }
639
- else if (this.connection.driver.options.type === "sap" &&
640
- valueSets.length > 1) {
641
- expression += " FROM dummy UNION ALL ";
538
+ expression +=
539
+ " FROM " +
540
+ this.connection.driver.dummyTableName +
541
+ " UNION ALL ";
642
542
  }
643
543
  else {
644
544
  expression += "), ";
@@ -729,6 +629,443 @@ class InsertQueryBuilder extends QueryBuilder_1.QueryBuilder {
729
629
  this.getValueSets().some((valueSet) => column.getEntityValue(valueSet) !== undefined &&
730
630
  column.getEntityValue(valueSet) !== null));
731
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
+ }
732
1069
  }
733
1070
  exports.InsertQueryBuilder = InsertQueryBuilder;
734
1071