@contember/schema-migrations 1.2.0-alpha.9 → 1.2.0-beta.1

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 (188) hide show
  1. package/dist/src/Migration.d.ts +2 -0
  2. package/dist/src/Migration.d.ts.map +1 -1
  3. package/dist/src/Migration.js +2 -2
  4. package/dist/src/Migration.js.map +1 -1
  5. package/dist/src/MigrationCreator.d.ts +3 -1
  6. package/dist/src/MigrationCreator.d.ts.map +1 -1
  7. package/dist/src/MigrationCreator.js +2 -2
  8. package/dist/src/MigrationCreator.js.map +1 -1
  9. package/dist/src/MigrationFilesManager.d.ts.map +1 -1
  10. package/dist/src/MigrationFilesManager.js +10 -19
  11. package/dist/src/MigrationFilesManager.js.map +1 -1
  12. package/dist/src/MigrationsResolver.d.ts.map +1 -1
  13. package/dist/src/MigrationsResolver.js +1 -0
  14. package/dist/src/MigrationsResolver.js.map +1 -1
  15. package/dist/src/SchemaDiffer.d.ts +6 -1
  16. package/dist/src/SchemaDiffer.d.ts.map +1 -1
  17. package/dist/src/SchemaDiffer.js +12 -7
  18. package/dist/src/SchemaDiffer.js.map +1 -1
  19. package/dist/src/SchemaMigrator.d.ts +2 -1
  20. package/dist/src/SchemaMigrator.d.ts.map +1 -1
  21. package/dist/src/SchemaMigrator.js.map +1 -1
  22. package/dist/src/modifications/ModificationHandler.d.ts +7 -4
  23. package/dist/src/modifications/ModificationHandler.d.ts.map +1 -1
  24. package/dist/src/modifications/ModificationHandler.js.map +1 -1
  25. package/dist/src/modifications/ModificationHandlerFactory.d.ts +2 -2
  26. package/dist/src/modifications/ModificationHandlerFactory.d.ts.map +1 -1
  27. package/dist/src/modifications/ModificationHandlerFactory.js +2 -0
  28. package/dist/src/modifications/ModificationHandlerFactory.js.map +1 -1
  29. package/dist/src/modifications/columns/CreateColumnModification.d.ts +1 -3
  30. package/dist/src/modifications/columns/CreateColumnModification.d.ts.map +1 -1
  31. package/dist/src/modifications/columns/CreateColumnModification.js +3 -3
  32. package/dist/src/modifications/columns/CreateColumnModification.js.map +1 -1
  33. package/dist/src/modifications/columns/UpdateColumnDefinitionModification.d.ts +3 -5
  34. package/dist/src/modifications/columns/UpdateColumnDefinitionModification.d.ts.map +1 -1
  35. package/dist/src/modifications/columns/UpdateColumnDefinitionModification.js +24 -20
  36. package/dist/src/modifications/columns/UpdateColumnDefinitionModification.js.map +1 -1
  37. package/dist/src/modifications/columns/UpdateColumnNameModification.d.ts +1 -3
  38. package/dist/src/modifications/columns/UpdateColumnNameModification.d.ts.map +1 -1
  39. package/dist/src/modifications/columns/UpdateColumnNameModification.js +7 -7
  40. package/dist/src/modifications/columns/UpdateColumnNameModification.js.map +1 -1
  41. package/dist/src/modifications/constraints/CreateUniqueConstraintModification.js +2 -2
  42. package/dist/src/modifications/constraints/CreateUniqueConstraintModification.js.map +1 -1
  43. package/dist/src/modifications/differs/RemoveChangedFieldDiffer.d.ts +1 -3
  44. package/dist/src/modifications/differs/RemoveChangedFieldDiffer.d.ts.map +1 -1
  45. package/dist/src/modifications/differs/RemoveChangedViewDiffer.d.ts +1 -2
  46. package/dist/src/modifications/differs/RemoveChangedViewDiffer.d.ts.map +1 -1
  47. package/dist/src/modifications/differs/RemoveChangedViewDiffer.js +7 -20
  48. package/dist/src/modifications/differs/RemoveChangedViewDiffer.js.map +1 -1
  49. package/dist/src/modifications/entities/CreateEntityModification.d.ts.map +1 -1
  50. package/dist/src/modifications/entities/CreateEntityModification.js +2 -2
  51. package/dist/src/modifications/entities/CreateEntityModification.js.map +1 -1
  52. package/dist/src/modifications/entities/RemoveEntityModification.d.ts +2 -1
  53. package/dist/src/modifications/entities/RemoveEntityModification.d.ts.map +1 -1
  54. package/dist/src/modifications/entities/RemoveEntityModification.js.map +1 -1
  55. package/dist/src/modifications/entities/UpdateEntityNameModification.d.ts +2 -2
  56. package/dist/src/modifications/entities/UpdateEntityNameModification.d.ts.map +1 -1
  57. package/dist/src/modifications/entities/UpdateEntityNameModification.js.map +1 -1
  58. package/dist/src/modifications/fields/RemoveFieldModification.js +4 -4
  59. package/dist/src/modifications/fields/RemoveFieldModification.js.map +1 -1
  60. package/dist/src/modifications/fields/UpdateFieldNameModification.d.ts +2 -2
  61. package/dist/src/modifications/fields/UpdateFieldNameModification.d.ts.map +1 -1
  62. package/dist/src/modifications/fields/UpdateFieldNameModification.js +7 -7
  63. package/dist/src/modifications/fields/UpdateFieldNameModification.js.map +1 -1
  64. package/dist/src/modifications/indexes/CreateIndexModification.js +3 -3
  65. package/dist/src/modifications/indexes/CreateIndexModification.js.map +1 -1
  66. package/dist/src/modifications/relations/ConvertOneHasManyToManyHasManyRelationModification.d.ts +3 -4
  67. package/dist/src/modifications/relations/ConvertOneHasManyToManyHasManyRelationModification.d.ts.map +1 -1
  68. package/dist/src/modifications/relations/ConvertOneHasManyToManyHasManyRelationModification.js +2 -2
  69. package/dist/src/modifications/relations/ConvertOneHasManyToManyHasManyRelationModification.js.map +1 -1
  70. package/dist/src/modifications/relations/ConvertOneToManyRelationModification.d.ts +3 -4
  71. package/dist/src/modifications/relations/ConvertOneToManyRelationModification.d.ts.map +1 -1
  72. package/dist/src/modifications/relations/ConvertOneToManyRelationModification.js +10 -2
  73. package/dist/src/modifications/relations/ConvertOneToManyRelationModification.js.map +1 -1
  74. package/dist/src/modifications/relations/CreateRelationInverseSideModification.d.ts +1 -3
  75. package/dist/src/modifications/relations/CreateRelationInverseSideModification.d.ts.map +1 -1
  76. package/dist/src/modifications/relations/CreateRelationModification.d.ts +3 -4
  77. package/dist/src/modifications/relations/CreateRelationModification.d.ts.map +1 -1
  78. package/dist/src/modifications/relations/CreateRelationModification.js +17 -6
  79. package/dist/src/modifications/relations/CreateRelationModification.js.map +1 -1
  80. package/dist/src/modifications/relations/DisableOrphanRemovalModification.d.ts +1 -3
  81. package/dist/src/modifications/relations/DisableOrphanRemovalModification.d.ts.map +1 -1
  82. package/dist/src/modifications/relations/EnableOrphanRemovalModification.d.ts +1 -3
  83. package/dist/src/modifications/relations/EnableOrphanRemovalModification.d.ts.map +1 -1
  84. package/dist/src/modifications/relations/MakeRelationNotNullModification.d.ts +1 -3
  85. package/dist/src/modifications/relations/MakeRelationNotNullModification.d.ts.map +1 -1
  86. package/dist/src/modifications/relations/MakeRelationNullableModification.d.ts +1 -3
  87. package/dist/src/modifications/relations/MakeRelationNullableModification.d.ts.map +1 -1
  88. package/dist/src/modifications/relations/ToggleJunctionEventLogModification.d.ts +1 -3
  89. package/dist/src/modifications/relations/ToggleJunctionEventLogModification.d.ts.map +1 -1
  90. package/dist/src/modifications/relations/UpdateRelationOnDeleteModification.d.ts +1 -3
  91. package/dist/src/modifications/relations/UpdateRelationOnDeleteModification.d.ts.map +1 -1
  92. package/dist/src/modifications/relations/UpdateRelationOrderByModification.d.ts +1 -3
  93. package/dist/src/modifications/relations/UpdateRelationOrderByModification.d.ts.map +1 -1
  94. package/dist/src/modifications/settings/UpdateSettingsModification.d.ts +28 -0
  95. package/dist/src/modifications/settings/UpdateSettingsModification.d.ts.map +1 -0
  96. package/dist/src/modifications/settings/UpdateSettingsModification.js +64 -0
  97. package/dist/src/modifications/settings/UpdateSettingsModification.js.map +1 -0
  98. package/dist/src/modifications/settings/index.d.ts +2 -0
  99. package/dist/src/modifications/settings/index.d.ts.map +1 -0
  100. package/dist/src/modifications/settings/index.js +18 -0
  101. package/dist/src/modifications/settings/index.js.map +1 -0
  102. package/dist/src/modifications/utils/columnUtils.d.ts +3 -0
  103. package/dist/src/modifications/utils/columnUtils.d.ts.map +1 -0
  104. package/dist/src/modifications/utils/columnUtils.js +9 -0
  105. package/dist/src/modifications/utils/columnUtils.js.map +1 -0
  106. package/dist/src/modifications/utils/createJunctionTable.d.ts +2 -1
  107. package/dist/src/modifications/utils/createJunctionTable.d.ts.map +1 -1
  108. package/dist/src/modifications/utils/createJunctionTable.js +7 -5
  109. package/dist/src/modifications/utils/createJunctionTable.js.map +1 -1
  110. package/dist/src/modifications/utils/diffUtils.d.ts +14 -12
  111. package/dist/src/modifications/utils/diffUtils.d.ts.map +1 -1
  112. package/dist/src/modifications/utils/diffUtils.js.map +1 -1
  113. package/dist/src/modifications/utils/schemaMeta.d.ts +8 -0
  114. package/dist/src/modifications/utils/schemaMeta.d.ts.map +1 -0
  115. package/dist/src/modifications/utils/schemaMeta.js +13 -0
  116. package/dist/src/modifications/utils/schemaMeta.js.map +1 -0
  117. package/dist/src/modifications/utils/schemaUpdateUtils.d.ts +4 -2
  118. package/dist/src/modifications/utils/schemaUpdateUtils.d.ts.map +1 -1
  119. package/dist/src/modifications/utils/schemaUpdateUtils.js +24 -2
  120. package/dist/src/modifications/utils/schemaUpdateUtils.js.map +1 -1
  121. package/dist/src/modifications/utils/viewDependencies.d.ts +4 -2
  122. package/dist/src/modifications/utils/viewDependencies.d.ts.map +1 -1
  123. package/dist/src/modifications/utils/viewDependencies.js +29 -6
  124. package/dist/src/modifications/utils/viewDependencies.js.map +1 -1
  125. package/dist/src/tsconfig.tsbuildinfo +1 -1
  126. package/dist/tests/cases/integration/convertOneHasManyToManyHasManyRelation.test.js +3 -2
  127. package/dist/tests/cases/integration/convertOneHasManyToManyHasManyRelation.test.js.map +1 -1
  128. package/dist/tests/cases/integration/createColumn.test.js +36 -0
  129. package/dist/tests/cases/integration/createColumn.test.js.map +1 -1
  130. package/dist/tests/cases/integration/createManyHasMany.test.js +3 -2
  131. package/dist/tests/cases/integration/createManyHasMany.test.js.map +1 -1
  132. package/dist/tests/cases/integration/createView.test.js +80 -0
  133. package/dist/tests/cases/integration/createView.test.js.map +1 -1
  134. package/dist/tests/cases/integration/removeField.test.js +72 -0
  135. package/dist/tests/cases/integration/removeField.test.js.map +1 -1
  136. package/dist/tests/cases/integration/takenIndexName.test.d.ts +2 -0
  137. package/dist/tests/cases/integration/takenIndexName.test.d.ts.map +1 -0
  138. package/dist/tests/cases/integration/takenIndexName.test.js +307 -0
  139. package/dist/tests/cases/integration/takenIndexName.test.js.map +1 -0
  140. package/dist/tests/cases/integration/updateColumnDefinition.test.js +160 -0
  141. package/dist/tests/cases/integration/updateColumnDefinition.test.js.map +1 -1
  142. package/dist/tests/scripts/checkCreateConsistentMigrations.js +5 -4
  143. package/dist/tests/scripts/checkCreateConsistentMigrations.js.map +1 -1
  144. package/dist/tests/src/tests.d.ts.map +1 -1
  145. package/dist/tests/src/tests.js +15 -5
  146. package/dist/tests/src/tests.js.map +1 -1
  147. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  148. package/package.json +6 -6
  149. package/src/Migration.ts +3 -1
  150. package/src/MigrationCreator.ts +2 -1
  151. package/src/MigrationFilesManager.ts +10 -20
  152. package/src/MigrationsResolver.ts +1 -0
  153. package/src/SchemaDiffer.ts +18 -7
  154. package/src/SchemaMigrator.ts +2 -1
  155. package/src/modifications/ModificationHandler.ts +5 -4
  156. package/src/modifications/ModificationHandlerFactory.ts +4 -1
  157. package/src/modifications/columns/CreateColumnModification.ts +3 -2
  158. package/src/modifications/columns/UpdateColumnDefinitionModification.ts +33 -24
  159. package/src/modifications/columns/UpdateColumnNameModification.ts +42 -42
  160. package/src/modifications/constraints/CreateUniqueConstraintModification.ts +2 -2
  161. package/src/modifications/differs/RemoveChangedViewDiffer.ts +9 -22
  162. package/src/modifications/entities/CreateEntityModification.ts +2 -1
  163. package/src/modifications/entities/RemoveEntityModification.ts +2 -1
  164. package/src/modifications/entities/UpdateEntityNameModification.ts +2 -1
  165. package/src/modifications/fields/RemoveFieldModification.ts +4 -4
  166. package/src/modifications/fields/UpdateFieldNameModification.ts +9 -8
  167. package/src/modifications/indexes/CreateIndexModification.ts +3 -3
  168. package/src/modifications/relations/ConvertOneHasManyToManyHasManyRelationModification.ts +13 -4
  169. package/src/modifications/relations/ConvertOneToManyRelationModification.ts +19 -3
  170. package/src/modifications/relations/CreateRelationModification.ts +33 -11
  171. package/src/modifications/settings/UpdateSettingsModification.ts +87 -0
  172. package/src/modifications/settings/index.ts +1 -0
  173. package/src/modifications/utils/columnUtils.ts +5 -0
  174. package/src/modifications/utils/createJunctionTable.ts +7 -5
  175. package/src/modifications/utils/diffUtils.ts +12 -12
  176. package/src/modifications/utils/schemaMeta.ts +18 -0
  177. package/src/modifications/utils/schemaUpdateUtils.ts +22 -2
  178. package/src/modifications/utils/viewDependencies.ts +28 -5
  179. package/src/tsconfig.json +2 -2
  180. package/tests/cases/integration/convertOneHasManyToManyHasManyRelation.test.ts +3 -2
  181. package/tests/cases/integration/createColumn.test.ts +33 -0
  182. package/tests/cases/integration/createManyHasMany.test.ts +3 -2
  183. package/tests/cases/integration/createView.test.ts +70 -0
  184. package/tests/cases/integration/removeField.test.ts +58 -0
  185. package/tests/cases/integration/takenIndexName.test.ts +314 -0
  186. package/tests/cases/integration/updateColumnDefinition.test.ts +138 -1
  187. package/tests/scripts/checkCreateConsistentMigrations.ts +3 -2
  188. package/tests/src/tests.ts +21 -11
@@ -1,38 +1,28 @@
1
1
  import { MigrationVersionHelper } from './MigrationVersionHelper'
2
- import * as fs from 'fs'
3
- import { promisify } from 'util'
4
- import * as path from 'path'
5
-
6
- const readFile = promisify(fs.readFile)
7
- const fsWrite = promisify(fs.writeFile)
8
- const fsRemove = promisify(fs.unlink)
9
- const fsRealpath = promisify(fs.realpath)
10
- const mkdir = promisify(fs.mkdir)
11
- const lstatFile = promisify(fs.lstat)
12
- const readDir = promisify(fs.readdir)
13
- const mvFile = promisify(fs.rename)
2
+ import * as fs from 'node:fs/promises'
3
+ import * as path from 'node:path'
14
4
 
15
5
  class MigrationFilesManager {
16
6
  constructor(public readonly directory: string) {}
17
7
 
18
8
  public async createFile(content: string, name: string): Promise<string> {
19
9
  const path = this.formatPath(name)
20
- await fsWrite(path, content, { encoding: 'utf8' })
21
- return await fsRealpath(path)
10
+ await fs.writeFile(path, content, { encoding: 'utf8' })
11
+ return await fs.realpath(path)
22
12
  }
23
13
 
24
14
  public async removeFile(name: string) {
25
15
  const path = this.formatPath(name)
26
- await fsRemove(path)
16
+ await fs.unlink(path)
27
17
  }
28
18
 
29
19
  public async moveFile(oldName: string, newName: string) {
30
- await mvFile(this.formatPath(oldName), this.formatPath(newName))
20
+ await fs.rename(this.formatPath(oldName), this.formatPath(newName))
31
21
  }
32
22
 
33
23
  public async createDirIfNotExist(): Promise<void> {
34
24
  try {
35
- await mkdir(this.directory)
25
+ await fs.mkdir(this.directory)
36
26
  } catch (e) {
37
27
  if (!(e instanceof Error) || !('code' in e) || (e as any).code !== 'EEXIST') {
38
28
  throw e
@@ -47,7 +37,7 @@ class MigrationFilesManager {
47
37
  files
48
38
  .filter(file => file.endsWith(`.json`))
49
39
  .filter(async file => {
50
- return (await lstatFile(`${this.directory}/${file}`)).isFile()
40
+ return (await fs.lstat(`${this.directory}/${file}`)).isFile()
51
41
  }),
52
42
  )
53
43
  return filteredFiles.sort()
@@ -55,7 +45,7 @@ class MigrationFilesManager {
55
45
 
56
46
  private async tryReadDir(): Promise<string[]> {
57
47
  try {
58
- return await readDir(this.directory)
48
+ return await fs.readdir(this.directory)
59
49
  } catch (e) {
60
50
  if (e instanceof Error && 'code' in e && (e as any).code === 'ENOENT') {
61
51
  return []
@@ -72,7 +62,7 @@ class MigrationFilesManager {
72
62
  const filesWithContent = files.map(async filename => ({
73
63
  filename: filename,
74
64
  path: `${this.directory}/${filename}`,
75
- content: await readFile(`${this.directory}/${filename}`, { encoding: 'utf8' }),
65
+ content: await fs.readFile(`${this.directory}/${filename}`, { encoding: 'utf8' }),
76
66
  }))
77
67
 
78
68
  return await Promise.all(filesWithContent)
@@ -18,6 +18,7 @@ export class MigrationsResolver {
18
18
  name: MigrationVersionHelper.extractName(filename),
19
19
  formatVersion: parsed.formatVersion || VERSION_INITIAL,
20
20
  modifications: parsed.modifications,
21
+ ...(parsed.skippedErrors ? { skippedErrors: parsed.skippedErrors } : {}),
21
22
  }
22
23
  })
23
24
  }
@@ -43,14 +43,23 @@ import {
43
43
  } from './modifications'
44
44
  import { ChangeViewNonViewDiffer, RemoveChangedFieldDiffer, RemoveChangedViewDiffer } from './modifications/differs'
45
45
  import { CreateIndexDiffer, RemoveIndexDiffer } from './modifications/indexes'
46
+ import { SchemaWithMeta } from './modifications/utils/schemaMeta'
47
+ import { UpdateSettingsDiffer } from './modifications/settings'
48
+
49
+ type DiffOptions = { skipRecreateValidation?: boolean; skipInitialSchemaValidation?: boolean }
46
50
 
47
51
  export class SchemaDiffer {
48
52
  constructor(private readonly schemaMigrator: SchemaMigrator) {}
49
53
 
50
- diffSchemas(originalSchema: Schema, updatedSchema: Schema, checkRecreate: boolean = true): Migration.Modification[] {
51
- const originalErrors = SchemaValidator.validate(originalSchema)
52
- if (originalErrors.length > 0) {
53
- throw new InvalidSchemaException('original schema is not valid', originalErrors)
54
+ diffSchemas(originalSchema: Schema, updatedSchema: Schema, {
55
+ skipInitialSchemaValidation = false,
56
+ skipRecreateValidation = false,
57
+ }: DiffOptions = {}): Migration.Modification[] {
58
+ if (!skipInitialSchemaValidation) {
59
+ const originalErrors = SchemaValidator.validate(originalSchema)
60
+ if (originalErrors.length > 0) {
61
+ throw new InvalidSchemaException('original schema is not valid', originalErrors)
62
+ }
54
63
  }
55
64
  const updatedErrors = SchemaValidator.validate(updatedSchema)
56
65
  if (updatedErrors.length > 0) {
@@ -58,6 +67,7 @@ export class SchemaDiffer {
58
67
  }
59
68
 
60
69
  const differs: Differ[] = [
70
+ new UpdateSettingsDiffer(),
61
71
  new ConvertOneToManyRelationDiffer(),
62
72
  new ConvertOneHasManyToManyHasManyRelationDiffer(),
63
73
  new RemoveUniqueConstraintDiffer(),
@@ -83,8 +93,8 @@ export class SchemaDiffer {
83
93
  new RemoveChangedFieldDiffer(it => isRelation(it) && isInverseRelation(it)),
84
94
  new CreateEntityDiffer(),
85
95
  new CreateColumnDiffer(),
86
- new CreateViewDiffer(),
87
96
  new CreateRelationDiffer(),
97
+ new CreateViewDiffer(),
88
98
  new CreateRelationInverseSideDiffer(),
89
99
  new CreateUniqueConstraintDiffer(),
90
100
  new CreateIndexDiffer(),
@@ -102,8 +112,9 @@ export class SchemaDiffer {
102
112
  }
103
113
 
104
114
 
105
- if (checkRecreate) {
106
- const errors = deepCompare(updatedSchema, appliedDiffsSchema, [])
115
+ if (!skipRecreateValidation) {
116
+ const { meta, ...appliedDiffsSchema2 } = appliedDiffsSchema as SchemaWithMeta
117
+ const errors = deepCompare(updatedSchema, appliedDiffsSchema2, [])
107
118
  if (errors.length === 0) {
108
119
  return diffs
109
120
  }
@@ -1,11 +1,12 @@
1
1
  import { Schema } from '@contember/schema'
2
2
  import { ModificationHandlerFactory } from './modifications'
3
3
  import { Migration } from './Migration'
4
+ import { SchemaWithMeta } from './modifications/utils/schemaMeta'
4
5
 
5
6
  export class SchemaMigrator {
6
7
  constructor(private readonly modificationHandlerFactory: ModificationHandlerFactory) {}
7
8
 
8
- public applyModifications(schema: Schema, diff: readonly Migration.Modification[], formatVersion: number): Schema {
9
+ public applyModifications(schema: Schema, diff: readonly Migration.Modification[], formatVersion: number): SchemaWithMeta {
9
10
  for (const modification of diff) {
10
11
  const { modification: name, ...data } = modification
11
12
  const modificationHandler = this.modificationHandlerFactory.create(name, data, schema, {
@@ -2,6 +2,7 @@ import { MigrationBuilder } from '@contember/database-migrations'
2
2
  import { SchemaUpdater } from './utils/schemaUpdateUtils'
3
3
  import { Schema } from '@contember/schema'
4
4
  import { Migration } from '../Migration'
5
+ import { SchemaWithMeta } from './utils/schemaMeta'
5
6
 
6
7
  export interface ModificationDescription {
7
8
  message: string
@@ -26,18 +27,18 @@ export interface ModificationHandlerOptions {
26
27
  }
27
28
 
28
29
  export interface ModificationHandlerConstructor<Data> {
29
- new(data: Data, schema: Schema, options: ModificationHandlerOptions): ModificationHandler<Data>
30
+ new(data: Data, schema: SchemaWithMeta, options: ModificationHandlerOptions): ModificationHandler<Data>
30
31
  }
31
32
 
32
- export interface Differ {
33
- createDiff: (originalSchema: Schema, updatedSchema: Schema) => Migration.Modification[]
33
+ export interface Differ<Data = { [field: string]: any }> {
34
+ createDiff: (originalSchema: Schema, updatedSchema: Schema) => Migration.Modification<Data>[]
34
35
  }
35
36
 
36
37
 
37
38
  export interface ModificationType<Id extends String, Data> {
38
39
  id: Id
39
40
  createModification: (data: Data) => Migration.Modification<Data>
40
- createHandler: (data: Data, schema: Schema, options: ModificationHandlerOptions) => ModificationHandler<Data>
41
+ createHandler: (data: Data, schema: SchemaWithMeta, options: ModificationHandlerOptions) => ModificationHandler<Data>
41
42
  }
42
43
 
43
44
  export const createModificationType = <Data, Id extends string>({ handler, id }: {
@@ -29,12 +29,14 @@ import {
29
29
  } from './relations'
30
30
  import { patchValidationSchemaModification, updateValidationSchemaModification } from './validation'
31
31
  import { createIndexModification, removeIndexModification } from './indexes'
32
+ import { SchemaWithMeta } from './utils/schemaMeta'
33
+ import { updateSettingsModification } from './settings'
32
34
 
33
35
 
34
36
  class ModificationHandlerFactory {
35
37
  constructor(private readonly map: Record<string, ModificationType<string, any>>) {}
36
38
 
37
- public create<D>(name: string, data: D, schema: Schema, options: ModificationHandlerOptions): ModificationHandler<D> {
39
+ public create<D>(name: string, data: D, schema: SchemaWithMeta, options: ModificationHandlerOptions): ModificationHandler<D> {
38
40
  if (!this.map[name]) {
39
41
  throw new Error(`Undefined modification handler for ${name}`)
40
42
  }
@@ -46,6 +48,7 @@ namespace ModificationHandlerFactory {
46
48
  type HandlerMap<D> = { [modificationName: string]: ModificationType<string, D> }
47
49
 
48
50
  const handlers = [
51
+ updateSettingsModification,
49
52
  updateAclSchemaModification,
50
53
  patchAclSchemaModification,
51
54
  createColumnModification,
@@ -6,6 +6,7 @@ import { wrapIdentifier } from '../../utils/dbHelpers'
6
6
  import { getColumnName, isColumn } from '@contember/schema-utils'
7
7
  import { ImplementationException } from '../../exceptions'
8
8
  import { createFields } from '../utils/diffUtils'
9
+ import { getColumnSqlType } from '../utils/columnUtils'
9
10
 
10
11
  export class CreateColumnModificationHandler implements ModificationHandler<CreateColumnModificationData> {
11
12
  constructor(private readonly data: CreateColumnModificationData, private readonly schema: Schema) {}
@@ -19,7 +20,7 @@ export class CreateColumnModificationHandler implements ModificationHandler<Crea
19
20
  const hasSeed = this.data.fillValue !== undefined || this.data.copyValue !== undefined
20
21
  builder.addColumn(entity.tableName, {
21
22
  [column.columnName]: {
22
- type: column.type === Model.ColumnType.Enum ? `"${column.columnType}"` : column.columnType,
23
+ type: getColumnSqlType(column),
23
24
  notNull: !column.nullable && !hasSeed,
24
25
  sequenceGenerated: column.sequence,
25
26
  },
@@ -31,7 +32,7 @@ export class CreateColumnModificationHandler implements ModificationHandler<Crea
31
32
  } else if (this.data.copyValue !== undefined) {
32
33
  const copyFrom = getColumnName(this.schema.model, entity, this.data.copyValue)
33
34
  builder.sql(`UPDATE ${wrapIdentifier(entity.tableName)}
34
- SET ${wrapIdentifier(column.columnName)} = ${wrapIdentifier(copyFrom)}`)
35
+ SET ${wrapIdentifier(column.columnName)} = ${wrapIdentifier(copyFrom)}::${getColumnSqlType(column)}`)
35
36
  } else {
36
37
  throw new ImplementationException()
37
38
  }
@@ -1,10 +1,12 @@
1
- import { MigrationBuilder } from '@contember/database-migrations'
1
+ import { escapeValue, MigrationBuilder } from '@contember/database-migrations'
2
2
  import { Model, Schema } from '@contember/schema'
3
3
  import { SchemaUpdater, updateEntity, updateField, updateModel } from '../utils/schemaUpdateUtils'
4
4
  import { createModificationType, Differ, ModificationHandler } from '../ModificationHandler'
5
5
  import deepEqual from 'fast-deep-equal'
6
6
  import { updateColumns } from '../utils/diffUtils'
7
7
  import { wrapIdentifier } from '@contember/database'
8
+ import { getColumnSqlType } from '../utils/columnUtils'
9
+ import { getEntityDependantViews } from '../utils/viewDependencies'
8
10
 
9
11
  export class UpdateColumnDefinitionModificationHandler implements ModificationHandler<UpdateColumnDefinitionModificationData> {
10
12
  constructor(private readonly data: UpdateColumnDefinitionModificationData, private readonly schema: Schema) {}
@@ -14,34 +16,40 @@ export class UpdateColumnDefinitionModificationHandler implements ModificationHa
14
16
  if (entity.view) {
15
17
  return
16
18
  }
17
- const field = entity.fields[this.data.fieldName] as Model.AnyColumn
18
- const definition = this.data.definition
19
- const newType = definition.columnType !== field.columnType
20
- ? definition.type === Model.ColumnType.Enum
21
- ? `"${definition.columnType}"`
22
- : definition.columnType
23
- : undefined
24
- builder.alterColumn(entity.tableName, field.columnName, {
25
- type: newType,
26
- notNull: field.nullable !== definition.nullable ? !definition.nullable : undefined,
27
- using: newType !== undefined ? `${wrapIdentifier(field.columnName)}::${newType}` : undefined,
28
- sequenceGenerated: field.sequence && !definition.sequence ? false : (!field.sequence ? definition.sequence : undefined),
19
+ const oldColumn = entity.fields[this.data.fieldName] as Model.AnyColumn
20
+ const newColumn = this.data.definition
21
+
22
+ const hasNewSequence = !oldColumn.sequence && newColumn.sequence
23
+ const hasNewType = newColumn.columnType !== oldColumn.columnType
24
+ const columnType = getColumnSqlType(newColumn)
25
+
26
+ const usingCast = `${wrapIdentifier(oldColumn.columnName)}::${columnType}`
27
+
28
+ builder.alterColumn(entity.tableName, oldColumn.columnName, {
29
+ type: hasNewSequence || hasNewType ? columnType : undefined,
30
+ notNull: oldColumn.nullable !== newColumn.nullable ? !newColumn.nullable : undefined,
31
+ using: hasNewSequence
32
+ ? `COALESCE(${usingCast}, nextval(PG_GET_SERIAL_SEQUENCE(${escapeValue(entity.tableName)}, ${escapeValue(oldColumn.columnName)})))`
33
+ : hasNewType
34
+ ? usingCast
35
+ : undefined,
36
+ sequenceGenerated: oldColumn.sequence && !newColumn.sequence ? false : (!oldColumn.sequence ? newColumn.sequence : undefined),
29
37
  })
30
38
 
31
39
  const seqAlter = []
32
- if (field.sequence && definition.sequence) {
33
- if (field.sequence.precedence !== definition.sequence.precedence) {
34
- seqAlter.push(`SET GENERATED ${definition.sequence.precedence}`)
40
+ if (oldColumn.sequence && newColumn.sequence) {
41
+ if (oldColumn.sequence.precedence !== newColumn.sequence.precedence) {
42
+ seqAlter.push(`SET GENERATED ${newColumn.sequence.precedence}`)
35
43
  }
36
- if (field.sequence.start !== definition.sequence.start && typeof definition.sequence.start == 'number') {
37
- seqAlter.push(`SET START WITH ${definition.sequence.start}`)
44
+ if (oldColumn.sequence.start !== newColumn.sequence.start && typeof newColumn.sequence.start == 'number') {
45
+ seqAlter.push(`SET START WITH ${newColumn.sequence.start}`)
38
46
  }
39
- if (definition.sequence.restart) {
47
+ if (newColumn.sequence.restart) {
40
48
  seqAlter.push('RESTART')
41
49
  }
42
50
  }
43
51
  if (seqAlter.length > 0) {
44
- builder.sql(`ALTER TABLE ${wrapIdentifier(entity.tableName)} ALTER ${wrapIdentifier(field.columnName)} ${seqAlter.join(' ')}`)
52
+ builder.sql(`ALTER TABLE ${wrapIdentifier(entity.tableName)} ALTER ${wrapIdentifier(oldColumn.columnName)} ${seqAlter.join(' ')}`)
45
53
  }
46
54
  }
47
55
 
@@ -79,7 +87,7 @@ type SequenceDefinitionAlter =
79
87
  }
80
88
 
81
89
  type ColumnDefinitionAlter =
82
- & Model.AnyColumn
90
+ & Omit<Model.AnyColumn, 'sequence' | 'columnName' | 'name'>
83
91
  & {
84
92
  sequence?: SequenceDefinitionAlter
85
93
  }
@@ -95,22 +103,23 @@ export const updateColumnDefinitionModification = createModificationType({
95
103
  handler: UpdateColumnDefinitionModificationHandler,
96
104
  })
97
105
 
98
- export class UpdateColumnDefinitionDiffer implements Differ {
106
+ export class UpdateColumnDefinitionDiffer implements Differ<UpdateColumnDefinitionModificationData> {
99
107
  createDiff(originalSchema: Schema, updatedSchema: Schema) {
100
108
  return updateColumns(originalSchema, updatedSchema, ({ originalColumn, updatedColumn, updatedEntity }) => {
101
109
  const {
102
110
  name: {},
103
111
  columnName: {},
104
112
  ...updatedDefinition
105
- } = updatedColumn as any
113
+ } = updatedColumn
106
114
  const {
107
115
  name: {},
108
116
  columnName: {},
109
117
  ...originalDefinition
110
- } = originalColumn as any
118
+ } = originalColumn
111
119
  if (deepEqual(updatedDefinition, originalDefinition)) {
112
120
  return undefined
113
121
  }
122
+
114
123
  return updateColumnDefinitionModification.createModification({
115
124
  entityName: updatedEntity.name,
116
125
  fieldName: updatedColumn.name,
@@ -15,13 +15,13 @@ export class UpdateColumnNameModificationHandler implements ModificationHandler<
15
15
  return
16
16
  }
17
17
  acceptFieldVisitor(this.schema.model, entity, this.data.fieldName, {
18
- visitColumn: (entity, column) => {
18
+ visitColumn: ({ entity, column }) => {
19
19
  builder.renameColumn(entity.tableName, column.columnName, this.data.columnName)
20
20
  },
21
- visitManyHasOne: (entity, relation) => {
21
+ visitManyHasOne: ({ entity, relation }) => {
22
22
  builder.renameColumn(entity.tableName, relation.joiningColumn.columnName, this.data.columnName)
23
23
  },
24
- visitOneHasOneOwning: (entity, relation) => {
24
+ visitOneHasOneOwning: ({ entity, relation }) => {
25
25
  builder.renameColumn(entity.tableName, relation.joiningColumn.columnName, this.data.columnName)
26
26
  },
27
27
  visitManyHasManyInverse: () => {
@@ -43,47 +43,47 @@ export class UpdateColumnNameModificationHandler implements ModificationHandler<
43
43
  return updateModel(
44
44
  updateEntity(
45
45
  this.data.entityName,
46
- updateField(this.data.fieldName, ({
47
- field,
48
- entity,
49
- }) => acceptFieldVisitor<Model.AnyField>(this.schema.model, entity, field, {
50
- visitColumn: (entity, column) => {
51
- return {
52
- ...column,
53
- columnName: this.data.columnName,
54
- }
55
- },
56
- visitManyHasOne: (entity, relation) => {
57
- return {
58
- ...relation,
59
- joiningColumn: {
60
- ...relation.joiningColumn,
61
- columnName: this.data.columnName,
62
- },
63
- }
64
- },
65
- visitOneHasOneOwning: (entity, relation) => {
66
- return {
67
- ...relation,
68
- joiningColumn: {
69
- ...relation.joiningColumn,
46
+ updateField(this.data.fieldName, ({ field, entity }) =>
47
+ acceptFieldVisitor<Model.AnyField>(this.schema.model, entity, field, {
48
+ visitColumn: ({ column }) => {
49
+ return {
50
+ ...column,
70
51
  columnName: this.data.columnName,
71
- },
72
- }
73
- },
74
- visitManyHasManyInverse: () => {
75
- throw new Error('Cannot rename column of many-to-many relation')
76
- },
77
- visitManyHasManyOwning: () => {
78
- throw new Error('Cannot rename column of many-to-many relation')
79
- },
80
- visitOneHasMany: () => {
81
- throw new Error('Cannot rename column of one-to-many relation')
82
- },
83
- visitOneHasOneInverse: () => {
84
- throw new Error('Cannot rename column of one-to-one inverse relation')
52
+ }
53
+ },
54
+ visitManyHasOne: ({ entity, relation }) => {
55
+ return {
56
+ ...relation,
57
+ joiningColumn: {
58
+ ...relation.joiningColumn,
59
+ columnName: this.data.columnName,
60
+ },
61
+ }
62
+ },
63
+ visitOneHasOneOwning: ({ entity, relation }) => {
64
+ return {
65
+ ...relation,
66
+ joiningColumn: {
67
+ ...relation.joiningColumn,
68
+ columnName: this.data.columnName,
69
+ },
70
+ }
71
+ },
72
+ visitManyHasManyInverse: () => {
73
+ throw new Error('Cannot rename column of many-to-many relation')
74
+ },
75
+ visitManyHasManyOwning: () => {
76
+ throw new Error('Cannot rename column of many-to-many relation')
77
+ },
78
+ visitOneHasMany: () => {
79
+ throw new Error('Cannot rename column of one-to-many relation')
80
+ },
81
+ visitOneHasOneInverse: () => {
82
+ throw new Error('Cannot rename column of one-to-one inverse relation')
83
+ },
85
84
  },
86
- })),
85
+ ),
86
+ ),
87
87
  ),
88
88
  )
89
89
  }
@@ -16,10 +16,10 @@ export class CreateUniqueConstraintModificationHandler implements ModificationHa
16
16
  const fields = this.data.unique.fields
17
17
  const columns = fields.map(fieldName => {
18
18
  return acceptFieldVisitor(this.schema.model, entity, fieldName, {
19
- visitColumn: ({}, column) => {
19
+ visitColumn: ({ column }) => {
20
20
  return column.columnName
21
21
  },
22
- visitManyHasOne: ({}, relation) => {
22
+ visitManyHasOne: ({ relation }) => {
23
23
  return relation.joiningColumn.columnName
24
24
  },
25
25
  visitOneHasMany: () => {
@@ -1,9 +1,8 @@
1
1
  import { Differ } from '../ModificationHandler'
2
- import { Model, Schema } from '@contember/schema'
2
+ import { Schema } from '@contember/schema'
3
3
  import deepEqual from 'fast-deep-equal'
4
- import { removeEntityModification } from '../entities'
5
- import { getViewDirectDependants } from '../utils/viewDependencies'
6
- import { Migration } from '../../Migration'
4
+ import { cascadeRemoveDependantViews } from '../utils/viewDependencies'
5
+ import { UpdateColumnDefinitionDiffer } from '../columns'
7
6
 
8
7
  export class RemoveChangedViewDiffer implements Differ {
9
8
  constructor() {
@@ -18,25 +17,13 @@ export class RemoveChangedViewDiffer implements Differ {
18
17
  }
19
18
  return !deepEqual(updatedEntity.view, it.view)
20
19
  })
21
- if (changedViews.length === 0) {
20
+ const columnDiffer = new UpdateColumnDefinitionDiffer()
21
+ const columnChanges = columnDiffer.createDiff(originalSchema, updatedSchema)
22
+ if (changedViews.length === 0 && columnChanges.length === 0) {
22
23
  return []
23
24
  }
24
- const removed = new Set<string>()
25
- const dependants = getViewDirectDependants(originalSchema.model)
26
- const modifications: Migration.Modification[] = []
27
- const removeCascade = (entity: Model.Entity) => {
28
- if (removed.has(entity.name)) {
29
- return
30
- }
31
- removed.add(entity.name)
32
- for (const dependant of dependants.get(entity.name) ?? []) {
33
- removeCascade(dependant)
34
- }
35
- modifications.push(removeEntityModification.createModification({ entityName: entity.name }))
36
- }
37
- for (const changed of changedViews) {
38
- removeCascade(changed)
39
- }
40
- return modifications
25
+ const changedColumnsEntities = Array.from(new Set(columnChanges.map(it => it.entityName)))
26
+ .map(it => originalSchema.model.entities[it])
27
+ return cascadeRemoveDependantViews(originalSchema.model, [...changedViews, ...changedColumnsEntities])
41
28
  }
42
29
  }
@@ -9,6 +9,7 @@ import {
9
9
  } from '../ModificationHandler'
10
10
  import { createEventTrigger, createEventTrxTrigger } from '../utils/sqlUpdateUtils'
11
11
  import { PartialEntity } from '../../utils/PartialEntity.js'
12
+ import { getColumnSqlType } from '../utils/columnUtils'
12
13
 
13
14
  export class CreateEntityModificationHandler implements ModificationHandler<CreateEntityModificationData> {
14
15
  constructor(
@@ -29,7 +30,7 @@ export class CreateEntityModificationHandler implements ModificationHandler<Crea
29
30
  builder.createTable(entity.tableName, {
30
31
  [primaryColumn.name]: {
31
32
  primaryKey: true,
32
- type: primaryColumn.type === Model.ColumnType.Enum ? `"${primaryColumn.columnType}"` : primaryColumn.columnType,
33
+ type: getColumnSqlType(primaryColumn),
33
34
  notNull: true,
34
35
  sequenceGenerated: primaryColumn.sequence,
35
36
  },
@@ -20,11 +20,12 @@ import {
20
20
  import { VERSION_ACL_PATCH, VERSION_REMOVE_REFERENCING_RELATIONS } from '../ModificationVersions'
21
21
  import { isRelation, PredicateDefinitionProcessor } from '@contember/schema-utils'
22
22
  import { removeFieldModification } from '../fields'
23
+ import { SchemaWithMeta } from '../utils/schemaMeta'
23
24
 
24
25
  export class RemoveEntityModificationHandler implements ModificationHandler<RemoveEntityModificationData> {
25
26
  constructor(
26
27
  private readonly data: RemoveEntityModificationData,
27
- private readonly schema: Schema,
28
+ private readonly schema: SchemaWithMeta,
28
29
  private readonly options: ModificationHandlerOptions,
29
30
  ) {}
30
31
 
@@ -19,13 +19,14 @@ import { NoopModification } from '../NoopModification'
19
19
  import { renameConstraintSchemaUpdater, renameConstraintsSqlBuilder } from '../utils/renameConstraintsHelper'
20
20
  import { changeValue } from '../utils/valueUtils'
21
21
  import { updateEntityTableNameModification } from './UpdateEntityTableNameModification'
22
+ import { SchemaWithMeta } from '../utils/schemaMeta'
22
23
 
23
24
  export class UpdateEntityNameModificationHandler implements ModificationHandler<UpdateEntityNameModificationData> {
24
25
  private subModification: ModificationHandler<any>
25
26
 
26
27
  constructor(
27
28
  private readonly data: UpdateEntityNameModificationData,
28
- private readonly schema: Schema,
29
+ private readonly schema: SchemaWithMeta,
29
30
  private readonly options: ModificationHandlerOptions,
30
31
  ) {
31
32
  this.subModification = data.tableName
@@ -19,19 +19,19 @@ export class RemoveFieldModificationHandler implements ModificationHandler<Remov
19
19
  return
20
20
  }
21
21
  acceptFieldVisitor(this.schema.model, this.data.entityName, this.data.fieldName, {
22
- visitColumn: (entity, column) => {
22
+ visitColumn: ({ entity, column }) => {
23
23
  builder.dropColumn(entity.tableName, column.columnName)
24
24
  },
25
- visitManyHasOne: (entity, relation, {}, _) => {
25
+ visitManyHasOne: ({ entity, relation }) => {
26
26
  builder.dropColumn(entity.tableName, relation.joiningColumn.columnName)
27
27
  },
28
28
  visitOneHasMany: () => {},
29
- visitOneHasOneOwning: (entity, relation, {}, _) => {
29
+ visitOneHasOneOwning: ({ entity, relation }) => {
30
30
  builder.dropConstraint(entity.tableName, NamingHelper.createUniqueConstraintName(entity.name, [relation.name]))
31
31
  builder.dropColumn(entity.tableName, relation.joiningColumn.columnName)
32
32
  },
33
33
  visitOneHasOneInverse: () => {},
34
- visitManyHasManyOwning: ({}, relation, {}, _) => {
34
+ visitManyHasManyOwning: ({ relation }) => {
35
35
  builder.dropTable(relation.joiningTable.tableName)
36
36
  },
37
37
  visitManyHasManyInverse: () => {},
@@ -20,13 +20,14 @@ import { renameConstraintSchemaUpdater, renameConstraintsSqlBuilder } from '../u
20
20
  import { changeValue } from '../utils/valueUtils'
21
21
  import { updateColumnNameModification } from '../columns'
22
22
  import { NoopModification } from '../NoopModification'
23
+ import { SchemaWithMeta } from '../utils/schemaMeta'
23
24
 
24
25
  export class UpdateFieldNameModificationHandler implements ModificationHandler<UpdateFieldNameModificationData> {
25
26
  private renameColumnSubModification: ModificationHandler<any> = new NoopModification()
26
27
 
27
28
  constructor(
28
29
  private readonly data: UpdateFieldNameModificationData,
29
- private readonly schema: Schema,
30
+ private readonly schema: SchemaWithMeta,
30
31
  private readonly options: ModificationHandlerOptions,
31
32
  ) {
32
33
  if (this.data.columnName) {
@@ -79,33 +80,33 @@ export class UpdateFieldNameModificationHandler implements ModificationHandler<U
79
80
  }
80
81
 
81
82
  return acceptFieldVisitor<Model.AnyField>(this.schema.model, entity, field, {
82
- visitColumn: (entity, field) => field,
83
- visitManyHasOne: (entity, relation, targetEntity, targetRelation) => {
83
+ visitColumn: ({ column }) => column,
84
+ visitManyHasOne: ({ targetEntity, relation, targetRelation }) => {
84
85
  return isUpdatedRelation(targetEntity, targetRelation)
85
86
  ? { ...relation, inversedBy: this.data.newFieldName }
86
87
  : relation
87
88
  },
88
- visitOneHasMany: (entity, relation, targetEntity, targetRelation) => {
89
+ visitOneHasMany: ({ relation, targetEntity, targetRelation }) => {
89
90
  return isUpdatedRelation(targetEntity, targetRelation)
90
91
  ? { ...relation, ownedBy: this.data.newFieldName }
91
92
  : relation
92
93
  },
93
- visitOneHasOneOwning: (entity, relation, targetEntity, targetRelation) => {
94
+ visitOneHasOneOwning: ({ relation, targetEntity, targetRelation }) => {
94
95
  return isUpdatedRelation(targetEntity, targetRelation)
95
96
  ? { ...relation, inversedBy: this.data.newFieldName }
96
97
  : relation
97
98
  },
98
- visitOneHasOneInverse: (entity, relation, targetEntity, targetRelation) => {
99
+ visitOneHasOneInverse: ({ relation, targetEntity, targetRelation }) => {
99
100
  return isUpdatedRelation(targetEntity, targetRelation)
100
101
  ? { ...relation, ownedBy: this.data.newFieldName }
101
102
  : relation
102
103
  },
103
- visitManyHasManyOwning: (entity, relation, targetEntity, targetRelation) => {
104
+ visitManyHasManyOwning: ({ relation, targetEntity, targetRelation }) => {
104
105
  return isUpdatedRelation(targetEntity, targetRelation)
105
106
  ? { ...relation, inversedBy: this.data.newFieldName }
106
107
  : relation
107
108
  },
108
- visitManyHasManyInverse: (entity, relation, targetEntity, targetRelation) => {
109
+ visitManyHasManyInverse: ({ relation, targetEntity, targetRelation }) => {
109
110
  return isUpdatedRelation(targetEntity, targetRelation)
110
111
  ? { ...relation, ownedBy: this.data.newFieldName }
111
112
  : relation