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