@contember/schema-migrations 1.1.0-alpha.2 → 1.1.0-alpha.3

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 (92) hide show
  1. package/dist/src/MigrationDescriber.d.ts +3 -3
  2. package/dist/src/MigrationDescriber.d.ts.map +1 -1
  3. package/dist/src/MigrationDescriber.js +7 -4
  4. package/dist/src/MigrationDescriber.js.map +1 -1
  5. package/dist/src/SchemaDiffer.d.ts.map +1 -1
  6. package/dist/src/SchemaDiffer.js +5 -5
  7. package/dist/src/SchemaDiffer.js.map +1 -1
  8. package/dist/src/SchemaMigrator.d.ts.map +1 -1
  9. package/dist/src/SchemaMigrator.js +4 -1
  10. package/dist/src/SchemaMigrator.js.map +1 -1
  11. package/dist/src/modifications/ModificationHandler.d.ts +5 -1
  12. package/dist/src/modifications/ModificationHandler.d.ts.map +1 -1
  13. package/dist/src/modifications/ModificationHandlerFactory.d.ts +2 -2
  14. package/dist/src/modifications/ModificationHandlerFactory.d.ts.map +1 -1
  15. package/dist/src/modifications/ModificationHandlerFactory.js +3 -2
  16. package/dist/src/modifications/ModificationHandlerFactory.js.map +1 -1
  17. package/dist/src/modifications/columns/UpdateColumnDefinitionModification.d.ts.map +1 -1
  18. package/dist/src/modifications/columns/UpdateColumnDefinitionModification.js +8 -5
  19. package/dist/src/modifications/columns/UpdateColumnDefinitionModification.js.map +1 -1
  20. package/dist/src/modifications/entities/CreateEntityModification.d.ts.map +1 -1
  21. package/dist/src/modifications/entities/CreateEntityModification.js +4 -3
  22. package/dist/src/modifications/entities/CreateEntityModification.js.map +1 -1
  23. package/dist/src/modifications/entities/RemoveEntityModification.d.ts.map +1 -1
  24. package/dist/src/modifications/entities/RemoveEntityModification.js +7 -7
  25. package/dist/src/modifications/entities/RemoveEntityModification.js.map +1 -1
  26. package/dist/src/modifications/entities/UpdateEntityNameModification.d.ts.map +1 -1
  27. package/dist/src/modifications/entities/UpdateEntityNameModification.js +6 -6
  28. package/dist/src/modifications/entities/UpdateEntityNameModification.js.map +1 -1
  29. package/dist/src/modifications/fields/RemoveFieldModification.d.ts.map +1 -1
  30. package/dist/src/modifications/fields/RemoveFieldModification.js +3 -3
  31. package/dist/src/modifications/fields/RemoveFieldModification.js.map +1 -1
  32. package/dist/src/modifications/fields/UpdateFieldNameModification.d.ts.map +1 -1
  33. package/dist/src/modifications/fields/UpdateFieldNameModification.js +6 -6
  34. package/dist/src/modifications/fields/UpdateFieldNameModification.js.map +1 -1
  35. package/dist/src/modifications/relations/ConvertOneHasManyToManyHasManyRelationModification.d.ts +10 -0
  36. package/dist/src/modifications/relations/ConvertOneHasManyToManyHasManyRelationModification.d.ts.map +1 -0
  37. package/dist/src/modifications/relations/ConvertOneHasManyToManyHasManyRelationModification.js +84 -0
  38. package/dist/src/modifications/relations/ConvertOneHasManyToManyHasManyRelationModification.js.map +1 -0
  39. package/dist/src/modifications/relations/ConvertOneToManyRelationModification.d.ts.map +1 -1
  40. package/dist/src/modifications/relations/ConvertOneToManyRelationModification.js +3 -3
  41. package/dist/src/modifications/relations/ConvertOneToManyRelationModification.js.map +1 -1
  42. package/dist/src/modifications/relations/CreateRelationModification.d.ts.map +1 -1
  43. package/dist/src/modifications/relations/CreateRelationModification.js +6 -31
  44. package/dist/src/modifications/relations/CreateRelationModification.js.map +1 -1
  45. package/dist/src/modifications/relations/index.d.ts +1 -0
  46. package/dist/src/modifications/relations/index.d.ts.map +1 -1
  47. package/dist/src/modifications/relations/index.js +1 -0
  48. package/dist/src/modifications/relations/index.js.map +1 -1
  49. package/dist/src/modifications/utils/createJunctionTable.d.ts +4 -0
  50. package/dist/src/modifications/utils/createJunctionTable.d.ts.map +1 -0
  51. package/dist/src/modifications/utils/createJunctionTable.js +33 -0
  52. package/dist/src/modifications/utils/createJunctionTable.js.map +1 -0
  53. package/dist/src/modifications/utils/getPrimaryColumnType.d.ts +3 -0
  54. package/dist/src/modifications/utils/getPrimaryColumnType.d.ts.map +1 -0
  55. package/dist/src/modifications/utils/getPrimaryColumnType.js +9 -0
  56. package/dist/src/modifications/utils/getPrimaryColumnType.js.map +1 -0
  57. package/dist/src/modifications/utils/sqlUpdateUtils.d.ts +2 -2
  58. package/dist/src/modifications/utils/sqlUpdateUtils.d.ts.map +1 -1
  59. package/dist/src/modifications/utils/sqlUpdateUtils.js +4 -4
  60. package/dist/src/modifications/utils/sqlUpdateUtils.js.map +1 -1
  61. package/dist/src/tsconfig.tsbuildinfo +1 -1
  62. package/dist/tests/cases/integration/convertOneHasManyToManyHasManyRelation.test.d.ts +2 -0
  63. package/dist/tests/cases/integration/convertOneHasManyToManyHasManyRelation.test.d.ts.map +1 -0
  64. package/dist/tests/cases/integration/convertOneHasManyToManyHasManyRelation.test.js +92 -0
  65. package/dist/tests/cases/integration/convertOneHasManyToManyHasManyRelation.test.js.map +1 -0
  66. package/dist/tests/cases/integration/updateColumnDefinition.test.js +1 -1
  67. package/dist/tests/cases/integration/updateColumnDefinition.test.js.map +1 -1
  68. package/dist/tests/src/tests.js +1 -1
  69. package/dist/tests/src/tests.js.map +1 -1
  70. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  71. package/package.json +7 -7
  72. package/src/MigrationDescriber.ts +16 -8
  73. package/src/SchemaDiffer.ts +7 -2
  74. package/src/SchemaMigrator.ts +4 -1
  75. package/src/modifications/ModificationHandler.ts +7 -1
  76. package/src/modifications/ModificationHandlerFactory.ts +5 -3
  77. package/src/modifications/columns/UpdateColumnDefinitionModification.ts +8 -6
  78. package/src/modifications/entities/CreateEntityModification.ts +8 -4
  79. package/src/modifications/entities/RemoveEntityModification.ts +8 -7
  80. package/src/modifications/entities/UpdateEntityNameModification.ts +6 -6
  81. package/src/modifications/fields/RemoveFieldModification.ts +4 -3
  82. package/src/modifications/fields/UpdateFieldNameModification.ts +6 -6
  83. package/src/modifications/relations/ConvertOneHasManyToManyHasManyRelationModification.ts +109 -0
  84. package/src/modifications/relations/ConvertOneToManyRelationModification.ts +7 -3
  85. package/src/modifications/relations/CreateRelationModification.ts +10 -36
  86. package/src/modifications/relations/index.ts +1 -0
  87. package/src/modifications/utils/createJunctionTable.ts +41 -0
  88. package/src/modifications/utils/getPrimaryColumnType.ts +6 -0
  89. package/src/modifications/utils/sqlUpdateUtils.ts +4 -4
  90. package/tests/cases/integration/convertOneHasManyToManyHasManyRelation.test.ts +81 -0
  91. package/tests/cases/integration/updateColumnDefinition.test.ts +1 -1
  92. package/tests/src/tests.ts +1 -1
@@ -1,12 +1,16 @@
1
1
  import { MigrationBuilder } from '@contember/database-migrations'
2
2
  import { Model, Schema } from '@contember/schema'
3
3
  import { SchemaUpdater, updateModel } from '../utils/schemaUpdateUtils'
4
- import { ModificationHandlerStatic } from '../ModificationHandler'
4
+ import { ModificationHandlerOptions, ModificationHandlerStatic } from '../ModificationHandler'
5
5
  import { createEventTrigger, createEventTrxTrigger } from '../utils/sqlUpdateUtils'
6
6
 
7
7
  export const CreateEntityModification: ModificationHandlerStatic<CreateEntityModificationData> = class {
8
8
  static id = 'createEntity'
9
- constructor(private readonly data: CreateEntityModificationData, private readonly schema: Schema) {}
9
+ constructor(
10
+ private readonly data: CreateEntityModificationData,
11
+ private readonly schema: Schema,
12
+ private readonly options: ModificationHandlerOptions,
13
+ ) {}
10
14
 
11
15
  public createSql(builder: MigrationBuilder): void {
12
16
  const entity = this.data.entity
@@ -23,8 +27,8 @@ export const CreateEntityModification: ModificationHandlerStatic<CreateEntityMod
23
27
  notNull: true,
24
28
  },
25
29
  })
26
- createEventTrigger(builder, entity.tableName, [entity.primaryColumn])
27
- createEventTrxTrigger(builder, entity.tableName)
30
+ createEventTrigger(builder, this.options.systemSchema, entity.tableName, [entity.primaryColumn])
31
+ createEventTrxTrigger(builder, this.options.systemSchema, entity.tableName)
28
32
  }
29
33
 
30
34
  public getSchemaUpdater(): SchemaUpdater {
@@ -11,17 +11,18 @@ import {
11
11
  updateModel,
12
12
  updateSchema,
13
13
  } from '../utils/schemaUpdateUtils'
14
- import { ModificationHandlerStatic } from '../ModificationHandler'
14
+ import { ModificationHandlerOptions, ModificationHandlerStatic } from '../ModificationHandler'
15
15
  import { VERSION_ACL_PATCH, VERSION_REMOVE_REFERENCING_RELATIONS } from '../ModificationVersions'
16
16
  import { isRelation, PredicateDefinitionProcessor } from '@contember/schema-utils'
17
17
  import { RemoveFieldModification } from '../fields'
18
18
 
19
19
  export const RemoveEntityModification: ModificationHandlerStatic<RemoveEntityModificationData> = class {
20
20
  static id = 'removeEntity'
21
+
21
22
  constructor(
22
23
  private readonly data: RemoveEntityModificationData,
23
24
  private readonly schema: Schema,
24
- private readonly formatVersion: number,
25
+ private readonly options: ModificationHandlerOptions,
25
26
  ) {}
26
27
 
27
28
  public createSql(builder: MigrationBuilder): void {
@@ -30,9 +31,9 @@ export const RemoveEntityModification: ModificationHandlerStatic<RemoveEntityMod
30
31
  builder.dropView(entity.tableName)
31
32
  return
32
33
  }
33
- if (this.formatVersion >= VERSION_REMOVE_REFERENCING_RELATIONS) {
34
+ if (this.options.formatVersion >= VERSION_REMOVE_REFERENCING_RELATIONS) {
34
35
  this.getFieldsToRemove(this.schema).forEach(([entityName, fieldName]) => {
35
- (new RemoveFieldModification({ entityName, fieldName }, this.schema, this.formatVersion)).createSql(builder)
36
+ (new RemoveFieldModification({ entityName, fieldName }, this.schema, this.options)).createSql(builder)
36
37
  })
37
38
  }
38
39
  builder.dropTable(entity.tableName)
@@ -40,7 +41,7 @@ export const RemoveEntityModification: ModificationHandlerStatic<RemoveEntityMod
40
41
 
41
42
  public getSchemaUpdater(): SchemaUpdater {
42
43
  return updateSchema(
43
- this.formatVersion >= VERSION_ACL_PATCH
44
+ this.options.formatVersion >= VERSION_ACL_PATCH
44
45
  ? updateAcl(
45
46
  updateAclEveryRole(
46
47
  ({ role }) => ({
@@ -72,11 +73,11 @@ export const RemoveEntityModification: ModificationHandlerStatic<RemoveEntityMod
72
73
  ),
73
74
  )
74
75
  : undefined,
75
- this.formatVersion >= VERSION_REMOVE_REFERENCING_RELATIONS
76
+ this.options.formatVersion >= VERSION_REMOVE_REFERENCING_RELATIONS
76
77
  ? ({ schema }) => {
77
78
  const fieldsToRemove = this.getFieldsToRemove(schema)
78
79
  return fieldsToRemove.reduce(
79
- (schema, [entity, field]) => removeField(entity, field, this.formatVersion)({ schema }),
80
+ (schema, [entity, field]) => removeField(entity, field, this.options.formatVersion)({ schema }),
80
81
  schema,
81
82
  )
82
83
  }
@@ -11,7 +11,7 @@ import {
11
11
  updateModel,
12
12
  updateSchema,
13
13
  } from '../utils/schemaUpdateUtils'
14
- import { ModificationHandler, ModificationHandlerStatic } from '../ModificationHandler'
14
+ import { ModificationHandler, ModificationHandlerOptions, ModificationHandlerStatic } from '../ModificationHandler'
15
15
  import { isIt } from '../../utils/isIt'
16
16
  import { VERSION_ACL_PATCH, VERSION_UPDATE_CONSTRAINT_NAME } from '../ModificationVersions'
17
17
  import { NamingHelper } from '@contember/schema-utils'
@@ -27,20 +27,20 @@ export const UpdateEntityNameModification: ModificationHandlerStatic<UpdateEntit
27
27
  constructor(
28
28
  private readonly data: UpdateEntityNameModificationData,
29
29
  private readonly schema: Schema,
30
- private readonly formatVersion: number,
30
+ private readonly options: ModificationHandlerOptions,
31
31
  ) {
32
32
  this.subModification = data.tableName
33
33
  ? new UpdateEntityTableNameModification(
34
34
  { entityName: data.entityName, tableName: data.tableName },
35
35
  schema,
36
- this.formatVersion,
36
+ this.options,
37
37
  )
38
38
  : new NoopModification()
39
39
  }
40
40
 
41
41
  public createSql(builder: MigrationBuilder): void {
42
42
  const entity = this.schema.model.entities[this.data.entityName]
43
- if (!entity.view && this.formatVersion >= VERSION_UPDATE_CONSTRAINT_NAME) {
43
+ if (!entity.view && this.options.formatVersion >= VERSION_UPDATE_CONSTRAINT_NAME) {
44
44
  renameConstraintsSqlBuilder(builder, entity, this.getNewConstraintName.bind(this))
45
45
  }
46
46
  this.subModification.createSql(builder)
@@ -58,7 +58,7 @@ export const UpdateEntityNameModification: ModificationHandlerStatic<UpdateEntit
58
58
  return field
59
59
  }),
60
60
  ),
61
- this.formatVersion >= VERSION_UPDATE_CONSTRAINT_NAME
61
+ this.options.formatVersion >= VERSION_UPDATE_CONSTRAINT_NAME
62
62
  ? updateEntity(this.data.entityName, renameConstraintSchemaUpdater(this.getNewConstraintName.bind(this)))
63
63
  : undefined,
64
64
  ({ model }) => {
@@ -76,7 +76,7 @@ export const UpdateEntityNameModification: ModificationHandlerStatic<UpdateEntit
76
76
  }
77
77
  },
78
78
  ),
79
- this.formatVersion >= VERSION_ACL_PATCH
79
+ this.options.formatVersion >= VERSION_ACL_PATCH
80
80
  ? updateAcl(
81
81
  updateAclEveryRole(
82
82
  ({ role }) => ({
@@ -2,15 +2,16 @@ import { acceptFieldVisitor, isInverseRelation, isRelation, NamingHelper } from
2
2
  import { MigrationBuilder } from '@contember/database-migrations'
3
3
  import { Schema } from '@contember/schema'
4
4
  import { removeField, SchemaUpdater } from '../utils/schemaUpdateUtils'
5
- import { ModificationHandlerStatic } from '../ModificationHandler'
5
+ import { ModificationHandlerOptions, ModificationHandlerStatic } from '../ModificationHandler'
6
6
  import { isDefined } from '../../utils/isDefined'
7
7
 
8
8
  export const RemoveFieldModification: ModificationHandlerStatic<RemoveFieldModificationData> = class {
9
9
  static id = 'removeField'
10
+
10
11
  constructor(
11
12
  private readonly data: RemoveFieldModificationData,
12
13
  private readonly schema: Schema,
13
- private readonly formatVersion: number,
14
+ private readonly options: ModificationHandlerOptions,
14
15
  ) {}
15
16
 
16
17
  public createSql(builder: MigrationBuilder): void {
@@ -39,7 +40,7 @@ export const RemoveFieldModification: ModificationHandlerStatic<RemoveFieldModif
39
40
  }
40
41
 
41
42
  public getSchemaUpdater(): SchemaUpdater {
42
- return removeField(this.data.entityName, this.data.fieldName, this.formatVersion)
43
+ return removeField(this.data.entityName, this.data.fieldName, this.options.formatVersion)
43
44
  }
44
45
 
45
46
  describe() {
@@ -13,7 +13,7 @@ import {
13
13
  updateModel,
14
14
  updateSchema,
15
15
  } from '../utils/schemaUpdateUtils'
16
- import { ModificationHandlerStatic } from '../ModificationHandler'
16
+ import { ModificationHandlerOptions, ModificationHandlerStatic } from '../ModificationHandler'
17
17
  import { acceptFieldVisitor, NamingHelper, PredicateDefinitionProcessor } from '@contember/schema-utils'
18
18
  import { VERSION_ACL_PATCH, VERSION_UPDATE_CONSTRAINT_NAME } from '../ModificationVersions'
19
19
  import { renameConstraintSchemaUpdater, renameConstraintsSqlBuilder } from '../utils/renameConstraintsHelper'
@@ -24,7 +24,7 @@ export const UpdateFieldNameModification: ModificationHandlerStatic<UpdateFieldN
24
24
  constructor(
25
25
  private readonly data: UpdateFieldNameModificationData,
26
26
  private readonly schema: Schema,
27
- private readonly formatVersion: number,
27
+ private readonly options: ModificationHandlerOptions,
28
28
  ) {}
29
29
 
30
30
  public createSql(builder: MigrationBuilder): void {
@@ -32,18 +32,18 @@ export const UpdateFieldNameModification: ModificationHandlerStatic<UpdateFieldN
32
32
  if (entity.view) {
33
33
  return
34
34
  }
35
- if (this.formatVersion >= VERSION_UPDATE_CONSTRAINT_NAME) {
35
+ if (this.options.formatVersion >= VERSION_UPDATE_CONSTRAINT_NAME) {
36
36
  renameConstraintsSqlBuilder(builder, entity, this.getNewConstraintName.bind(this))
37
37
  }
38
38
  }
39
39
 
40
40
  public getSchemaUpdater(): SchemaUpdater {
41
41
  const updateConstraintName =
42
- this.formatVersion >= VERSION_UPDATE_CONSTRAINT_NAME
42
+ this.options.formatVersion >= VERSION_UPDATE_CONSTRAINT_NAME
43
43
  ? updateEntity(this.data.entityName, renameConstraintSchemaUpdater(this.getNewConstraintName.bind(this)))
44
44
  : undefined
45
45
  const updateConstraintFields =
46
- this.formatVersion >= VERSION_UPDATE_CONSTRAINT_NAME
46
+ this.options.formatVersion >= VERSION_UPDATE_CONSTRAINT_NAME
47
47
  ? updateEntity(this.data.entityName, ({ entity }) => {
48
48
  return {
49
49
  ...entity,
@@ -112,7 +112,7 @@ export const UpdateFieldNameModification: ModificationHandlerStatic<UpdateFieldN
112
112
  }
113
113
  })
114
114
  const updateAclOp =
115
- this.formatVersion >= VERSION_ACL_PATCH
115
+ this.options.formatVersion >= VERSION_ACL_PATCH
116
116
  ? updateAcl(
117
117
  updateAclEveryRole(
118
118
  updateAclEveryEntity(
@@ -0,0 +1,109 @@
1
+ import { MigrationBuilder } from '@contember/database-migrations'
2
+ import { Model, Schema } from '@contember/schema'
3
+ import { addField, SchemaUpdater, updateEntity, updateModel, updateSchema } from '../utils/schemaUpdateUtils'
4
+ import { ModificationHandler, ModificationHandlerOptions, ModificationHandlerStatic } from '../ModificationHandler'
5
+ import { isInverseRelation, isOwningRelation } from '@contember/schema-utils'
6
+ import { updateRelations } from '../utils/diffUtils'
7
+ import { UpdateFieldNameModification } from '../fields'
8
+ import { createJunctionTableSql } from '../utils/createJunctionTable'
9
+ import { wrapIdentifier } from '@contember/database'
10
+
11
+ export const ConvertOneHasManyToManyHasManyRelationModification: ModificationHandlerStatic<ConvertOneHasManyToManyHasManyRelationModificationData> = class {
12
+ static id = 'convertOneHasManyToManyHasManyRelation'
13
+ private subModification: ModificationHandler<any>
14
+
15
+ constructor(
16
+ private readonly data: ConvertOneHasManyToManyHasManyRelationModificationData,
17
+ private readonly schema: Schema,
18
+ private readonly options: ModificationHandlerOptions,
19
+ ) {
20
+ this.subModification = new UpdateFieldNameModification(
21
+ {
22
+ entityName: this.data.entityName,
23
+ fieldName: this.data.fieldName,
24
+ newFieldName: this.data.owningSide.name,
25
+ },
26
+ schema,
27
+ this.options,
28
+ )
29
+ }
30
+
31
+ public createSql(builder: MigrationBuilder): void {
32
+ const targetEntity = this.schema.model.entities[this.data.owningSide.target]
33
+ const { relation: oldRelation } = this.getRelation()
34
+ const entity = this.schema.model.entities[this.data.entityName]
35
+
36
+ createJunctionTableSql(builder, this.options.systemSchema, entity, targetEntity, this.data.owningSide)
37
+ const joiningTable = this.data.owningSide.joiningTable
38
+ builder.sql(`
39
+ INSERT INTO ${wrapIdentifier(joiningTable.tableName)} (
40
+ ${wrapIdentifier(joiningTable.joiningColumn.columnName)},
41
+ ${wrapIdentifier(joiningTable.inverseJoiningColumn.columnName)}
42
+ )
43
+ SELECT ${wrapIdentifier(entity.primaryColumn)}, ${wrapIdentifier(oldRelation.joiningColumn.columnName)}
44
+ FROM ${wrapIdentifier(entity.tableName)}
45
+ WHERE ${wrapIdentifier(oldRelation.joiningColumn.columnName)} IS NOT NULL`)
46
+
47
+ builder.dropColumn(entity.tableName, oldRelation.joiningColumn.columnName)
48
+ }
49
+
50
+ public getSchemaUpdater(): SchemaUpdater {
51
+ const { entityName } = this.data
52
+ return updateSchema(
53
+ this.subModification.getSchemaUpdater(),
54
+ updateModel(updateEntity(entityName, addField(this.data.owningSide))),
55
+ this.data.inverseSide ? updateModel(updateEntity(this.data.owningSide.target, addField(this.data.inverseSide))) : undefined,
56
+ )
57
+ }
58
+
59
+ describe() {
60
+ return {
61
+ message: `Converts OneHasMany relation to ManyHasMany on ${this.data.entityName}.${this.data.fieldName}`,
62
+ }
63
+ }
64
+
65
+ static createModification(data: ConvertOneHasManyToManyHasManyRelationModificationData) {
66
+ return { modification: this.id, ...data }
67
+ }
68
+
69
+ static createDiff(originalSchema: Schema, updatedSchema: Schema) {
70
+ return updateRelations(originalSchema, updatedSchema, ({ originalRelation, updatedRelation, updatedEntity }) => {
71
+ if (
72
+ isInverseRelation(originalRelation) &&
73
+ isInverseRelation(updatedRelation) &&
74
+ originalRelation.type === Model.RelationType.OneHasMany &&
75
+ updatedRelation.type === Model.RelationType.ManyHasMany
76
+ ) {
77
+ const owningOldName = originalRelation.ownedBy
78
+ const owningNewName = updatedRelation.ownedBy
79
+ const owningSide = updatedSchema.model.entities[updatedRelation.target].fields[owningNewName]
80
+ if (owningSide.type !== Model.RelationType.ManyHasMany || !isOwningRelation(owningSide)) {
81
+ throw new Error()
82
+ }
83
+ return ConvertOneHasManyToManyHasManyRelationModification.createModification({
84
+ entityName: updatedRelation.target,
85
+ fieldName: owningOldName,
86
+ owningSide,
87
+ inverseSide: updatedRelation,
88
+ })
89
+ }
90
+ return undefined
91
+ })
92
+ }
93
+
94
+ private getRelation(): { entity: Model.Entity; relation: Model.ManyHasOneRelation } {
95
+ const entity = this.schema.model.entities[this.data.entityName]
96
+ const relation = entity.fields[this.data.fieldName]
97
+ if (relation.type !== Model.RelationType.ManyHasOne) {
98
+ throw new Error()
99
+ }
100
+ return { entity, relation }
101
+ }
102
+ }
103
+
104
+ export interface ConvertOneHasManyToManyHasManyRelationModificationData {
105
+ entityName: string
106
+ fieldName: string
107
+ owningSide: Model.ManyHasManyOwningRelation
108
+ inverseSide?: Model.ManyHasManyInverseRelation
109
+ }
@@ -1,7 +1,7 @@
1
1
  import { MigrationBuilder } from '@contember/database-migrations'
2
2
  import { Model, Schema } from '@contember/schema'
3
3
  import { SchemaUpdater, updateEntity, updateField, updateModel, updateSchema } from '../utils/schemaUpdateUtils'
4
- import { ModificationHandler, ModificationHandlerStatic } from '../ModificationHandler'
4
+ import { ModificationHandler, ModificationHandlerOptions, ModificationHandlerStatic } from '../ModificationHandler'
5
5
  import { isOwningRelation, NamingHelper } from '@contember/schema-utils'
6
6
  import { updateRelations } from '../utils/diffUtils'
7
7
  import { UpdateFieldNameModification } from '../fields'
@@ -11,7 +11,11 @@ export const ConvertOneToManyRelationModification: ModificationHandlerStatic<Con
11
11
  static id = 'convertOneToManyRelation'
12
12
  private subModification: ModificationHandler<any>
13
13
 
14
- constructor(private readonly data: ConvertOneToManyRelationModificationData, private readonly schema: Schema, private readonly formatVersion: number) {
14
+ constructor(
15
+ private readonly data: ConvertOneToManyRelationModificationData,
16
+ private readonly schema: Schema,
17
+ private readonly options: ModificationHandlerOptions,
18
+ ) {
15
19
  const { relation } = this.getRelation()
16
20
  this.subModification = data.newInverseSideFieldName && relation.inversedBy ?
17
21
  new UpdateFieldNameModification(
@@ -21,7 +25,7 @@ export const ConvertOneToManyRelationModification: ModificationHandlerStatic<Con
21
25
  newFieldName: data.newInverseSideFieldName,
22
26
  },
23
27
  schema,
24
- formatVersion,
28
+ this.options,
25
29
  )
26
30
  : new NoopModification()
27
31
  }
@@ -8,20 +8,21 @@ import {
8
8
  } from '@contember/schema-utils'
9
9
  import { MigrationBuilder } from '@contember/database-migrations'
10
10
  import { addField, SchemaUpdater, updateEntity, updateModel } from '../utils/schemaUpdateUtils'
11
- import { ModificationHandlerStatic } from '../ModificationHandler'
12
- import { createEventTrigger, createEventTrxTrigger } from '../utils/sqlUpdateUtils'
11
+ import { ModificationHandlerOptions, ModificationHandlerStatic } from '../ModificationHandler'
13
12
  import { isIt } from '../../utils/isIt'
14
13
  import { createFields } from '../utils/diffUtils'
14
+ import { getPrimaryColumnType } from '../utils/getPrimaryColumnType'
15
+ import { createJunctionTableSql } from '../utils/createJunctionTable'
15
16
 
16
- const getPrimaryType = (entity: Model.Entity): string => {
17
- const column = entity.fields[entity.primary] as Model.AnyColumn
18
- return column.columnType
19
- }
20
17
 
21
18
  export const CreateRelationModification: ModificationHandlerStatic<CreateRelationModificationData> = class {
22
19
  static id = 'createRelation'
23
20
 
24
- constructor(private readonly data: CreateRelationModificationData, private readonly schema: Schema) {}
21
+ constructor(
22
+ private readonly data: CreateRelationModificationData,
23
+ private readonly schema: Schema,
24
+ private readonly options: ModificationHandlerOptions,
25
+ ) {}
25
26
 
26
27
  public createSql(builder: MigrationBuilder): void {
27
28
  const entity = this.schema.model.entities[this.data.entityName]
@@ -32,7 +33,7 @@ export const CreateRelationModification: ModificationHandlerStatic<CreateRelatio
32
33
  const createOwningSide = (relation: Model.OneHasOneOwningRelation | Model.ManyHasOneRelation) => {
33
34
  builder.addColumn(entity.tableName, {
34
35
  [relation.joiningColumn.columnName]: {
35
- type: getPrimaryType(targetEntity),
36
+ type: getPrimaryColumnType(targetEntity),
36
37
  notNull: !relation.nullable,
37
38
  },
38
39
  })
@@ -65,34 +66,7 @@ export const CreateRelationModification: ModificationHandlerStatic<CreateRelatio
65
66
  },
66
67
  visitOneHasOneInverse: () => {},
67
68
  visitManyHasManyOwning: ({}, relation, {}, _) => {
68
- const primaryColumns = [
69
- relation.joiningTable.joiningColumn.columnName,
70
- relation.joiningTable.inverseJoiningColumn.columnName,
71
- ]
72
- builder.createTable(
73
- relation.joiningTable.tableName,
74
- {
75
- [relation.joiningTable.joiningColumn.columnName]: {
76
- type: getPrimaryType(entity),
77
- notNull: true,
78
- references: `"${entity.tableName}"("${entity.primaryColumn}")`,
79
- onDelete: 'CASCADE',
80
- },
81
- [relation.joiningTable.inverseJoiningColumn.columnName]: {
82
- type: getPrimaryType(targetEntity),
83
- notNull: true,
84
- references: `"${targetEntity.tableName}"("${targetEntity.primaryColumn}")`,
85
- onDelete: 'CASCADE',
86
- },
87
- },
88
- {
89
- constraints: {
90
- primaryKey: primaryColumns,
91
- },
92
- },
93
- )
94
- createEventTrigger(builder, relation.joiningTable.tableName, primaryColumns)
95
- createEventTrxTrigger(builder, relation.joiningTable.tableName)
69
+ createJunctionTableSql(builder, this.options.systemSchema, entity, targetEntity, relation)
96
70
  },
97
71
  visitManyHasManyInverse: () => {},
98
72
  })
@@ -1,3 +1,4 @@
1
+ export * from './ConvertOneHasManyToManyHasManyRelationModification'
1
2
  export * from './ConvertOneToManyRelationModification'
2
3
  export * from './CreateRelationInverseSideModification'
3
4
  export * from './CreateRelationModification'
@@ -0,0 +1,41 @@
1
+ import { createEventTrigger, createEventTrxTrigger } from './sqlUpdateUtils'
2
+ import { MigrationBuilder } from '@contember/database-migrations'
3
+ import { Model } from '@contember/schema'
4
+ import { getPrimaryColumnType } from './getPrimaryColumnType'
5
+
6
+ export const createJunctionTableSql = (
7
+ builder: MigrationBuilder,
8
+ systemSchema: string,
9
+ entity: Model.Entity,
10
+ targetEntity: Model.Entity,
11
+ relation: Model.ManyHasManyOwningRelation,
12
+ ) => {
13
+ const primaryColumns = [
14
+ relation.joiningTable.joiningColumn.columnName,
15
+ relation.joiningTable.inverseJoiningColumn.columnName,
16
+ ]
17
+ builder.createTable(
18
+ relation.joiningTable.tableName,
19
+ {
20
+ [relation.joiningTable.joiningColumn.columnName]: {
21
+ type: getPrimaryColumnType(entity),
22
+ notNull: true,
23
+ references: `"${entity.tableName}"("${entity.primaryColumn}")`,
24
+ onDelete: 'CASCADE',
25
+ },
26
+ [relation.joiningTable.inverseJoiningColumn.columnName]: {
27
+ type: getPrimaryColumnType(targetEntity),
28
+ notNull: true,
29
+ references: `"${targetEntity.tableName}"("${targetEntity.primaryColumn}")`,
30
+ onDelete: 'CASCADE',
31
+ },
32
+ },
33
+ {
34
+ constraints: {
35
+ primaryKey: primaryColumns,
36
+ },
37
+ },
38
+ )
39
+ createEventTrigger(builder, systemSchema, relation.joiningTable.tableName, primaryColumns)
40
+ createEventTrxTrigger(builder, systemSchema, relation.joiningTable.tableName)
41
+ }
@@ -0,0 +1,6 @@
1
+ import { Model } from '@contember/schema'
2
+
3
+ export const getPrimaryColumnType = (entity: Model.Entity): string => {
4
+ const column = entity.fields[entity.primary] as Model.AnyColumn
5
+ return column.columnType
6
+ }
@@ -1,19 +1,19 @@
1
1
  import { MigrationBuilder, Name } from '@contember/database-migrations'
2
2
 
3
- export const createEventTrigger = (builder: MigrationBuilder, tableName: Name, primaryColumns: string[]) => {
3
+ export const createEventTrigger = (builder: MigrationBuilder, systemSchema: string, tableName: Name, primaryColumns: string[]) => {
4
4
  builder.createTrigger(tableName, 'log_event', {
5
5
  when: 'AFTER',
6
6
  operation: ['INSERT', 'UPDATE', 'DELETE'],
7
7
  level: 'ROW',
8
8
  function: {
9
- schema: 'system',
9
+ schema: systemSchema,
10
10
  name: 'trigger_event',
11
11
  },
12
12
  functionParams: primaryColumns,
13
13
  })
14
14
  }
15
15
 
16
- export const createEventTrxTrigger = (builder: MigrationBuilder, tableName: Name) => {
16
+ export const createEventTrxTrigger = (builder: MigrationBuilder, systemSchema: string, tableName: Name) => {
17
17
  builder.createTrigger(tableName, 'log_event_trx', {
18
18
  when: 'AFTER',
19
19
  operation: ['INSERT', 'UPDATE', 'DELETE'],
@@ -22,7 +22,7 @@ export const createEventTrxTrigger = (builder: MigrationBuilder, tableName: Name
22
22
  deferrable: true,
23
23
  deferred: true,
24
24
  function: {
25
- schema: 'system',
25
+ schema: systemSchema,
26
26
  name: 'trigger_event_commit',
27
27
  },
28
28
  })
@@ -0,0 +1,81 @@
1
+ import { testMigrations } from '../../src/tests'
2
+ import { SQL } from '../../src/tags'
3
+ import { SchemaDefinition as def } from '@contember/schema-definition'
4
+
5
+ namespace ConvertOHMToMHMSchemaOrig {
6
+ export class Article {
7
+ title = def.stringColumn()
8
+ category = def.manyHasOne(Category, 'articles')
9
+ }
10
+
11
+ export class Category {
12
+ articles = def.oneHasMany(Article, 'category')
13
+ }
14
+ }
15
+
16
+ namespace ConvertOHMToMHMSchemaOrigUpdated {
17
+ export class Article {
18
+ title = def.stringColumn()
19
+ categories = def.manyHasMany(Category, 'articles')
20
+ }
21
+
22
+ export class Category {
23
+ articles = def.manyHasManyInverse(Article, 'categories')
24
+ }
25
+ }
26
+
27
+
28
+ testMigrations('convert one has many to many has many', {
29
+ originalSchema: def.createModel(ConvertOHMToMHMSchemaOrig),
30
+ updatedSchema: def.createModel(ConvertOHMToMHMSchemaOrigUpdated),
31
+ diff: [
32
+ {
33
+ modification: 'convertOneHasManyToManyHasManyRelation',
34
+ entityName: 'Article',
35
+ fieldName: 'category',
36
+ owningSide: {
37
+ type: 'ManyHasMany',
38
+ name: 'categories',
39
+ inversedBy: 'articles',
40
+ target: 'Category',
41
+ joiningTable: {
42
+ tableName: 'article_categories',
43
+ joiningColumn: {
44
+ columnName: 'article_id',
45
+ onDelete: 'cascade',
46
+ },
47
+ inverseJoiningColumn: {
48
+ columnName: 'category_id',
49
+ onDelete: 'cascade',
50
+ },
51
+ },
52
+ },
53
+ inverseSide: {
54
+ name: 'articles',
55
+ ownedBy: 'categories',
56
+ target: 'Article',
57
+ type: 'ManyHasMany',
58
+ },
59
+ },
60
+ ],
61
+ sql: SQL`
62
+ CREATE TABLE "article_categories" (
63
+ "article_id" uuid NOT NULL REFERENCES "article"("id") ON DELETE CASCADE,
64
+ "category_id" uuid NOT NULL REFERENCES "category"("id") ON DELETE CASCADE, CONSTRAINT
65
+ "article_categories_pkey" PRIMARY KEY ("article_id", "category_id")
66
+ );
67
+
68
+ CREATE TRIGGER "log_event" AFTER INSERT OR UPDATE OR DELETE
69
+ ON "article_categories" FOR EACH ROW
70
+ EXECUTE PROCEDURE "system"."trigger_event"($pga$article_id$pga$, $pga$category_id$pga$);
71
+
72
+ CREATE CONSTRAINT TRIGGER "log_event_trx" AFTER INSERT OR UPDATE OR DELETE
73
+ ON "article_categories" DEFERRABLE INITIALLY DEFERRED FOR EACH ROW
74
+ EXECUTE PROCEDURE "system"."trigger_event_commit"();
75
+
76
+ INSERT INTO "article_categories" ( "article_id", "category_id" )
77
+ SELECT "id", "category_id" FROM "article" WHERE "category_id" IS NOT NULL;
78
+
79
+ ALTER TABLE "article" DROP "category_id";
80
+ `,
81
+ })
@@ -29,5 +29,5 @@ testMigrations('update column definition', {
29
29
  },
30
30
  ],
31
31
  sql: SQL`ALTER TABLE "author"
32
- ALTER "registered_at" SET DATA TYPE timestamptz;`,
32
+ ALTER "registered_at" SET DATA TYPE timestamptz USING "registered_at"::timestamptz;`,
33
33
  })
@@ -69,7 +69,7 @@ export function testGenerateSql(originalSchema: Model.Schema, diff: Migration.Mo
69
69
  let schema = { model: originalSchema, acl: emptyAcl, validation: {} }
70
70
  const builder = createMigrationBuilder()
71
71
  for (let { modification, ...data } of diff) {
72
- const modificationHandler = modificationFactory.create(modification, data, schema, VERSION_LATEST)
72
+ const modificationHandler = modificationFactory.create(modification, data, schema, { formatVersion: VERSION_LATEST, systemSchema: 'system' })
73
73
  modificationHandler.createSql(builder)
74
74
  schema = modificationHandler.getSchemaUpdater()({ schema })
75
75
  }