diff --git a/server/src/sql-tools/diff/comparers/column.comparer.spec.ts b/server/src/sql-tools/comparers/column.comparer.spec.ts similarity index 89% rename from server/src/sql-tools/diff/comparers/column.comparer.spec.ts rename to server/src/sql-tools/comparers/column.comparer.spec.ts index 082d15f0db..25ef8543a8 100644 --- a/server/src/sql-tools/diff/comparers/column.comparer.spec.ts +++ b/server/src/sql-tools/comparers/column.comparer.spec.ts @@ -1,4 +1,4 @@ -import { compareColumns } from 'src/sql-tools/diff/comparers/column.comparer'; +import { compareColumns } from 'src/sql-tools/comparers/column.comparer'; import { DatabaseColumn, Reason } from 'src/sql-tools/types'; import { describe, expect, it } from 'vitest'; @@ -18,7 +18,7 @@ describe('compareColumns', () => { { tableName: 'table1', columnName: 'test', - type: 'column.drop', + type: 'ColumnDrop', reason: Reason.MissingInSource, }, ]); @@ -29,7 +29,7 @@ describe('compareColumns', () => { it('should work', () => { expect(compareColumns.onMissing(testColumn)).toEqual([ { - type: 'column.add', + type: 'ColumnAdd', column: testColumn, reason: Reason.MissingInTarget, }, @@ -50,11 +50,11 @@ describe('compareColumns', () => { { columnName: 'test', tableName: 'table1', - type: 'column.drop', + type: 'ColumnDrop', reason, }, { - type: 'column.add', + type: 'ColumnAdd', column: source, reason, }, @@ -69,7 +69,7 @@ describe('compareColumns', () => { { columnName: 'test', tableName: 'table1', - type: 'column.alter', + type: 'ColumnAlter', changes: { comment: 'new comment', }, diff --git a/server/src/sql-tools/diff/comparers/column.comparer.ts b/server/src/sql-tools/comparers/column.comparer.ts similarity index 90% rename from server/src/sql-tools/diff/comparers/column.comparer.ts rename to server/src/sql-tools/comparers/column.comparer.ts index 205bd594ae..5cc3f7a930 100644 --- a/server/src/sql-tools/diff/comparers/column.comparer.ts +++ b/server/src/sql-tools/comparers/column.comparer.ts @@ -4,14 +4,14 @@ import { Comparer, DatabaseColumn, Reason, SchemaDiff } from 'src/sql-tools/type export const compareColumns: Comparer = { onMissing: (source) => [ { - type: 'column.add', + type: 'ColumnAdd', column: source, reason: Reason.MissingInTarget, }, ], onExtra: (target) => [ { - type: 'column.drop', + type: 'ColumnDrop', tableName: target.tableName, columnName: target.name, reason: Reason.MissingInSource, @@ -31,7 +31,7 @@ export const compareColumns: Comparer = { const items: SchemaDiff[] = []; if (source.nullable !== target.nullable) { items.push({ - type: 'column.alter', + type: 'ColumnAlter', tableName: source.tableName, columnName: source.name, changes: { @@ -43,7 +43,7 @@ export const compareColumns: Comparer = { if (!isDefaultEqual(source, target)) { items.push({ - type: 'column.alter', + type: 'ColumnAlter', tableName: source.tableName, columnName: source.name, changes: { @@ -55,7 +55,7 @@ export const compareColumns: Comparer = { if (source.comment !== target.comment) { items.push({ - type: 'column.alter', + type: 'ColumnAlter', tableName: source.tableName, columnName: source.name, changes: { @@ -72,11 +72,11 @@ export const compareColumns: Comparer = { const dropAndRecreateColumn = (source: DatabaseColumn, target: DatabaseColumn, reason: string): SchemaDiff[] => { return [ { - type: 'column.drop', + type: 'ColumnDrop', tableName: target.tableName, columnName: target.name, reason, }, - { type: 'column.add', column: source, reason }, + { type: 'ColumnAdd', column: source, reason }, ]; }; diff --git a/server/src/sql-tools/diff/comparers/constraint.comparer.spec.ts b/server/src/sql-tools/comparers/constraint.comparer.spec.ts similarity index 80% rename from server/src/sql-tools/diff/comparers/constraint.comparer.spec.ts rename to server/src/sql-tools/comparers/constraint.comparer.spec.ts index 69d8a8cc43..b5da19e8df 100644 --- a/server/src/sql-tools/diff/comparers/constraint.comparer.spec.ts +++ b/server/src/sql-tools/comparers/constraint.comparer.spec.ts @@ -1,9 +1,9 @@ -import { compareConstraints } from 'src/sql-tools/diff/comparers/constraint.comparer'; -import { DatabaseConstraint, DatabaseConstraintType, Reason } from 'src/sql-tools/types'; +import { compareConstraints } from 'src/sql-tools/comparers/constraint.comparer'; +import { ConstraintType, DatabaseConstraint, Reason } from 'src/sql-tools/types'; import { describe, expect, it } from 'vitest'; const testConstraint: DatabaseConstraint = { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'test', tableName: 'table1', columnNames: ['column1'], @@ -15,7 +15,7 @@ describe('compareConstraints', () => { it('should work', () => { expect(compareConstraints.onExtra(testConstraint)).toEqual([ { - type: 'constraint.drop', + type: 'ConstraintDrop', constraintName: 'test', tableName: 'table1', reason: Reason.MissingInSource, @@ -28,7 +28,7 @@ describe('compareConstraints', () => { it('should work', () => { expect(compareConstraints.onMissing(testConstraint)).toEqual([ { - type: 'constraint.add', + type: 'ConstraintAdd', constraint: testConstraint, reason: Reason.MissingInTarget, }, @@ -49,11 +49,11 @@ describe('compareConstraints', () => { { constraintName: 'test', tableName: 'table1', - type: 'constraint.drop', + type: 'ConstraintDrop', reason, }, { - type: 'constraint.add', + type: 'ConstraintAdd', constraint: source, reason, }, diff --git a/server/src/sql-tools/diff/comparers/constraint.comparer.ts b/server/src/sql-tools/comparers/constraint.comparer.ts similarity index 91% rename from server/src/sql-tools/diff/comparers/constraint.comparer.ts rename to server/src/sql-tools/comparers/constraint.comparer.ts index ccb594741c..0ff6fbe131 100644 --- a/server/src/sql-tools/diff/comparers/constraint.comparer.ts +++ b/server/src/sql-tools/comparers/constraint.comparer.ts @@ -2,9 +2,9 @@ import { haveEqualColumns } from 'src/sql-tools/helpers'; import { CompareFunction, Comparer, + ConstraintType, DatabaseCheckConstraint, DatabaseConstraint, - DatabaseConstraintType, DatabaseForeignKeyConstraint, DatabasePrimaryKeyConstraint, DatabaseUniqueConstraint, @@ -15,14 +15,14 @@ import { export const compareConstraints: Comparer = { onMissing: (source) => [ { - type: 'constraint.add', + type: 'ConstraintAdd', constraint: source, reason: Reason.MissingInTarget, }, ], onExtra: (target) => [ { - type: 'constraint.drop', + type: 'ConstraintDrop', tableName: target.tableName, constraintName: target.name, reason: Reason.MissingInSource, @@ -30,19 +30,19 @@ export const compareConstraints: Comparer = { ], onCompare: (source, target) => { switch (source.type) { - case DatabaseConstraintType.PRIMARY_KEY: { + case ConstraintType.PRIMARY_KEY: { return comparePrimaryKeyConstraint(source, target as DatabasePrimaryKeyConstraint); } - case DatabaseConstraintType.FOREIGN_KEY: { + case ConstraintType.FOREIGN_KEY: { return compareForeignKeyConstraint(source, target as DatabaseForeignKeyConstraint); } - case DatabaseConstraintType.UNIQUE: { + case ConstraintType.UNIQUE: { return compareUniqueConstraint(source, target as DatabaseUniqueConstraint); } - case DatabaseConstraintType.CHECK: { + case ConstraintType.CHECK: { return compareCheckConstraint(source, target as DatabaseCheckConstraint); } @@ -123,11 +123,11 @@ const dropAndRecreateConstraint = ( ): SchemaDiff[] => { return [ { - type: 'constraint.drop', + type: 'ConstraintDrop', tableName: target.tableName, constraintName: target.name, reason, }, - { type: 'constraint.add', constraint: source, reason }, + { type: 'ConstraintAdd', constraint: source, reason }, ]; }; diff --git a/server/src/sql-tools/diff/comparers/enum.comparer.spec.ts b/server/src/sql-tools/comparers/enum.comparer.spec.ts similarity index 87% rename from server/src/sql-tools/diff/comparers/enum.comparer.spec.ts rename to server/src/sql-tools/comparers/enum.comparer.spec.ts index 6e1ad992d5..82fc205662 100644 --- a/server/src/sql-tools/diff/comparers/enum.comparer.spec.ts +++ b/server/src/sql-tools/comparers/enum.comparer.spec.ts @@ -1,4 +1,4 @@ -import { compareEnums } from 'src/sql-tools/diff/comparers/enum.comparer'; +import { compareEnums } from 'src/sql-tools/comparers/enum.comparer'; import { DatabaseEnum, Reason } from 'src/sql-tools/types'; import { describe, expect, it } from 'vitest'; @@ -10,7 +10,7 @@ describe('compareEnums', () => { expect(compareEnums.onExtra(testEnum)).toEqual([ { enumName: 'test', - type: 'enum.drop', + type: 'EnumDrop', reason: Reason.MissingInSource, }, ]); @@ -21,7 +21,7 @@ describe('compareEnums', () => { it('should work', () => { expect(compareEnums.onMissing(testEnum)).toEqual([ { - type: 'enum.create', + type: 'EnumCreate', enum: testEnum, reason: Reason.MissingInTarget, }, @@ -40,11 +40,11 @@ describe('compareEnums', () => { expect(compareEnums.onCompare(source, target)).toEqual([ { enumName: 'test', - type: 'enum.drop', + type: 'EnumDrop', reason: 'enum values has changed (foo,bar vs foo,bar,world)', }, { - type: 'enum.create', + type: 'EnumCreate', enum: source, reason: 'enum values has changed (foo,bar vs foo,bar,world)', }, diff --git a/server/src/sql-tools/diff/comparers/enum.comparer.ts b/server/src/sql-tools/comparers/enum.comparer.ts similarity index 87% rename from server/src/sql-tools/diff/comparers/enum.comparer.ts rename to server/src/sql-tools/comparers/enum.comparer.ts index 408f01050b..d81f9ed3c0 100644 --- a/server/src/sql-tools/diff/comparers/enum.comparer.ts +++ b/server/src/sql-tools/comparers/enum.comparer.ts @@ -3,14 +3,14 @@ import { Comparer, DatabaseEnum, Reason } from 'src/sql-tools/types'; export const compareEnums: Comparer = { onMissing: (source) => [ { - type: 'enum.create', + type: 'EnumCreate', enum: source, reason: Reason.MissingInTarget, }, ], onExtra: (target) => [ { - type: 'enum.drop', + type: 'EnumDrop', enumName: target.name, reason: Reason.MissingInSource, }, @@ -21,12 +21,12 @@ export const compareEnums: Comparer = { const reason = `enum values has changed (${source.values} vs ${target.values})`; return [ { - type: 'enum.drop', + type: 'EnumDrop', enumName: source.name, reason, }, { - type: 'enum.create', + type: 'EnumCreate', enum: source, reason, }, diff --git a/server/src/sql-tools/diff/comparers/extension.comparer.spec.ts b/server/src/sql-tools/comparers/extension.comparer.spec.ts similarity index 84% rename from server/src/sql-tools/diff/comparers/extension.comparer.spec.ts rename to server/src/sql-tools/comparers/extension.comparer.spec.ts index 753c461c69..38e553719d 100644 --- a/server/src/sql-tools/diff/comparers/extension.comparer.spec.ts +++ b/server/src/sql-tools/comparers/extension.comparer.spec.ts @@ -1,4 +1,4 @@ -import { compareExtensions } from 'src/sql-tools/diff/comparers/extension.comparer'; +import { compareExtensions } from 'src/sql-tools/comparers/extension.comparer'; import { Reason } from 'src/sql-tools/types'; import { describe, expect, it } from 'vitest'; @@ -10,7 +10,7 @@ describe('compareExtensions', () => { expect(compareExtensions.onExtra(testExtension)).toEqual([ { extensionName: 'test', - type: 'extension.drop', + type: 'ExtensionDrop', reason: Reason.MissingInSource, }, ]); @@ -21,7 +21,7 @@ describe('compareExtensions', () => { it('should work', () => { expect(compareExtensions.onMissing(testExtension)).toEqual([ { - type: 'extension.create', + type: 'ExtensionCreate', extension: testExtension, reason: Reason.MissingInTarget, }, diff --git a/server/src/sql-tools/diff/comparers/extension.comparer.ts b/server/src/sql-tools/comparers/extension.comparer.ts similarity index 87% rename from server/src/sql-tools/diff/comparers/extension.comparer.ts rename to server/src/sql-tools/comparers/extension.comparer.ts index 1c9d19165a..441b00e3e3 100644 --- a/server/src/sql-tools/diff/comparers/extension.comparer.ts +++ b/server/src/sql-tools/comparers/extension.comparer.ts @@ -3,14 +3,14 @@ import { Comparer, DatabaseExtension, Reason } from 'src/sql-tools/types'; export const compareExtensions: Comparer = { onMissing: (source) => [ { - type: 'extension.create', + type: 'ExtensionCreate', extension: source, reason: Reason.MissingInTarget, }, ], onExtra: (target) => [ { - type: 'extension.drop', + type: 'ExtensionDrop', extensionName: target.name, reason: Reason.MissingInSource, }, diff --git a/server/src/sql-tools/diff/comparers/function.comparer.spec.ts b/server/src/sql-tools/comparers/function.comparer.spec.ts similarity index 88% rename from server/src/sql-tools/diff/comparers/function.comparer.spec.ts rename to server/src/sql-tools/comparers/function.comparer.spec.ts index ac478ed000..964768cf98 100644 --- a/server/src/sql-tools/diff/comparers/function.comparer.spec.ts +++ b/server/src/sql-tools/comparers/function.comparer.spec.ts @@ -1,4 +1,4 @@ -import { compareFunctions } from 'src/sql-tools/diff/comparers/function.comparer'; +import { compareFunctions } from 'src/sql-tools/comparers/function.comparer'; import { DatabaseFunction, Reason } from 'src/sql-tools/types'; import { describe, expect, it } from 'vitest'; @@ -14,7 +14,7 @@ describe('compareFunctions', () => { expect(compareFunctions.onExtra(testFunction)).toEqual([ { functionName: 'test', - type: 'function.drop', + type: 'FunctionDrop', reason: Reason.MissingInSource, }, ]); @@ -25,7 +25,7 @@ describe('compareFunctions', () => { it('should work', () => { expect(compareFunctions.onMissing(testFunction)).toEqual([ { - type: 'function.create', + type: 'FunctionCreate', function: testFunction, reason: Reason.MissingInTarget, }, @@ -43,7 +43,7 @@ describe('compareFunctions', () => { const target: DatabaseFunction = { ...testFunction, expression: 'SELECT 2' }; expect(compareFunctions.onCompare(source, target)).toEqual([ { - type: 'function.create', + type: 'FunctionCreate', reason: 'function expression has changed (SELECT 1 vs SELECT 2)', function: source, }, diff --git a/server/src/sql-tools/diff/comparers/function.comparer.ts b/server/src/sql-tools/comparers/function.comparer.ts similarity index 87% rename from server/src/sql-tools/diff/comparers/function.comparer.ts rename to server/src/sql-tools/comparers/function.comparer.ts index d10353b89c..000cf07058 100644 --- a/server/src/sql-tools/diff/comparers/function.comparer.ts +++ b/server/src/sql-tools/comparers/function.comparer.ts @@ -3,14 +3,14 @@ import { Comparer, DatabaseFunction, Reason } from 'src/sql-tools/types'; export const compareFunctions: Comparer = { onMissing: (source) => [ { - type: 'function.create', + type: 'FunctionCreate', function: source, reason: Reason.MissingInTarget, }, ], onExtra: (target) => [ { - type: 'function.drop', + type: 'FunctionDrop', functionName: target.name, reason: Reason.MissingInSource, }, @@ -20,7 +20,7 @@ export const compareFunctions: Comparer = { const reason = `function expression has changed (${source.expression} vs ${target.expression})`; return [ { - type: 'function.create', + type: 'FunctionCreate', function: source, reason, }, diff --git a/server/src/sql-tools/diff/comparers/index.comparer.spec.ts b/server/src/sql-tools/comparers/index.comparer.spec.ts similarity index 89% rename from server/src/sql-tools/diff/comparers/index.comparer.spec.ts rename to server/src/sql-tools/comparers/index.comparer.spec.ts index 806bab190c..b00be386e0 100644 --- a/server/src/sql-tools/diff/comparers/index.comparer.spec.ts +++ b/server/src/sql-tools/comparers/index.comparer.spec.ts @@ -1,4 +1,4 @@ -import { compareIndexes } from 'src/sql-tools/diff/comparers/index.comparer'; +import { compareIndexes } from 'src/sql-tools/comparers/index.comparer'; import { DatabaseIndex, Reason } from 'src/sql-tools/types'; import { describe, expect, it } from 'vitest'; @@ -15,7 +15,7 @@ describe('compareIndexes', () => { it('should work', () => { expect(compareIndexes.onExtra(testIndex)).toEqual([ { - type: 'index.drop', + type: 'IndexDrop', indexName: 'test', reason: Reason.MissingInSource, }, @@ -27,7 +27,7 @@ describe('compareIndexes', () => { it('should work', () => { expect(compareIndexes.onMissing(testIndex)).toEqual([ { - type: 'index.create', + type: 'IndexCreate', index: testIndex, reason: Reason.MissingInTarget, }, @@ -58,11 +58,11 @@ describe('compareIndexes', () => { expect(compareIndexes.onCompare(source, target)).toEqual([ { indexName: 'test', - type: 'index.drop', + type: 'IndexDrop', reason: 'columns are different (column1 vs column1,column2)', }, { - type: 'index.create', + type: 'IndexCreate', index: source, reason: 'columns are different (column1 vs column1,column2)', }, diff --git a/server/src/sql-tools/diff/comparers/index.comparer.ts b/server/src/sql-tools/comparers/index.comparer.ts similarity index 88% rename from server/src/sql-tools/diff/comparers/index.comparer.ts rename to server/src/sql-tools/comparers/index.comparer.ts index ef07e3a17b..99571cf61a 100644 --- a/server/src/sql-tools/diff/comparers/index.comparer.ts +++ b/server/src/sql-tools/comparers/index.comparer.ts @@ -4,14 +4,14 @@ import { Comparer, DatabaseIndex, Reason } from 'src/sql-tools/types'; export const compareIndexes: Comparer = { onMissing: (source) => [ { - type: 'index.create', + type: 'IndexCreate', index: source, reason: Reason.MissingInTarget, }, ], onExtra: (target) => [ { - type: 'index.drop', + type: 'IndexDrop', indexName: target.name, reason: Reason.MissingInSource, }, @@ -36,8 +36,8 @@ export const compareIndexes: Comparer = { if (reason) { return [ - { type: 'index.drop', indexName: target.name, reason }, - { type: 'index.create', index: source, reason }, + { type: 'IndexDrop', indexName: target.name, reason }, + { type: 'IndexCreate', index: source, reason }, ]; } diff --git a/server/src/sql-tools/diff/comparers/parameter.comparer.spec.ts b/server/src/sql-tools/comparers/parameter.comparer.spec.ts similarity index 86% rename from server/src/sql-tools/diff/comparers/parameter.comparer.spec.ts rename to server/src/sql-tools/comparers/parameter.comparer.spec.ts index 517ec79341..cd1520faff 100644 --- a/server/src/sql-tools/diff/comparers/parameter.comparer.spec.ts +++ b/server/src/sql-tools/comparers/parameter.comparer.spec.ts @@ -1,4 +1,4 @@ -import { compareParameters } from 'src/sql-tools/diff/comparers/parameter.comparer'; +import { compareParameters } from 'src/sql-tools/comparers/parameter.comparer'; import { DatabaseParameter, Reason } from 'src/sql-tools/types'; import { describe, expect, it } from 'vitest'; @@ -15,7 +15,7 @@ describe('compareParameters', () => { it('should work', () => { expect(compareParameters.onExtra(testParameter)).toEqual([ { - type: 'parameter.reset', + type: 'ParameterReset', databaseName: 'immich', parameterName: 'test', reason: Reason.MissingInSource, @@ -28,7 +28,7 @@ describe('compareParameters', () => { it('should work', () => { expect(compareParameters.onMissing(testParameter)).toEqual([ { - type: 'parameter.set', + type: 'ParameterSet', parameter: testParameter, reason: Reason.MissingInTarget, }, diff --git a/server/src/sql-tools/diff/comparers/parameter.comparer.ts b/server/src/sql-tools/comparers/parameter.comparer.ts similarity index 88% rename from server/src/sql-tools/diff/comparers/parameter.comparer.ts rename to server/src/sql-tools/comparers/parameter.comparer.ts index 03c24bada7..d1a33ad090 100644 --- a/server/src/sql-tools/diff/comparers/parameter.comparer.ts +++ b/server/src/sql-tools/comparers/parameter.comparer.ts @@ -3,14 +3,14 @@ import { Comparer, DatabaseParameter, Reason } from 'src/sql-tools/types'; export const compareParameters: Comparer = { onMissing: (source) => [ { - type: 'parameter.set', + type: 'ParameterSet', parameter: source, reason: Reason.MissingInTarget, }, ], onExtra: (target) => [ { - type: 'parameter.reset', + type: 'ParameterReset', databaseName: target.databaseName, parameterName: target.name, reason: Reason.MissingInSource, diff --git a/server/src/sql-tools/diff/comparers/table.comparer.spec.ts b/server/src/sql-tools/comparers/table.comparer.spec.ts similarity index 86% rename from server/src/sql-tools/diff/comparers/table.comparer.spec.ts rename to server/src/sql-tools/comparers/table.comparer.spec.ts index 0b1873b2ba..575e25ab44 100644 --- a/server/src/sql-tools/diff/comparers/table.comparer.spec.ts +++ b/server/src/sql-tools/comparers/table.comparer.spec.ts @@ -1,4 +1,4 @@ -import { compareTables } from 'src/sql-tools/diff/comparers/table.comparer'; +import { compareTables } from 'src/sql-tools/comparers/table.comparer'; import { DatabaseTable, Reason } from 'src/sql-tools/types'; import { describe, expect, it } from 'vitest'; @@ -16,7 +16,7 @@ describe('compareParameters', () => { it('should work', () => { expect(compareTables.onExtra(testTable)).toEqual([ { - type: 'table.drop', + type: 'TableDrop', tableName: 'test', reason: Reason.MissingInSource, }, @@ -28,7 +28,7 @@ describe('compareParameters', () => { it('should work', () => { expect(compareTables.onMissing(testTable)).toEqual([ { - type: 'table.create', + type: 'TableCreate', table: testTable, reason: Reason.MissingInTarget, }, diff --git a/server/src/sql-tools/diff/comparers/table.comparer.ts b/server/src/sql-tools/comparers/table.comparer.ts similarity index 54% rename from server/src/sql-tools/diff/comparers/table.comparer.ts rename to server/src/sql-tools/comparers/table.comparer.ts index 8f6d0e04f8..c920a1d07a 100644 --- a/server/src/sql-tools/diff/comparers/table.comparer.ts +++ b/server/src/sql-tools/comparers/table.comparer.ts @@ -1,47 +1,33 @@ -import { compareColumns } from 'src/sql-tools/diff/comparers/column.comparer'; -import { compareConstraints } from 'src/sql-tools/diff/comparers/constraint.comparer'; -import { compareIndexes } from 'src/sql-tools/diff/comparers/index.comparer'; -import { compareTriggers } from 'src/sql-tools/diff/comparers/trigger.comparer'; +import { compareColumns } from 'src/sql-tools/comparers/column.comparer'; +import { compareConstraints } from 'src/sql-tools/comparers/constraint.comparer'; +import { compareIndexes } from 'src/sql-tools/comparers/index.comparer'; +import { compareTriggers } from 'src/sql-tools/comparers/trigger.comparer'; import { compare } from 'src/sql-tools/helpers'; import { Comparer, DatabaseTable, Reason, SchemaDiff } from 'src/sql-tools/types'; +const newTable = (name: string) => ({ + name, + columns: [], + indexes: [], + constraints: [], + triggers: [], + synchronize: true, +}); + export const compareTables: Comparer = { onMissing: (source) => [ { - type: 'table.create', + type: 'TableCreate', table: source, reason: Reason.MissingInTarget, }, // TODO merge constraints into table create record when possible - ...compareTable( - source, - { - name: source.name, - columns: [], - indexes: [], - constraints: [], - triggers: [], - synchronize: true, - }, - - { columns: false }, - ), + ...compareTable(source, newTable(source.name), { columns: false }), ], onExtra: (target) => [ - ...compareTable( - { - name: target.name, - columns: [], - indexes: [], - constraints: [], - triggers: [], - synchronize: true, - }, - target, - { columns: false }, - ), + ...compareTable(newTable(target.name), target, { columns: false }), { - type: 'table.drop', + type: 'TableDrop', tableName: target.name, reason: Reason.MissingInSource, }, diff --git a/server/src/sql-tools/diff/comparers/trigger.comparer.spec.ts b/server/src/sql-tools/comparers/trigger.comparer.spec.ts similarity index 87% rename from server/src/sql-tools/diff/comparers/trigger.comparer.spec.ts rename to server/src/sql-tools/comparers/trigger.comparer.spec.ts index 800cb4d66b..731fae8da2 100644 --- a/server/src/sql-tools/diff/comparers/trigger.comparer.spec.ts +++ b/server/src/sql-tools/comparers/trigger.comparer.spec.ts @@ -1,4 +1,4 @@ -import { compareTriggers } from 'src/sql-tools/diff/comparers/trigger.comparer'; +import { compareTriggers } from 'src/sql-tools/comparers/trigger.comparer'; import { DatabaseTrigger, Reason } from 'src/sql-tools/types'; import { describe, expect, it } from 'vitest'; @@ -17,7 +17,7 @@ describe('compareTriggers', () => { it('should work', () => { expect(compareTriggers.onExtra(testTrigger)).toEqual([ { - type: 'trigger.drop', + type: 'TriggerDrop', tableName: 'table1', triggerName: 'test', reason: Reason.MissingInSource, @@ -30,7 +30,7 @@ describe('compareTriggers', () => { it('should work', () => { expect(compareTriggers.onMissing(testTrigger)).toEqual([ { - type: 'trigger.create', + type: 'TriggerCreate', trigger: testTrigger, reason: Reason.MissingInTarget, }, @@ -47,42 +47,42 @@ describe('compareTriggers', () => { const source: DatabaseTrigger = { ...testTrigger, functionName: 'my_new_name' }; const target: DatabaseTrigger = { ...testTrigger, functionName: 'my_old_name' }; const reason = `function is different (my_new_name vs my_old_name)`; - expect(compareTriggers.onCompare(source, target)).toEqual([{ type: 'trigger.create', trigger: source, reason }]); + expect(compareTriggers.onCompare(source, target)).toEqual([{ type: 'TriggerCreate', trigger: source, reason }]); }); it('should detect a change in actions', () => { const source: DatabaseTrigger = { ...testTrigger, actions: ['delete'] }; const target: DatabaseTrigger = { ...testTrigger, actions: ['delete', 'insert'] }; const reason = `action is different (delete vs delete,insert)`; - expect(compareTriggers.onCompare(source, target)).toEqual([{ type: 'trigger.create', trigger: source, reason }]); + expect(compareTriggers.onCompare(source, target)).toEqual([{ type: 'TriggerCreate', trigger: source, reason }]); }); it('should detect a change in timing', () => { const source: DatabaseTrigger = { ...testTrigger, timing: 'before' }; const target: DatabaseTrigger = { ...testTrigger, timing: 'after' }; const reason = `timing method is different (before vs after)`; - expect(compareTriggers.onCompare(source, target)).toEqual([{ type: 'trigger.create', trigger: source, reason }]); + expect(compareTriggers.onCompare(source, target)).toEqual([{ type: 'TriggerCreate', trigger: source, reason }]); }); it('should detect a change in scope', () => { const source: DatabaseTrigger = { ...testTrigger, scope: 'row' }; const target: DatabaseTrigger = { ...testTrigger, scope: 'statement' }; const reason = `scope is different (row vs statement)`; - expect(compareTriggers.onCompare(source, target)).toEqual([{ type: 'trigger.create', trigger: source, reason }]); + expect(compareTriggers.onCompare(source, target)).toEqual([{ type: 'TriggerCreate', trigger: source, reason }]); }); it('should detect a change in new table reference', () => { const source: DatabaseTrigger = { ...testTrigger, referencingNewTableAs: 'new_table' }; const target: DatabaseTrigger = { ...testTrigger, referencingNewTableAs: undefined }; const reason = `new table reference is different (new_table vs undefined)`; - expect(compareTriggers.onCompare(source, target)).toEqual([{ type: 'trigger.create', trigger: source, reason }]); + expect(compareTriggers.onCompare(source, target)).toEqual([{ type: 'TriggerCreate', trigger: source, reason }]); }); it('should detect a change in old table reference', () => { const source: DatabaseTrigger = { ...testTrigger, referencingOldTableAs: 'old_table' }; const target: DatabaseTrigger = { ...testTrigger, referencingOldTableAs: undefined }; const reason = `old table reference is different (old_table vs undefined)`; - expect(compareTriggers.onCompare(source, target)).toEqual([{ type: 'trigger.create', trigger: source, reason }]); + expect(compareTriggers.onCompare(source, target)).toEqual([{ type: 'TriggerCreate', trigger: source, reason }]); }); }); }); diff --git a/server/src/sql-tools/diff/comparers/trigger.comparer.ts b/server/src/sql-tools/comparers/trigger.comparer.ts similarity index 92% rename from server/src/sql-tools/diff/comparers/trigger.comparer.ts rename to server/src/sql-tools/comparers/trigger.comparer.ts index 38adae9905..da1de6e48b 100644 --- a/server/src/sql-tools/diff/comparers/trigger.comparer.ts +++ b/server/src/sql-tools/comparers/trigger.comparer.ts @@ -3,14 +3,14 @@ import { Comparer, DatabaseTrigger, Reason } from 'src/sql-tools/types'; export const compareTriggers: Comparer = { onMissing: (source) => [ { - type: 'trigger.create', + type: 'TriggerCreate', trigger: source, reason: Reason.MissingInTarget, }, ], onExtra: (target) => [ { - type: 'trigger.drop', + type: 'TriggerDrop', tableName: target.tableName, triggerName: target.name, reason: Reason.MissingInSource, @@ -33,7 +33,7 @@ export const compareTriggers: Comparer = { } if (reason) { - return [{ type: 'trigger.create', trigger: source, reason }]; + return [{ type: 'TriggerCreate', trigger: source, reason }]; } return []; diff --git a/server/src/sql-tools/from-code/decorators/after-delete.decorator.ts b/server/src/sql-tools/decorators/after-delete.decorator.ts similarity index 81% rename from server/src/sql-tools/from-code/decorators/after-delete.decorator.ts rename to server/src/sql-tools/decorators/after-delete.decorator.ts index 7713c4b625..181bfab6c8 100644 --- a/server/src/sql-tools/from-code/decorators/after-delete.decorator.ts +++ b/server/src/sql-tools/decorators/after-delete.decorator.ts @@ -1,4 +1,4 @@ -import { TriggerFunction, TriggerFunctionOptions } from 'src/sql-tools/from-code/decorators/trigger-function.decorator'; +import { TriggerFunction, TriggerFunctionOptions } from 'src/sql-tools/decorators/trigger-function.decorator'; export const AfterDeleteTrigger = (options: Omit) => TriggerFunction({ diff --git a/server/src/sql-tools/from-code/decorators/after-insert.decorator.ts b/server/src/sql-tools/decorators/after-insert.decorator.ts similarity index 81% rename from server/src/sql-tools/from-code/decorators/after-insert.decorator.ts rename to server/src/sql-tools/decorators/after-insert.decorator.ts index 103d59b4fc..c302a5cebe 100644 --- a/server/src/sql-tools/from-code/decorators/after-insert.decorator.ts +++ b/server/src/sql-tools/decorators/after-insert.decorator.ts @@ -1,4 +1,4 @@ -import { TriggerFunction, TriggerFunctionOptions } from 'src/sql-tools/from-code/decorators/trigger-function.decorator'; +import { TriggerFunction, TriggerFunctionOptions } from 'src/sql-tools/decorators/trigger-function.decorator'; export const AfterInsertTrigger = (options: Omit) => TriggerFunction({ diff --git a/server/src/sql-tools/from-code/decorators/before-update.decorator.ts b/server/src/sql-tools/decorators/before-update.decorator.ts similarity index 81% rename from server/src/sql-tools/from-code/decorators/before-update.decorator.ts rename to server/src/sql-tools/decorators/before-update.decorator.ts index 03dad25ed0..2119e29c9b 100644 --- a/server/src/sql-tools/from-code/decorators/before-update.decorator.ts +++ b/server/src/sql-tools/decorators/before-update.decorator.ts @@ -1,4 +1,4 @@ -import { TriggerFunction, TriggerFunctionOptions } from 'src/sql-tools/from-code/decorators/trigger-function.decorator'; +import { TriggerFunction, TriggerFunctionOptions } from 'src/sql-tools/decorators/trigger-function.decorator'; export const BeforeUpdateTrigger = (options: Omit) => TriggerFunction({ diff --git a/server/src/sql-tools/from-code/decorators/check.decorator.ts b/server/src/sql-tools/decorators/check.decorator.ts similarity index 84% rename from server/src/sql-tools/from-code/decorators/check.decorator.ts rename to server/src/sql-tools/decorators/check.decorator.ts index 7d046df0c3..56fe1ecc3f 100644 --- a/server/src/sql-tools/from-code/decorators/check.decorator.ts +++ b/server/src/sql-tools/decorators/check.decorator.ts @@ -1,4 +1,4 @@ -import { register } from 'src/sql-tools/from-code/register'; +import { register } from 'src/sql-tools/register'; export type CheckOptions = { name?: string; diff --git a/server/src/sql-tools/from-code/decorators/column.decorator.ts b/server/src/sql-tools/decorators/column.decorator.ts similarity index 93% rename from server/src/sql-tools/from-code/decorators/column.decorator.ts rename to server/src/sql-tools/decorators/column.decorator.ts index 7b00af80cc..adb3d0ed59 100644 --- a/server/src/sql-tools/from-code/decorators/column.decorator.ts +++ b/server/src/sql-tools/decorators/column.decorator.ts @@ -1,5 +1,5 @@ -import { register } from 'src/sql-tools/from-code/register'; import { asOptions } from 'src/sql-tools/helpers'; +import { register } from 'src/sql-tools/register'; import { ColumnStorage, ColumnType, DatabaseEnum } from 'src/sql-tools/types'; export type ColumnValue = null | boolean | string | number | object | Date | (() => string); diff --git a/server/src/sql-tools/from-code/decorators/configuration-parameter.decorator.ts b/server/src/sql-tools/decorators/configuration-parameter.decorator.ts similarity index 76% rename from server/src/sql-tools/from-code/decorators/configuration-parameter.decorator.ts rename to server/src/sql-tools/decorators/configuration-parameter.decorator.ts index 6a987884d1..953027d25c 100644 --- a/server/src/sql-tools/from-code/decorators/configuration-parameter.decorator.ts +++ b/server/src/sql-tools/decorators/configuration-parameter.decorator.ts @@ -1,5 +1,5 @@ -import { ColumnValue } from 'src/sql-tools/from-code/decorators/column.decorator'; -import { register } from 'src/sql-tools/from-code/register'; +import { ColumnValue } from 'src/sql-tools/decorators/column.decorator'; +import { register } from 'src/sql-tools/register'; import { ParameterScope } from 'src/sql-tools/types'; export type ConfigurationParameterOptions = { diff --git a/server/src/sql-tools/from-code/decorators/create-date-column.decorator.ts b/server/src/sql-tools/decorators/create-date-column.decorator.ts similarity index 67% rename from server/src/sql-tools/from-code/decorators/create-date-column.decorator.ts rename to server/src/sql-tools/decorators/create-date-column.decorator.ts index 8f81d59914..1a3362a614 100644 --- a/server/src/sql-tools/from-code/decorators/create-date-column.decorator.ts +++ b/server/src/sql-tools/decorators/create-date-column.decorator.ts @@ -1,4 +1,4 @@ -import { Column, ColumnOptions } from 'src/sql-tools/from-code/decorators/column.decorator'; +import { Column, ColumnOptions } from 'src/sql-tools/decorators/column.decorator'; export const CreateDateColumn = (options: ColumnOptions = {}): PropertyDecorator => { return Column({ diff --git a/server/src/sql-tools/from-code/decorators/database.decorator.ts b/server/src/sql-tools/decorators/database.decorator.ts similarity index 84% rename from server/src/sql-tools/from-code/decorators/database.decorator.ts rename to server/src/sql-tools/decorators/database.decorator.ts index 3bcc464f74..17b2460df6 100644 --- a/server/src/sql-tools/from-code/decorators/database.decorator.ts +++ b/server/src/sql-tools/decorators/database.decorator.ts @@ -1,4 +1,4 @@ -import { register } from 'src/sql-tools/from-code/register'; +import { register } from 'src/sql-tools/register'; export type DatabaseOptions = { name?: string; diff --git a/server/src/sql-tools/from-code/decorators/delete-date-column.decorator.ts b/server/src/sql-tools/decorators/delete-date-column.decorator.ts similarity index 66% rename from server/src/sql-tools/from-code/decorators/delete-date-column.decorator.ts rename to server/src/sql-tools/decorators/delete-date-column.decorator.ts index 518c4e76fc..ca5427c27f 100644 --- a/server/src/sql-tools/from-code/decorators/delete-date-column.decorator.ts +++ b/server/src/sql-tools/decorators/delete-date-column.decorator.ts @@ -1,4 +1,4 @@ -import { Column, ColumnOptions } from 'src/sql-tools/from-code/decorators/column.decorator'; +import { Column, ColumnOptions } from 'src/sql-tools/decorators/column.decorator'; export const DeleteDateColumn = (options: ColumnOptions = {}): PropertyDecorator => { return Column({ diff --git a/server/src/sql-tools/from-code/decorators/extension.decorator.ts b/server/src/sql-tools/decorators/extension.decorator.ts similarity index 86% rename from server/src/sql-tools/from-code/decorators/extension.decorator.ts rename to server/src/sql-tools/decorators/extension.decorator.ts index c43a18c16f..d431cbfd02 100644 --- a/server/src/sql-tools/from-code/decorators/extension.decorator.ts +++ b/server/src/sql-tools/decorators/extension.decorator.ts @@ -1,5 +1,5 @@ -import { register } from 'src/sql-tools/from-code/register'; import { asOptions } from 'src/sql-tools/helpers'; +import { register } from 'src/sql-tools/register'; export type ExtensionOptions = { name: string; diff --git a/server/src/sql-tools/from-code/decorators/extensions.decorator.ts b/server/src/sql-tools/decorators/extensions.decorator.ts similarity index 88% rename from server/src/sql-tools/from-code/decorators/extensions.decorator.ts rename to server/src/sql-tools/decorators/extensions.decorator.ts index 9d3769a210..724446c5fa 100644 --- a/server/src/sql-tools/from-code/decorators/extensions.decorator.ts +++ b/server/src/sql-tools/decorators/extensions.decorator.ts @@ -1,5 +1,5 @@ -import { register } from 'src/sql-tools/from-code/register'; import { asOptions } from 'src/sql-tools/helpers'; +import { register } from 'src/sql-tools/register'; export type ExtensionsOptions = { name: string; diff --git a/server/src/sql-tools/decorators/foreign-key-column.decorator.ts b/server/src/sql-tools/decorators/foreign-key-column.decorator.ts new file mode 100644 index 0000000000..c9c83f010d --- /dev/null +++ b/server/src/sql-tools/decorators/foreign-key-column.decorator.ts @@ -0,0 +1,16 @@ +/* eslint-disable @typescript-eslint/no-unsafe-function-type */ +import { ForeignKeyAction } from 'src/sql-tools//decorators/foreign-key-constraint.decorator'; +import { ColumnBaseOptions } from 'src/sql-tools/decorators/column.decorator'; +import { register } from 'src/sql-tools/register'; + +export type ForeignKeyColumnOptions = ColumnBaseOptions & { + onUpdate?: ForeignKeyAction; + onDelete?: ForeignKeyAction; + constraintName?: string; +}; + +export const ForeignKeyColumn = (target: () => Function, options: ForeignKeyColumnOptions): PropertyDecorator => { + return (object: object, propertyName: string | symbol) => { + register({ type: 'foreignKeyColumn', item: { object, propertyName, options, target } }); + }; +}; diff --git a/server/src/sql-tools/from-code/decorators/foreign-key-constraint.decorator.ts b/server/src/sql-tools/decorators/foreign-key-constraint.decorator.ts similarity index 92% rename from server/src/sql-tools/from-code/decorators/foreign-key-constraint.decorator.ts rename to server/src/sql-tools/decorators/foreign-key-constraint.decorator.ts index 7d18a9fda0..e5d2f513dc 100644 --- a/server/src/sql-tools/from-code/decorators/foreign-key-constraint.decorator.ts +++ b/server/src/sql-tools/decorators/foreign-key-constraint.decorator.ts @@ -1,4 +1,4 @@ -import { register } from 'src/sql-tools/from-code/register'; +import { register } from 'src/sql-tools/register'; export type ForeignKeyAction = 'CASCADE' | 'SET NULL' | 'SET DEFAULT' | 'RESTRICT' | 'NO ACTION'; diff --git a/server/src/sql-tools/from-code/decorators/generated-column.decorator.ts b/server/src/sql-tools/decorators/generated-column.decorator.ts similarity index 95% rename from server/src/sql-tools/from-code/decorators/generated-column.decorator.ts rename to server/src/sql-tools/decorators/generated-column.decorator.ts index 82d3131b5c..4338b4146c 100644 --- a/server/src/sql-tools/from-code/decorators/generated-column.decorator.ts +++ b/server/src/sql-tools/decorators/generated-column.decorator.ts @@ -1,4 +1,4 @@ -import { Column, ColumnOptions, ColumnValue } from 'src/sql-tools/from-code/decorators/column.decorator'; +import { Column, ColumnOptions, ColumnValue } from 'src/sql-tools/decorators/column.decorator'; import { ColumnType } from 'src/sql-tools/types'; export type GeneratedColumnStrategy = 'uuid' | 'identity'; diff --git a/server/src/sql-tools/from-code/decorators/index.decorator.ts b/server/src/sql-tools/decorators/index.decorator.ts similarity index 89% rename from server/src/sql-tools/from-code/decorators/index.decorator.ts rename to server/src/sql-tools/decorators/index.decorator.ts index 5d90c4f58d..1b6d38e390 100644 --- a/server/src/sql-tools/from-code/decorators/index.decorator.ts +++ b/server/src/sql-tools/decorators/index.decorator.ts @@ -1,5 +1,5 @@ -import { register } from 'src/sql-tools/from-code/register'; import { asOptions } from 'src/sql-tools/helpers'; +import { register } from 'src/sql-tools/register'; export type IndexOptions = { name?: string; diff --git a/server/src/sql-tools/decorators/index.ts b/server/src/sql-tools/decorators/index.ts new file mode 100644 index 0000000000..86affe5002 --- /dev/null +++ b/server/src/sql-tools/decorators/index.ts @@ -0,0 +1,22 @@ +export * from 'src/sql-tools/decorators/after-delete.decorator'; +export * from 'src/sql-tools/decorators/after-insert.decorator'; +export * from 'src/sql-tools/decorators/before-update.decorator'; +export * from 'src/sql-tools/decorators/check.decorator'; +export * from 'src/sql-tools/decorators/column.decorator'; +export * from 'src/sql-tools/decorators/configuration-parameter.decorator'; +export * from 'src/sql-tools/decorators/create-date-column.decorator'; +export * from 'src/sql-tools/decorators/database.decorator'; +export * from 'src/sql-tools/decorators/delete-date-column.decorator'; +export * from 'src/sql-tools/decorators/extension.decorator'; +export * from 'src/sql-tools/decorators/extensions.decorator'; +export * from 'src/sql-tools/decorators/foreign-key-column.decorator'; +export * from 'src/sql-tools/decorators/foreign-key-constraint.decorator'; +export * from 'src/sql-tools/decorators/generated-column.decorator'; +export * from 'src/sql-tools/decorators/index.decorator'; +export * from 'src/sql-tools/decorators/primary-column.decorator'; +export * from 'src/sql-tools/decorators/primary-generated-column.decorator'; +export * from 'src/sql-tools/decorators/table.decorator'; +export * from 'src/sql-tools/decorators/trigger-function.decorator'; +export * from 'src/sql-tools/decorators/trigger.decorator'; +export * from 'src/sql-tools/decorators/unique.decorator'; +export * from 'src/sql-tools/decorators/update-date-column.decorator'; diff --git a/server/src/sql-tools/from-code/decorators/primary-column.decorator.ts b/server/src/sql-tools/decorators/primary-column.decorator.ts similarity index 56% rename from server/src/sql-tools/from-code/decorators/primary-column.decorator.ts rename to server/src/sql-tools/decorators/primary-column.decorator.ts index f702965675..e605b4be5d 100644 --- a/server/src/sql-tools/from-code/decorators/primary-column.decorator.ts +++ b/server/src/sql-tools/decorators/primary-column.decorator.ts @@ -1,3 +1,3 @@ -import { Column, ColumnOptions } from 'src/sql-tools/from-code/decorators/column.decorator'; +import { Column, ColumnOptions } from 'src/sql-tools/decorators/column.decorator'; export const PrimaryColumn = (options: Omit = {}) => Column({ ...options, primary: true }); diff --git a/server/src/sql-tools/from-code/decorators/primary-generated-column.decorator.ts b/server/src/sql-tools/decorators/primary-generated-column.decorator.ts similarity index 79% rename from server/src/sql-tools/from-code/decorators/primary-generated-column.decorator.ts rename to server/src/sql-tools/decorators/primary-generated-column.decorator.ts index 9dc8ca6817..25e125ebf6 100644 --- a/server/src/sql-tools/from-code/decorators/primary-generated-column.decorator.ts +++ b/server/src/sql-tools/decorators/primary-generated-column.decorator.ts @@ -1,4 +1,4 @@ -import { GenerateColumnOptions, GeneratedColumn } from 'src/sql-tools/from-code/decorators/generated-column.decorator'; +import { GenerateColumnOptions, GeneratedColumn } from 'src/sql-tools/decorators/generated-column.decorator'; export const PrimaryGeneratedColumn = (options: Omit = {}) => GeneratedColumn({ ...options, primary: true }); diff --git a/server/src/sql-tools/from-code/decorators/table.decorator.ts b/server/src/sql-tools/decorators/table.decorator.ts similarity index 88% rename from server/src/sql-tools/from-code/decorators/table.decorator.ts rename to server/src/sql-tools/decorators/table.decorator.ts index 589a88aa29..7ea5882147 100644 --- a/server/src/sql-tools/from-code/decorators/table.decorator.ts +++ b/server/src/sql-tools/decorators/table.decorator.ts @@ -1,5 +1,5 @@ -import { register } from 'src/sql-tools/from-code/register'; import { asOptions } from 'src/sql-tools/helpers'; +import { register } from 'src/sql-tools/register'; export type TableOptions = { name?: string; diff --git a/server/src/sql-tools/from-code/decorators/trigger-function.decorator.ts b/server/src/sql-tools/decorators/trigger-function.decorator.ts similarity index 78% rename from server/src/sql-tools/from-code/decorators/trigger-function.decorator.ts rename to server/src/sql-tools/decorators/trigger-function.decorator.ts index cb2fa36800..17016f7946 100644 --- a/server/src/sql-tools/from-code/decorators/trigger-function.decorator.ts +++ b/server/src/sql-tools/decorators/trigger-function.decorator.ts @@ -1,4 +1,4 @@ -import { Trigger, TriggerOptions } from 'src/sql-tools/from-code/decorators/trigger.decorator'; +import { Trigger, TriggerOptions } from 'src/sql-tools/decorators/trigger.decorator'; import { DatabaseFunction } from 'src/sql-tools/types'; export type TriggerFunctionOptions = Omit & { function: DatabaseFunction }; diff --git a/server/src/sql-tools/from-code/decorators/trigger.decorator.ts b/server/src/sql-tools/decorators/trigger.decorator.ts similarity index 90% rename from server/src/sql-tools/from-code/decorators/trigger.decorator.ts rename to server/src/sql-tools/decorators/trigger.decorator.ts index e0c0ccf3e4..ce9a5c17f7 100644 --- a/server/src/sql-tools/from-code/decorators/trigger.decorator.ts +++ b/server/src/sql-tools/decorators/trigger.decorator.ts @@ -1,4 +1,4 @@ -import { register } from 'src/sql-tools/from-code/register'; +import { register } from 'src/sql-tools/register'; import { TriggerAction, TriggerScope, TriggerTiming } from 'src/sql-tools/types'; export type TriggerOptions = { diff --git a/server/src/sql-tools/from-code/decorators/unique.decorator.ts b/server/src/sql-tools/decorators/unique.decorator.ts similarity index 84% rename from server/src/sql-tools/from-code/decorators/unique.decorator.ts rename to server/src/sql-tools/decorators/unique.decorator.ts index c7186d7296..1f61fccb6f 100644 --- a/server/src/sql-tools/from-code/decorators/unique.decorator.ts +++ b/server/src/sql-tools/decorators/unique.decorator.ts @@ -1,4 +1,4 @@ -import { register } from 'src/sql-tools/from-code/register'; +import { register } from 'src/sql-tools/register'; export type UniqueOptions = { name?: string; diff --git a/server/src/sql-tools/from-code/decorators/update-date-column.decorator.ts b/server/src/sql-tools/decorators/update-date-column.decorator.ts similarity index 67% rename from server/src/sql-tools/from-code/decorators/update-date-column.decorator.ts rename to server/src/sql-tools/decorators/update-date-column.decorator.ts index ddc7a6a1e8..68dd50c617 100644 --- a/server/src/sql-tools/from-code/decorators/update-date-column.decorator.ts +++ b/server/src/sql-tools/decorators/update-date-column.decorator.ts @@ -1,4 +1,4 @@ -import { Column, ColumnOptions } from 'src/sql-tools/from-code/decorators/column.decorator'; +import { Column, ColumnOptions } from 'src/sql-tools/decorators/column.decorator'; export const UpdateDateColumn = (options: ColumnOptions = {}): PropertyDecorator => { return Column({ diff --git a/server/src/sql-tools/diff/index.ts b/server/src/sql-tools/diff/index.ts deleted file mode 100644 index dd90293dc3..0000000000 --- a/server/src/sql-tools/diff/index.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { compareEnums } from 'src/sql-tools/diff/comparers/enum.comparer'; -import { compareExtensions } from 'src/sql-tools/diff/comparers/extension.comparer'; -import { compareFunctions } from 'src/sql-tools/diff/comparers/function.comparer'; -import { compareParameters } from 'src/sql-tools/diff/comparers/parameter.comparer'; -import { compareTables } from 'src/sql-tools/diff/comparers/table.comparer'; -import { compare } from 'src/sql-tools/helpers'; -import { schemaDiffToSql } from 'src/sql-tools/to-sql'; -import { - DatabaseConstraintType, - DatabaseSchema, - SchemaDiff, - SchemaDiffOptions, - SchemaDiffToSqlOptions, -} from 'src/sql-tools/types'; - -/** - * Compute the difference between two database schemas - */ -export const schemaDiff = (source: DatabaseSchema, target: DatabaseSchema, options: SchemaDiffOptions = {}) => { - const items = [ - ...compare(source.parameters, target.parameters, options.parameters, compareParameters), - ...compare(source.extensions, target.extensions, options.extension, compareExtensions), - ...compare(source.functions, target.functions, options.functions, compareFunctions), - ...compare(source.enums, target.enums, options.enums, compareEnums), - ...compare(source.tables, target.tables, options.tables, compareTables), - ]; - - type SchemaName = SchemaDiff['type']; - const itemMap: Record = { - 'enum.create': [], - 'enum.drop': [], - 'extension.create': [], - 'extension.drop': [], - 'function.create': [], - 'function.drop': [], - 'table.create': [], - 'table.drop': [], - 'column.add': [], - 'column.alter': [], - 'column.drop': [], - 'constraint.add': [], - 'constraint.drop': [], - 'index.create': [], - 'index.drop': [], - 'trigger.create': [], - 'trigger.drop': [], - 'parameter.set': [], - 'parameter.reset': [], - }; - - for (const item of items) { - itemMap[item.type].push(item); - } - - const constraintAdds = itemMap['constraint.add'].filter((item) => item.type === 'constraint.add'); - - const orderedItems = [ - ...itemMap['extension.create'], - ...itemMap['function.create'], - ...itemMap['parameter.set'], - ...itemMap['parameter.reset'], - ...itemMap['enum.create'], - ...itemMap['trigger.drop'], - ...itemMap['index.drop'], - ...itemMap['constraint.drop'], - ...itemMap['table.create'], - ...itemMap['column.alter'], - ...itemMap['column.add'], - ...constraintAdds.filter(({ constraint }) => constraint.type === DatabaseConstraintType.PRIMARY_KEY), - ...constraintAdds.filter(({ constraint }) => constraint.type === DatabaseConstraintType.FOREIGN_KEY), - ...constraintAdds.filter(({ constraint }) => constraint.type === DatabaseConstraintType.UNIQUE), - ...constraintAdds.filter(({ constraint }) => constraint.type === DatabaseConstraintType.CHECK), - ...itemMap['index.create'], - ...itemMap['trigger.create'], - ...itemMap['column.drop'], - ...itemMap['table.drop'], - ...itemMap['enum.drop'], - ...itemMap['function.drop'], - ]; - - return { - items: orderedItems, - asSql: (options?: SchemaDiffToSqlOptions) => schemaDiffToSql(orderedItems, options), - }; -}; diff --git a/server/src/sql-tools/from-code/decorators/foreign-key-column.decorator.ts b/server/src/sql-tools/from-code/decorators/foreign-key-column.decorator.ts deleted file mode 100644 index d2b7d623a7..0000000000 --- a/server/src/sql-tools/from-code/decorators/foreign-key-column.decorator.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ColumnBaseOptions } from 'src/sql-tools/from-code/decorators/column.decorator'; -import { ForeignKeyAction } from 'src/sql-tools/from-code/decorators/foreign-key-constraint.decorator'; -import { register } from 'src/sql-tools/from-code/register'; - -export type ForeignKeyColumnOptions = ColumnBaseOptions & { - onUpdate?: ForeignKeyAction; - onDelete?: ForeignKeyAction; - constraintName?: string; -}; - -export const ForeignKeyColumn = (target: () => object, options: ForeignKeyColumnOptions): PropertyDecorator => { - return (object: object, propertyName: string | symbol) => { - register({ type: 'foreignKeyColumn', item: { object, propertyName, options, target } }); - }; -}; diff --git a/server/src/sql-tools/from-code/index.spec.ts b/server/src/sql-tools/from-code/index.spec.ts deleted file mode 100644 index 5306722c76..0000000000 --- a/server/src/sql-tools/from-code/index.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { readdirSync } from 'node:fs'; -import { join } from 'node:path'; -import { reset, schemaFromCode } from 'src/sql-tools/from-code'; -import { describe, expect, it } from 'vitest'; - -describe(schemaFromCode.name, () => { - beforeEach(() => { - reset(); - }); - - it('should work', () => { - expect(schemaFromCode()).toEqual({ - name: 'postgres', - schemaName: 'public', - functions: [], - enums: [], - extensions: [], - parameters: [], - tables: [], - warnings: [], - }); - }); - - describe('test files', () => { - const files = readdirSync('test/sql-tools', { withFileTypes: true }); - for (const file of files) { - const filePath = join(file.parentPath, file.name); - it(filePath, async () => { - const module = await import(filePath); - expect(module.description).toBeDefined(); - expect(module.schema).toBeDefined(); - expect(schemaFromCode(), module.description).toEqual(module.schema); - }); - } - }); -}); diff --git a/server/src/sql-tools/from-code/index.ts b/server/src/sql-tools/from-code/index.ts deleted file mode 100644 index d820f236df..0000000000 --- a/server/src/sql-tools/from-code/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -import 'reflect-metadata'; -import { processCheckConstraints } from 'src/sql-tools/from-code/processors/check-constraint.processor'; -import { processColumns } from 'src/sql-tools/from-code/processors/column.processor'; -import { processConfigurationParameters } from 'src/sql-tools/from-code/processors/configuration-parameter.processor'; -import { processDatabases } from 'src/sql-tools/from-code/processors/database.processor'; -import { processEnums } from 'src/sql-tools/from-code/processors/enum.processor'; -import { processExtensions } from 'src/sql-tools/from-code/processors/extension.processor'; -import { processForeignKeyColumns } from 'src/sql-tools/from-code/processors/foreign-key-column.processor'; -import { processForeignKeyConstraints } from 'src/sql-tools/from-code/processors/foreign-key-constraint.processor'; -import { processFunctions } from 'src/sql-tools/from-code/processors/function.processor'; -import { processIndexes } from 'src/sql-tools/from-code/processors/index.processor'; -import { processPrimaryKeyConstraints } from 'src/sql-tools/from-code/processors/primary-key-contraint.processor'; -import { processTables } from 'src/sql-tools/from-code/processors/table.processor'; -import { processTriggers } from 'src/sql-tools/from-code/processors/trigger.processor'; -import { Processor, SchemaBuilder } from 'src/sql-tools/from-code/processors/type'; -import { processUniqueConstraints } from 'src/sql-tools/from-code/processors/unique-constraint.processor'; -import { getRegisteredItems, resetRegisteredItems } from 'src/sql-tools/from-code/register'; -import { DatabaseSchema } from 'src/sql-tools/types'; - -let initialized = false; -let schema: DatabaseSchema; - -export const reset = () => { - initialized = false; - resetRegisteredItems(); -}; - -const processors: Processor[] = [ - processDatabases, - processConfigurationParameters, - processEnums, - processExtensions, - processFunctions, - processTables, - processColumns, - processForeignKeyColumns, - processForeignKeyConstraints, - processUniqueConstraints, - processCheckConstraints, - processPrimaryKeyConstraints, - processIndexes, - processTriggers, -]; - -export type SchemaFromCodeOptions = { - /** automatically create indexes on foreign key columns */ - createForeignKeyIndexes?: boolean; -}; -export const schemaFromCode = (options: SchemaFromCodeOptions = {}) => { - if (!initialized) { - const globalOptions = { - createForeignKeyIndexes: options.createForeignKeyIndexes ?? true, - }; - - const builder: SchemaBuilder = { - name: 'postgres', - schemaName: 'public', - tables: [], - functions: [], - enums: [], - extensions: [], - parameters: [], - warnings: [], - }; - - const items = getRegisteredItems(); - - for (const processor of processors) { - processor(builder, items, globalOptions); - } - - schema = { ...builder, tables: builder.tables.map(({ metadata: _, ...table }) => table) }; - initialized = true; - } - - return schema; -}; diff --git a/server/src/sql-tools/from-code/processors/column.processor.ts b/server/src/sql-tools/from-code/processors/column.processor.ts deleted file mode 100644 index 6fff3070e3..0000000000 --- a/server/src/sql-tools/from-code/processors/column.processor.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { ColumnOptions } from 'src/sql-tools/from-code/decorators/column.decorator'; -import { onMissingTable, resolveTable } from 'src/sql-tools/from-code/processors/table.processor'; -import { Processor, SchemaBuilder } from 'src/sql-tools/from-code/processors/type'; -import { addWarning, asMetadataKey, fromColumnValue } from 'src/sql-tools/helpers'; -import { DatabaseColumn } from 'src/sql-tools/types'; - -export const processColumns: Processor = (builder, items) => { - for (const { - type, - item: { object, propertyName, options }, - } of items.filter((item) => item.type === 'column' || item.type === 'foreignKeyColumn')) { - const table = resolveTable(builder, object.constructor); - if (!table) { - onMissingTable(builder, type === 'column' ? '@Column' : '@ForeignKeyColumn', object, propertyName); - continue; - } - - const columnName = options.name ?? String(propertyName); - const existingColumn = table.columns.find((column) => column.name === columnName); - if (existingColumn) { - // TODO log warnings if column name is not unique - continue; - } - - const tableName = table.name; - - let defaultValue = fromColumnValue(options.default); - let nullable = options.nullable ?? false; - - // map `{ default: null }` to `{ nullable: true }` - if (defaultValue === null) { - nullable = true; - defaultValue = undefined; - } - - const isEnum = !!(options as ColumnOptions).enum; - - const column: DatabaseColumn = { - name: columnName, - tableName, - primary: options.primary ?? false, - default: defaultValue, - nullable, - isArray: (options as ColumnOptions).array ?? false, - length: options.length, - type: isEnum ? 'enum' : options.type || 'character varying', - enumName: isEnum ? (options as ColumnOptions).enum!.name : undefined, - comment: options.comment, - storage: options.storage, - identity: options.identity, - synchronize: options.synchronize ?? true, - }; - - writeMetadata(object, propertyName, { name: column.name, options }); - - table.columns.push(column); - } -}; - -type ColumnMetadata = { name: string; options: ColumnOptions }; - -export const resolveColumn = (builder: SchemaBuilder, object: object, propertyName: string | symbol) => { - const table = resolveTable(builder, object.constructor); - if (!table) { - return {}; - } - - const metadata = readMetadata(object, propertyName); - if (!metadata) { - return { table }; - } - - const column = table.columns.find((column) => column.name === metadata.name); - return { table, column }; -}; - -export const onMissingColumn = ( - builder: SchemaBuilder, - context: string, - object: object, - propertyName?: symbol | string, -) => { - const label = object.constructor.name + (propertyName ? '.' + String(propertyName) : ''); - addWarning(builder, context, `Unable to find column (${label})`); -}; - -const METADATA_KEY = asMetadataKey('table-metadata'); - -const writeMetadata = (object: object, propertyName: symbol | string, metadata: ColumnMetadata) => - Reflect.defineMetadata(METADATA_KEY, metadata, object, propertyName); - -const readMetadata = (object: object, propertyName: symbol | string): ColumnMetadata | undefined => - Reflect.getMetadata(METADATA_KEY, object, propertyName); diff --git a/server/src/sql-tools/from-code/processors/table.processor.ts b/server/src/sql-tools/from-code/processors/table.processor.ts deleted file mode 100644 index e96f858266..0000000000 --- a/server/src/sql-tools/from-code/processors/table.processor.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { TableOptions } from 'src/sql-tools/from-code/decorators/table.decorator'; -import { Processor, SchemaBuilder } from 'src/sql-tools/from-code/processors/type'; -import { addWarning, asMetadataKey, asSnakeCase } from 'src/sql-tools/helpers'; - -export const processTables: Processor = (builder, items) => { - for (const { - item: { options, object }, - } of items.filter((item) => item.type === 'table')) { - const test = readMetadata(object); - if (test) { - throw new Error( - `Table ${test.name} has already been registered. Does ${object.name} have two @Table() decorators?`, - ); - } - - const tableName = options.name || asSnakeCase(object.name); - - writeMetadata(object, { name: tableName, options }); - - builder.tables.push({ - name: tableName, - columns: [], - constraints: [], - indexes: [], - triggers: [], - synchronize: options.synchronize ?? true, - metadata: { options, object }, - }); - } -}; - -export const resolveTable = (builder: SchemaBuilder, object: object) => { - const metadata = readMetadata(object); - if (!metadata) { - return; - } - - return builder.tables.find((table) => table.name === metadata.name); -}; - -export const onMissingTable = ( - builder: SchemaBuilder, - context: string, - object: object, - propertyName?: symbol | string, -) => { - const label = object.constructor.name + (propertyName ? '.' + String(propertyName) : ''); - addWarning(builder, context, `Unable to find table (${label})`); -}; - -const METADATA_KEY = asMetadataKey('table-metadata'); - -type TableMetadata = { name: string; options: TableOptions }; - -const readMetadata = (object: object): TableMetadata | undefined => Reflect.getMetadata(METADATA_KEY, object); - -const writeMetadata = (object: object, metadata: TableMetadata): void => - Reflect.defineMetadata(METADATA_KEY, metadata, object); diff --git a/server/src/sql-tools/from-code/processors/type.ts b/server/src/sql-tools/from-code/processors/type.ts deleted file mode 100644 index deb142d278..0000000000 --- a/server/src/sql-tools/from-code/processors/type.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { SchemaFromCodeOptions } from 'src/sql-tools/from-code'; -import { TableOptions } from 'src/sql-tools/from-code/decorators/table.decorator'; -import { RegisterItem } from 'src/sql-tools/from-code/register-item'; -import { DatabaseSchema, DatabaseTable } from 'src/sql-tools/types'; - -// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type -export type TableWithMetadata = DatabaseTable & { metadata: { options: TableOptions; object: Function } }; -export type SchemaBuilder = Omit & { tables: TableWithMetadata[] }; - -export type Processor = (builder: SchemaBuilder, items: RegisterItem[], options: SchemaFromCodeOptions) => void; diff --git a/server/src/sql-tools/from-database/index.ts b/server/src/sql-tools/from-database/index.ts deleted file mode 100644 index 771a24d7b1..0000000000 --- a/server/src/sql-tools/from-database/index.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Kysely } from 'kysely'; -import { PostgresJSDialect } from 'kysely-postgres-js'; -import { Sql } from 'postgres'; -import { readColumns } from 'src/sql-tools/from-database/readers/column.reader'; -import { readComments } from 'src/sql-tools/from-database/readers/comment.reader'; -import { readConstraints } from 'src/sql-tools/from-database/readers/constraint.reader'; -import { readExtensions } from 'src/sql-tools/from-database/readers/extension.reader'; -import { readFunctions } from 'src/sql-tools/from-database/readers/function.reader'; -import { readIndexes } from 'src/sql-tools/from-database/readers/index.reader'; -import { readName } from 'src/sql-tools/from-database/readers/name.reader'; -import { readParameters } from 'src/sql-tools/from-database/readers/parameter.reader'; -import { readTables } from 'src/sql-tools/from-database/readers/table.reader'; -import { readTriggers } from 'src/sql-tools/from-database/readers/trigger.reader'; -import { DatabaseReader } from 'src/sql-tools/from-database/readers/type'; -import { DatabaseSchema, LoadSchemaOptions, PostgresDB } from 'src/sql-tools/types'; - -const readers: DatabaseReader[] = [ - // - readName, - readParameters, - readExtensions, - readFunctions, - readTables, - readColumns, - readIndexes, - readConstraints, - readTriggers, - readComments, -]; - -/** - * Load the database schema from the database - */ -export const schemaFromDatabase = async (postgres: Sql, options: LoadSchemaOptions = {}): Promise => { - const schema: DatabaseSchema = { - name: 'immich', - schemaName: options.schemaName || 'public', - parameters: [], - functions: [], - enums: [], - extensions: [], - tables: [], - warnings: [], - }; - - const db = new Kysely({ dialect: new PostgresJSDialect({ postgres }) }); - for (const reader of readers) { - await reader(schema, db); - } - - await db.destroy(); - - return schema; -}; diff --git a/server/src/sql-tools/from-database/readers/type.ts b/server/src/sql-tools/from-database/readers/type.ts deleted file mode 100644 index d8a21d486b..0000000000 --- a/server/src/sql-tools/from-database/readers/type.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { DatabaseClient, DatabaseSchema } from 'src/sql-tools/types'; - -export type DatabaseReader = (schema: DatabaseSchema, db: DatabaseClient) => Promise; diff --git a/server/src/sql-tools/helpers.ts b/server/src/sql-tools/helpers.ts index 015bbe4d9c..119fe63291 100644 --- a/server/src/sql-tools/helpers.ts +++ b/server/src/sql-tools/helpers.ts @@ -1,15 +1,6 @@ import { createHash } from 'node:crypto'; -import { ColumnValue } from 'src/sql-tools/from-code/decorators/column.decorator'; -import { SchemaBuilder } from 'src/sql-tools/from-code/processors/type'; -import { - Comparer, - DatabaseColumn, - DiffOptions, - SchemaDiff, - TriggerAction, - TriggerScope, - TriggerTiming, -} from 'src/sql-tools/types'; +import { ColumnValue } from 'src/sql-tools/decorators/column.decorator'; +import { Comparer, DatabaseColumn, IgnoreOptions, SchemaDiff } from 'src/sql-tools/types'; export const asMetadataKey = (name: string) => `sql-tools:${name}`; @@ -27,46 +18,6 @@ export const asOptions = (options: string | T): T = }; export const sha1 = (value: string) => createHash('sha1').update(value).digest('hex'); -export const hasMask = (input: number, mask: number) => (input & mask) === mask; - -export const parseTriggerType = (type: number) => { - // eslint-disable-next-line unicorn/prefer-math-trunc - const scope: TriggerScope = hasMask(type, 1 << 0) ? 'row' : 'statement'; - - let timing: TriggerTiming = 'after'; - const timingMasks: Array<{ mask: number; value: TriggerTiming }> = [ - { mask: 1 << 1, value: 'before' }, - { mask: 1 << 6, value: 'instead of' }, - ]; - - for (const { mask, value } of timingMasks) { - if (hasMask(type, mask)) { - timing = value; - break; - } - } - - const actions: TriggerAction[] = []; - const actionMasks: Array<{ mask: number; value: TriggerAction }> = [ - { mask: 1 << 2, value: 'insert' }, - { mask: 1 << 3, value: 'delete' }, - { mask: 1 << 4, value: 'update' }, - { mask: 1 << 5, value: 'truncate' }, - ]; - - for (const { mask, value } of actionMasks) { - if (hasMask(type, mask)) { - actions.push(value); - break; - } - } - - if (actions.length === 0) { - throw new Error(`Unable to parse trigger type ${type}`); - } - - return { actions, timing, scope }; -}; export const fromColumnValue = (columnValue?: ColumnValue) => { if (columnValue === undefined) { @@ -108,7 +59,7 @@ export const haveEqualColumns = (sourceColumns?: string[], targetColumns?: strin export const compare = ( sources: T[], targets: T[], - options: DiffOptions | undefined, + options: IgnoreOptions | undefined, comparer: Comparer, ) => { options = options || {}; @@ -144,7 +95,7 @@ export const compare = ( const isIgnored = ( source: { synchronize?: boolean } | undefined, target: { synchronize?: boolean } | undefined, - options: DiffOptions, + options: IgnoreOptions, ) => { return (options.ignoreExtra && !source) || (options.ignoreMissing && !target); }; @@ -214,20 +165,3 @@ export const asColumnComment = (tableName: string, columnName: string, comment: export const asColumnList = (columns: string[]) => columns.map((column) => `"${column}"`).join(', '); export const asForeignKeyConstraintName = (table: string, columns: string[]) => asKey('FK_', table, [...columns]); - -export const asIndexName = (table: string, columns?: string[], where?: string) => { - const items: string[] = []; - for (const columnName of columns ?? []) { - items.push(columnName); - } - - if (where) { - items.push(where); - } - - return asKey('IDX_', table, items); -}; - -export const addWarning = (builder: SchemaBuilder, context: string, message: string) => { - builder.warnings.push(`[${context}] ${message}`); -}; diff --git a/server/src/sql-tools/from-code/processors/check-constraint.processor.ts b/server/src/sql-tools/processors/check-constraint.processor.ts similarity index 63% rename from server/src/sql-tools/from-code/processors/check-constraint.processor.ts rename to server/src/sql-tools/processors/check-constraint.processor.ts index feb21b9894..10e5c18791 100644 --- a/server/src/sql-tools/from-code/processors/check-constraint.processor.ts +++ b/server/src/sql-tools/processors/check-constraint.processor.ts @@ -1,22 +1,20 @@ -import { onMissingTable, resolveTable } from 'src/sql-tools/from-code/processors/table.processor'; -import { Processor } from 'src/sql-tools/from-code/processors/type'; import { asKey } from 'src/sql-tools/helpers'; -import { DatabaseConstraintType } from 'src/sql-tools/types'; +import { ConstraintType, Processor } from 'src/sql-tools/types'; export const processCheckConstraints: Processor = (builder, items) => { for (const { item: { object, options }, } of items.filter((item) => item.type === 'checkConstraint')) { - const table = resolveTable(builder, object); + const table = builder.getTableByObject(object); if (!table) { - onMissingTable(builder, '@Check', object); + builder.warnMissingTable('@Check', object); continue; } const tableName = table.name; table.constraints.push({ - type: DatabaseConstraintType.CHECK, + type: ConstraintType.CHECK, name: options.name || asCheckConstraintName(tableName, options.expression), tableName, expression: options.expression, diff --git a/server/src/sql-tools/processors/column.processor.ts b/server/src/sql-tools/processors/column.processor.ts new file mode 100644 index 0000000000..3981ee4036 --- /dev/null +++ b/server/src/sql-tools/processors/column.processor.ts @@ -0,0 +1,55 @@ +import { ColumnOptions } from 'src/sql-tools/decorators/column.decorator'; +import { fromColumnValue } from 'src/sql-tools/helpers'; +import { Processor } from 'src/sql-tools/types'; + +export const processColumns: Processor = (builder, items) => { + for (const { + type, + item: { object, propertyName, options }, + } of items.filter((item) => item.type === 'column' || item.type === 'foreignKeyColumn')) { + const table = builder.getTableByObject(object.constructor); + if (!table) { + builder.warnMissingTable(type === 'column' ? '@Column' : '@ForeignKeyColumn', object, propertyName); + continue; + } + + const columnName = options.name ?? String(propertyName); + const existingColumn = table.columns.find((column) => column.name === columnName); + if (existingColumn) { + // TODO log warnings if column name is not unique + continue; + } + + let defaultValue = fromColumnValue(options.default); + let nullable = options.nullable ?? false; + + // map `{ default: null }` to `{ nullable: true }` + if (defaultValue === null) { + nullable = true; + defaultValue = undefined; + } + + const isEnum = !!(options as ColumnOptions).enum; + + builder.addColumn( + table, + { + name: columnName, + tableName: table.name, + primary: options.primary ?? false, + default: defaultValue, + nullable, + isArray: (options as ColumnOptions).array ?? false, + length: options.length, + type: isEnum ? 'enum' : options.type || 'character varying', + enumName: isEnum ? (options as ColumnOptions).enum!.name : undefined, + comment: options.comment, + storage: options.storage, + identity: options.identity, + synchronize: options.synchronize ?? true, + }, + options, + propertyName, + ); + } +}; diff --git a/server/src/sql-tools/from-code/processors/configuration-parameter.processor.ts b/server/src/sql-tools/processors/configuration-parameter.processor.ts similarity index 81% rename from server/src/sql-tools/from-code/processors/configuration-parameter.processor.ts rename to server/src/sql-tools/processors/configuration-parameter.processor.ts index 493214e5b8..4b2d56f103 100644 --- a/server/src/sql-tools/from-code/processors/configuration-parameter.processor.ts +++ b/server/src/sql-tools/processors/configuration-parameter.processor.ts @@ -1,12 +1,12 @@ -import { Processor } from 'src/sql-tools/from-code/processors/type'; import { fromColumnValue } from 'src/sql-tools/helpers'; +import { Processor } from 'src/sql-tools/types'; export const processConfigurationParameters: Processor = (builder, items) => { for (const { item: { options }, } of items.filter((item) => item.type === 'configurationParameter')) { builder.parameters.push({ - databaseName: builder.name, + databaseName: builder.databaseName, name: options.name, value: fromColumnValue(options.value), scope: options.scope, diff --git a/server/src/sql-tools/from-code/processors/database.processor.ts b/server/src/sql-tools/processors/database.processor.ts similarity index 63% rename from server/src/sql-tools/from-code/processors/database.processor.ts rename to server/src/sql-tools/processors/database.processor.ts index 9b0662f7e0..9c9e61a6cf 100644 --- a/server/src/sql-tools/from-code/processors/database.processor.ts +++ b/server/src/sql-tools/processors/database.processor.ts @@ -1,10 +1,10 @@ -import { Processor } from 'src/sql-tools/from-code/processors/type'; import { asSnakeCase } from 'src/sql-tools/helpers'; +import { Processor } from 'src/sql-tools/types'; export const processDatabases: Processor = (builder, items) => { for (const { item: { object, options }, } of items.filter((item) => item.type === 'database')) { - builder.name = options.name || asSnakeCase(object.name); + builder.databaseName = options.name || asSnakeCase(object.name); } }; diff --git a/server/src/sql-tools/from-code/processors/enum.processor.ts b/server/src/sql-tools/processors/enum.processor.ts similarity index 76% rename from server/src/sql-tools/from-code/processors/enum.processor.ts rename to server/src/sql-tools/processors/enum.processor.ts index d6d19ec025..d396089c8f 100644 --- a/server/src/sql-tools/from-code/processors/enum.processor.ts +++ b/server/src/sql-tools/processors/enum.processor.ts @@ -1,4 +1,4 @@ -import { Processor } from 'src/sql-tools/from-code/processors/type'; +import { Processor } from 'src/sql-tools/types'; export const processEnums: Processor = (builder, items) => { for (const { item } of items.filter((item) => item.type === 'enum')) { diff --git a/server/src/sql-tools/from-code/processors/extension.processor.ts b/server/src/sql-tools/processors/extension.processor.ts similarity index 80% rename from server/src/sql-tools/from-code/processors/extension.processor.ts rename to server/src/sql-tools/processors/extension.processor.ts index 4b12054aa3..97a39c0d8b 100644 --- a/server/src/sql-tools/from-code/processors/extension.processor.ts +++ b/server/src/sql-tools/processors/extension.processor.ts @@ -1,4 +1,4 @@ -import { Processor } from 'src/sql-tools/from-code/processors/type'; +import { Processor } from 'src/sql-tools/types'; export const processExtensions: Processor = (builder, items) => { for (const { diff --git a/server/src/sql-tools/from-code/processors/foreign-key-column.processor.ts b/server/src/sql-tools/processors/foreign-key-column.processor.ts similarity index 64% rename from server/src/sql-tools/from-code/processors/foreign-key-column.processor.ts rename to server/src/sql-tools/processors/foreign-key-column.processor.ts index d706763d1d..a3cac0a85e 100644 --- a/server/src/sql-tools/from-code/processors/foreign-key-column.processor.ts +++ b/server/src/sql-tools/processors/foreign-key-column.processor.ts @@ -1,28 +1,25 @@ -import { onMissingColumn, resolveColumn } from 'src/sql-tools/from-code/processors/column.processor'; -import { onMissingTable, resolveTable } from 'src/sql-tools/from-code/processors/table.processor'; -import { Processor } from 'src/sql-tools/from-code/processors/type'; import { asForeignKeyConstraintName, asKey } from 'src/sql-tools/helpers'; -import { DatabaseActionType, DatabaseConstraintType } from 'src/sql-tools/types'; +import { ActionType, ConstraintType, Processor } from 'src/sql-tools/types'; export const processForeignKeyColumns: Processor = (builder, items) => { for (const { item: { object, propertyName, options, target }, } of items.filter((item) => item.type === 'foreignKeyColumn')) { - const { table, column } = resolveColumn(builder, object, propertyName); + const { table, column } = builder.getColumnByObjectAndPropertyName(object, propertyName); if (!table) { - onMissingTable(builder, '@ForeignKeyColumn', object); + builder.warnMissingTable('@ForeignKeyColumn', object); continue; } if (!column) { // should be impossible since they are pre-created in `column.processor.ts` - onMissingColumn(builder, '@ForeignKeyColumn', object, propertyName); + builder.warnMissingColumn('@ForeignKeyColumn', object, propertyName); continue; } - const referenceTable = resolveTable(builder, target()); + const referenceTable = builder.getTableByObject(target()); if (!referenceTable) { - onMissingTable(builder, '@ForeignKeyColumn', object, propertyName); + builder.warnMissingTable('@ForeignKeyColumn', object, propertyName); continue; } @@ -41,11 +38,11 @@ export const processForeignKeyColumns: Processor = (builder, items) => { name, tableName: table.name, columnNames, - type: DatabaseConstraintType.FOREIGN_KEY, + type: ConstraintType.FOREIGN_KEY, referenceTableName: referenceTable.name, referenceColumnNames, - onUpdate: options.onUpdate as DatabaseActionType, - onDelete: options.onDelete as DatabaseActionType, + onUpdate: options.onUpdate as ActionType, + onDelete: options.onDelete as ActionType, synchronize: options.synchronize ?? true, }); @@ -54,7 +51,7 @@ export const processForeignKeyColumns: Processor = (builder, items) => { name: options.uniqueConstraintName || asRelationKeyConstraintName(table.name, columnNames), tableName: table.name, columnNames, - type: DatabaseConstraintType.UNIQUE, + type: ConstraintType.UNIQUE, synchronize: options.synchronize ?? true, }); } diff --git a/server/src/sql-tools/from-code/processors/foreign-key-constraint.processor.ts b/server/src/sql-tools/processors/foreign-key-constraint.processor.ts similarity index 61% rename from server/src/sql-tools/from-code/processors/foreign-key-constraint.processor.ts rename to server/src/sql-tools/processors/foreign-key-constraint.processor.ts index e88297b6c6..704e6ed8e4 100644 --- a/server/src/sql-tools/from-code/processors/foreign-key-constraint.processor.ts +++ b/server/src/sql-tools/processors/foreign-key-constraint.processor.ts @@ -1,23 +1,20 @@ -import { onMissingTable, resolveTable } from 'src/sql-tools/from-code/processors/table.processor'; -import { Processor } from 'src/sql-tools/from-code/processors/type'; -import { addWarning, asForeignKeyConstraintName, asIndexName } from 'src/sql-tools/helpers'; -import { DatabaseActionType, DatabaseConstraintType } from 'src/sql-tools/types'; +import { asForeignKeyConstraintName } from 'src/sql-tools/helpers'; +import { ActionType, ConstraintType, Processor } from 'src/sql-tools/types'; export const processForeignKeyConstraints: Processor = (builder, items, config) => { for (const { item: { object, options }, } of items.filter((item) => item.type === 'foreignKeyConstraint')) { - const table = resolveTable(builder, object); + const table = builder.getTableByObject(object); if (!table) { - onMissingTable(builder, '@ForeignKeyConstraint', { name: 'referenceTable' }); + builder.warnMissingTable('@ForeignKeyConstraint', { name: 'referenceTable' }); continue; } - const referenceTable = resolveTable(builder, options.referenceTable()); + const referenceTable = builder.getTableByObject(options.referenceTable()); if (!referenceTable) { const referenceTableName = options.referenceTable()?.name; - addWarning( - builder, + builder.warn( '@ForeignKeyConstraint.referenceTable', `Unable to find table` + (referenceTableName ? ` (${referenceTableName})` : ''), ); @@ -28,21 +25,18 @@ export const processForeignKeyConstraints: Processor = (builder, items, config) for (const columnName of options.columns) { if (!table.columns.some(({ name }) => name === columnName)) { - addWarning( - builder, - '@ForeignKeyConstraint.columns', - `Unable to find column (${table.metadata.object.name}.${columnName})`, - ); + const metadata = builder.getTableMetadata(table); + builder.warn('@ForeignKeyConstraint.columns', `Unable to find column (${metadata.object.name}.${columnName})`); missingColumn = true; } } for (const columnName of options.referenceColumns || []) { if (!referenceTable.columns.some(({ name }) => name === columnName)) { - addWarning( - builder, + const metadata = builder.getTableMetadata(referenceTable); + builder.warn( '@ForeignKeyConstraint.referenceColumns', - `Unable to find column (${referenceTable.metadata.object.name}.${columnName})`, + `Unable to find column (${metadata.object.name}.${columnName})`, ); missingColumn = true; } @@ -58,14 +52,14 @@ export const processForeignKeyConstraints: Processor = (builder, items, config) const name = options.name || asForeignKeyConstraintName(table.name, options.columns); table.constraints.push({ - type: DatabaseConstraintType.FOREIGN_KEY, + type: ConstraintType.FOREIGN_KEY, name, tableName: table.name, columnNames: options.columns, referenceTableName: referenceTable.name, referenceColumnNames: referenceColumns, - onUpdate: options.onUpdate as DatabaseActionType, - onDelete: options.onDelete as DatabaseActionType, + onUpdate: options.onUpdate as ActionType, + onDelete: options.onDelete as ActionType, synchronize: options.synchronize ?? true, }); @@ -75,7 +69,7 @@ export const processForeignKeyConstraints: Processor = (builder, items, config) if (options.index || options.indexName || config.createForeignKeyIndexes) { table.indexes.push({ - name: options.indexName || asIndexName(table.name, options.columns), + name: options.indexName || builder.asIndexName(table.name, options.columns), tableName: table.name, columnNames: options.columns, unique: false, diff --git a/server/src/sql-tools/from-code/processors/function.processor.ts b/server/src/sql-tools/processors/function.processor.ts similarity index 77% rename from server/src/sql-tools/from-code/processors/function.processor.ts rename to server/src/sql-tools/processors/function.processor.ts index cbd9c87abc..669d1a5ec0 100644 --- a/server/src/sql-tools/from-code/processors/function.processor.ts +++ b/server/src/sql-tools/processors/function.processor.ts @@ -1,4 +1,4 @@ -import { Processor } from 'src/sql-tools/from-code/processors/type'; +import { Processor } from 'src/sql-tools/types'; export const processFunctions: Processor = (builder, items) => { for (const { item } of items.filter((item) => item.type === 'function')) { diff --git a/server/src/sql-tools/from-code/processors/index.processor.ts b/server/src/sql-tools/processors/index.processor.ts similarity index 70% rename from server/src/sql-tools/from-code/processors/index.processor.ts rename to server/src/sql-tools/processors/index.processor.ts index 4de8914231..546bac236a 100644 --- a/server/src/sql-tools/from-code/processors/index.processor.ts +++ b/server/src/sql-tools/processors/index.processor.ts @@ -1,20 +1,17 @@ -import { onMissingColumn, resolveColumn } from 'src/sql-tools/from-code/processors/column.processor'; -import { onMissingTable, resolveTable } from 'src/sql-tools/from-code/processors/table.processor'; -import { Processor } from 'src/sql-tools/from-code/processors/type'; -import { asIndexName } from 'src/sql-tools/helpers'; +import { Processor } from 'src/sql-tools/types'; export const processIndexes: Processor = (builder, items, config) => { for (const { item: { object, options }, } of items.filter((item) => item.type === 'index')) { - const table = resolveTable(builder, object); + const table = builder.getTableByObject(object); if (!table) { - onMissingTable(builder, '@Check', object); + builder.warnMissingTable('@Check', object); continue; } table.indexes.push({ - name: options.name || asIndexName(table.name, options.columns, options.where), + name: options.name || builder.asIndexName(table.name, options.columns, options.where), tableName: table.name, unique: options.unique ?? false, expression: options.expression, @@ -31,15 +28,15 @@ export const processIndexes: Processor = (builder, items, config) => { type, item: { object, propertyName, options }, } of items.filter((item) => item.type === 'column' || item.type === 'foreignKeyColumn')) { - const { table, column } = resolveColumn(builder, object, propertyName); + const { table, column } = builder.getColumnByObjectAndPropertyName(object, propertyName); if (!table) { - onMissingTable(builder, '@Column', object); + builder.warnMissingTable('@Column', object); continue; } if (!column) { // should be impossible since they are created in `column.processor.ts` - onMissingColumn(builder, '@Column', object, propertyName); + builder.warnMissingColumn('@Column', object, propertyName); continue; } @@ -53,7 +50,7 @@ export const processIndexes: Processor = (builder, items, config) => { continue; } - const indexName = options.indexName || asIndexName(table.name, [column.name]); + const indexName = options.indexName || builder.asIndexName(table.name, [column.name]); const isIndexPresent = table.indexes.some((index) => index.name === indexName); if (isIndexPresent) { diff --git a/server/src/sql-tools/processors/index.ts b/server/src/sql-tools/processors/index.ts new file mode 100644 index 0000000000..054540f7b0 --- /dev/null +++ b/server/src/sql-tools/processors/index.ts @@ -0,0 +1,32 @@ +import { processCheckConstraints } from 'src/sql-tools/processors/check-constraint.processor'; +import { processColumns } from 'src/sql-tools/processors/column.processor'; +import { processConfigurationParameters } from 'src/sql-tools/processors/configuration-parameter.processor'; +import { processDatabases } from 'src/sql-tools/processors/database.processor'; +import { processEnums } from 'src/sql-tools/processors/enum.processor'; +import { processExtensions } from 'src/sql-tools/processors/extension.processor'; +import { processForeignKeyColumns } from 'src/sql-tools/processors/foreign-key-column.processor'; +import { processForeignKeyConstraints } from 'src/sql-tools/processors/foreign-key-constraint.processor'; +import { processFunctions } from 'src/sql-tools/processors/function.processor'; +import { processIndexes } from 'src/sql-tools/processors/index.processor'; +import { processPrimaryKeyConstraints } from 'src/sql-tools/processors/primary-key-contraint.processor'; +import { processTables } from 'src/sql-tools/processors/table.processor'; +import { processTriggers } from 'src/sql-tools/processors/trigger.processor'; +import { processUniqueConstraints } from 'src/sql-tools/processors/unique-constraint.processor'; +import { Processor } from 'src/sql-tools/types'; + +export const processors: Processor[] = [ + processDatabases, + processConfigurationParameters, + processEnums, + processExtensions, + processFunctions, + processTables, + processColumns, + processForeignKeyColumns, + processForeignKeyConstraints, + processUniqueConstraints, + processCheckConstraints, + processPrimaryKeyConstraints, + processIndexes, + processTriggers, +]; diff --git a/server/src/sql-tools/from-code/processors/primary-key-contraint.processor.ts b/server/src/sql-tools/processors/primary-key-contraint.processor.ts similarity index 61% rename from server/src/sql-tools/from-code/processors/primary-key-contraint.processor.ts rename to server/src/sql-tools/processors/primary-key-contraint.processor.ts index 74aecc5ea0..be8983f79a 100644 --- a/server/src/sql-tools/from-code/processors/primary-key-contraint.processor.ts +++ b/server/src/sql-tools/processors/primary-key-contraint.processor.ts @@ -1,6 +1,5 @@ -import { Processor } from 'src/sql-tools/from-code/processors/type'; import { asKey } from 'src/sql-tools/helpers'; -import { DatabaseConstraintType } from 'src/sql-tools/types'; +import { ConstraintType, Processor } from 'src/sql-tools/types'; export const processPrimaryKeyConstraints: Processor = (builder) => { for (const table of builder.tables) { @@ -11,13 +10,15 @@ export const processPrimaryKeyConstraints: Processor = (builder) => { columnNames.push(column.name); } } + if (columnNames.length > 0) { + const tableMetadata = builder.getTableMetadata(table); table.constraints.push({ - type: DatabaseConstraintType.PRIMARY_KEY, - name: table.metadata.options.primaryConstraintName || asPrimaryKeyConstraintName(table.name, columnNames), + type: ConstraintType.PRIMARY_KEY, + name: tableMetadata.options.primaryConstraintName || asPrimaryKeyConstraintName(table.name, columnNames), tableName: table.name, columnNames, - synchronize: table.metadata.options.synchronize ?? true, + synchronize: tableMetadata.options.synchronize ?? true, }); } } diff --git a/server/src/sql-tools/processors/table.processor.ts b/server/src/sql-tools/processors/table.processor.ts new file mode 100644 index 0000000000..967e4a023b --- /dev/null +++ b/server/src/sql-tools/processors/table.processor.ts @@ -0,0 +1,28 @@ +import { asSnakeCase } from 'src/sql-tools/helpers'; +import { Processor } from 'src/sql-tools/types'; + +export const processTables: Processor = (builder, items) => { + for (const { + item: { options, object }, + } of items.filter((item) => item.type === 'table')) { + const test = builder.getTableByObject(object); + if (test) { + throw new Error( + `Table ${test.name} has already been registered. Does ${object.name} have two @Table() decorators?`, + ); + } + + builder.addTable( + { + name: options.name || asSnakeCase(object.name), + columns: [], + constraints: [], + indexes: [], + triggers: [], + synchronize: options.synchronize ?? true, + }, + options, + object, + ); + } +}; diff --git a/server/src/sql-tools/from-code/processors/trigger.processor.ts b/server/src/sql-tools/processors/trigger.processor.ts similarity index 71% rename from server/src/sql-tools/from-code/processors/trigger.processor.ts rename to server/src/sql-tools/processors/trigger.processor.ts index 4b875f353b..03e7076fdb 100644 --- a/server/src/sql-tools/from-code/processors/trigger.processor.ts +++ b/server/src/sql-tools/processors/trigger.processor.ts @@ -1,15 +1,14 @@ -import { TriggerOptions } from 'src/sql-tools/from-code/decorators/trigger.decorator'; -import { onMissingTable, resolveTable } from 'src/sql-tools/from-code/processors/table.processor'; -import { Processor } from 'src/sql-tools/from-code/processors/type'; +import { TriggerOptions } from 'src/sql-tools/decorators/trigger.decorator'; import { asKey } from 'src/sql-tools/helpers'; +import { Processor } from 'src/sql-tools/types'; export const processTriggers: Processor = (builder, items) => { for (const { item: { object, options }, } of items.filter((item) => item.type === 'trigger')) { - const table = resolveTable(builder, object); + const table = builder.getTableByObject(object); if (!table) { - onMissingTable(builder, '@Trigger', object); + builder.warnMissingTable('@Trigger', object); continue; } diff --git a/server/src/sql-tools/from-code/processors/unique-constraint.processor.ts b/server/src/sql-tools/processors/unique-constraint.processor.ts similarity index 66% rename from server/src/sql-tools/from-code/processors/unique-constraint.processor.ts rename to server/src/sql-tools/processors/unique-constraint.processor.ts index 9014378085..eed9597879 100644 --- a/server/src/sql-tools/from-code/processors/unique-constraint.processor.ts +++ b/server/src/sql-tools/processors/unique-constraint.processor.ts @@ -1,16 +1,13 @@ -import { onMissingColumn, resolveColumn } from 'src/sql-tools/from-code/processors/column.processor'; -import { onMissingTable, resolveTable } from 'src/sql-tools/from-code/processors/table.processor'; -import { Processor } from 'src/sql-tools/from-code/processors/type'; import { asKey } from 'src/sql-tools/helpers'; -import { DatabaseConstraintType } from 'src/sql-tools/types'; +import { ConstraintType, Processor } from 'src/sql-tools/types'; export const processUniqueConstraints: Processor = (builder, items) => { for (const { item: { object, options }, } of items.filter((item) => item.type === 'uniqueConstraint')) { - const table = resolveTable(builder, object); + const table = builder.getTableByObject(object); if (!table) { - onMissingTable(builder, '@Unique', object); + builder.warnMissingTable('@Unique', object); continue; } @@ -18,7 +15,7 @@ export const processUniqueConstraints: Processor = (builder, items) => { const columnNames = options.columns; table.constraints.push({ - type: DatabaseConstraintType.UNIQUE, + type: ConstraintType.UNIQUE, name: options.name || asUniqueConstraintName(tableName, columnNames), tableName, columnNames, @@ -31,21 +28,21 @@ export const processUniqueConstraints: Processor = (builder, items) => { type, item: { object, propertyName, options }, } of items.filter((item) => item.type === 'column' || item.type === 'foreignKeyColumn')) { - const { table, column } = resolveColumn(builder, object, propertyName); + const { table, column } = builder.getColumnByObjectAndPropertyName(object, propertyName); if (!table) { - onMissingTable(builder, '@Column', object); + builder.warnMissingTable('@Column', object); continue; } if (!column) { // should be impossible since they are created in `column.processor.ts` - onMissingColumn(builder, '@Column', object, propertyName); + builder.warnMissingColumn('@Column', object, propertyName); continue; } if (type === 'column' && !options.primary && (options.unique || options.uniqueConstraintName)) { table.constraints.push({ - type: DatabaseConstraintType.UNIQUE, + type: ConstraintType.UNIQUE, name: options.uniqueConstraintName || asUniqueConstraintName(table.name, [column.name]), tableName: table.name, columnNames: [column.name], diff --git a/server/src/sql-tools/public_api.ts b/server/src/sql-tools/public_api.ts index 61e4a3e431..aaef55dd8d 100644 --- a/server/src/sql-tools/public_api.ts +++ b/server/src/sql-tools/public_api.ts @@ -1,29 +1,7 @@ -export { schemaDiff } from 'src/sql-tools/diff'; -export { schemaFromCode } from 'src/sql-tools/from-code'; -export * from 'src/sql-tools/from-code/decorators/after-delete.decorator'; -export * from 'src/sql-tools/from-code/decorators/after-insert.decorator'; -export * from 'src/sql-tools/from-code/decorators/before-update.decorator'; -export * from 'src/sql-tools/from-code/decorators/check.decorator'; -export * from 'src/sql-tools/from-code/decorators/column.decorator'; -export * from 'src/sql-tools/from-code/decorators/configuration-parameter.decorator'; -export * from 'src/sql-tools/from-code/decorators/create-date-column.decorator'; -export * from 'src/sql-tools/from-code/decorators/database.decorator'; -export * from 'src/sql-tools/from-code/decorators/delete-date-column.decorator'; -export * from 'src/sql-tools/from-code/decorators/extension.decorator'; -export * from 'src/sql-tools/from-code/decorators/extensions.decorator'; -export * from 'src/sql-tools/from-code/decorators/foreign-key-column.decorator'; -export * from 'src/sql-tools/from-code/decorators/foreign-key-constraint.decorator'; -export * from 'src/sql-tools/from-code/decorators/generated-column.decorator'; -export * from 'src/sql-tools/from-code/decorators/index.decorator'; -export * from 'src/sql-tools/from-code/decorators/primary-column.decorator'; -export * from 'src/sql-tools/from-code/decorators/primary-generated-column.decorator'; -export * from 'src/sql-tools/from-code/decorators/table.decorator'; -export * from 'src/sql-tools/from-code/decorators/trigger-function.decorator'; -export * from 'src/sql-tools/from-code/decorators/trigger.decorator'; -export * from 'src/sql-tools/from-code/decorators/unique.decorator'; -export * from 'src/sql-tools/from-code/decorators/update-date-column.decorator'; -export * from 'src/sql-tools/from-code/register-enum'; -export * from 'src/sql-tools/from-code/register-function'; -export { schemaFromDatabase } from 'src/sql-tools/from-database'; -export { schemaDiffToSql } from 'src/sql-tools/to-sql'; +export * from 'src/sql-tools/decorators'; +export * from 'src/sql-tools/register-enum'; +export * from 'src/sql-tools/register-function'; +export { schemaDiff, schemaDiffToSql } from 'src/sql-tools/schema-diff'; +export { schemaFromCode } from 'src/sql-tools/schema-from-code'; +export { schemaFromDatabase } from 'src/sql-tools/schema-from-database'; export * from 'src/sql-tools/types'; diff --git a/server/src/sql-tools/from-database/readers/column.reader.ts b/server/src/sql-tools/readers/column.reader.ts similarity index 96% rename from server/src/sql-tools/from-database/readers/column.reader.ts rename to server/src/sql-tools/readers/column.reader.ts index 167124bac9..c81fafa68f 100644 --- a/server/src/sql-tools/from-database/readers/column.reader.ts +++ b/server/src/sql-tools/readers/column.reader.ts @@ -1,7 +1,6 @@ import { sql } from 'kysely'; import { jsonArrayFrom } from 'kysely/helpers/postgres'; -import { DatabaseReader } from 'src/sql-tools/from-database/readers/type'; -import { ColumnType, DatabaseColumn } from 'src/sql-tools/types'; +import { ColumnType, DatabaseColumn, DatabaseReader } from 'src/sql-tools/types'; export const readColumns: DatabaseReader = async (schema, db) => { const columns = await db diff --git a/server/src/sql-tools/from-database/readers/comment.reader.ts b/server/src/sql-tools/readers/comment.reader.ts similarity index 93% rename from server/src/sql-tools/from-database/readers/comment.reader.ts rename to server/src/sql-tools/readers/comment.reader.ts index 3dd4f4adc9..a06e111f84 100644 --- a/server/src/sql-tools/from-database/readers/comment.reader.ts +++ b/server/src/sql-tools/readers/comment.reader.ts @@ -1,4 +1,4 @@ -import { DatabaseReader } from 'src/sql-tools/from-database/readers/type'; +import { DatabaseReader } from 'src/sql-tools/types'; export const readComments: DatabaseReader = async (schema, db) => { const comments = await db diff --git a/server/src/sql-tools/from-database/readers/constraint.reader.ts b/server/src/sql-tools/readers/constraint.reader.ts similarity index 88% rename from server/src/sql-tools/from-database/readers/constraint.reader.ts rename to server/src/sql-tools/readers/constraint.reader.ts index a633324f25..0e4334bf04 100644 --- a/server/src/sql-tools/from-database/readers/constraint.reader.ts +++ b/server/src/sql-tools/readers/constraint.reader.ts @@ -1,6 +1,5 @@ import { sql } from 'kysely'; -import { DatabaseReader } from 'src/sql-tools/from-database/readers/type'; -import { DatabaseActionType, DatabaseConstraintType } from 'src/sql-tools/types'; +import { ActionType, ConstraintType, DatabaseReader } from 'src/sql-tools/types'; export const readConstraints: DatabaseReader = async (schema, db) => { const constraints = await db @@ -60,7 +59,7 @@ export const readConstraints: DatabaseReader = async (schema, db) => { continue; } table.constraints.push({ - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: constraintName, tableName: constraint.table_name, columnNames: constraint.column_names, @@ -79,7 +78,7 @@ export const readConstraints: DatabaseReader = async (schema, db) => { } table.constraints.push({ - type: DatabaseConstraintType.FOREIGN_KEY, + type: ConstraintType.FOREIGN_KEY, name: constraintName, tableName: constraint.table_name, columnNames: constraint.column_names, @@ -95,7 +94,7 @@ export const readConstraints: DatabaseReader = async (schema, db) => { // unique constraint case 'u': { table.constraints.push({ - type: DatabaseConstraintType.UNIQUE, + type: ConstraintType.UNIQUE, name: constraintName, tableName: constraint.table_name, columnNames: constraint.column_names as string[], @@ -107,7 +106,7 @@ export const readConstraints: DatabaseReader = async (schema, db) => { // check constraint case 'c': { table.constraints.push({ - type: DatabaseConstraintType.CHECK, + type: ConstraintType.CHECK, name: constraint.constraint_name, tableName: constraint.table_name, expression: constraint.expression.replace('CHECK ', ''), @@ -122,23 +121,23 @@ export const readConstraints: DatabaseReader = async (schema, db) => { const asDatabaseAction = (action: string) => { switch (action) { case 'a': { - return DatabaseActionType.NO_ACTION; + return ActionType.NO_ACTION; } case 'c': { - return DatabaseActionType.CASCADE; + return ActionType.CASCADE; } case 'r': { - return DatabaseActionType.RESTRICT; + return ActionType.RESTRICT; } case 'n': { - return DatabaseActionType.SET_NULL; + return ActionType.SET_NULL; } case 'd': { - return DatabaseActionType.SET_DEFAULT; + return ActionType.SET_DEFAULT; } default: { - return DatabaseActionType.NO_ACTION; + return ActionType.NO_ACTION; } } }; diff --git a/server/src/sql-tools/from-database/readers/extension.reader.ts b/server/src/sql-tools/readers/extension.reader.ts similarity index 86% rename from server/src/sql-tools/from-database/readers/extension.reader.ts rename to server/src/sql-tools/readers/extension.reader.ts index c0a59f85d8..6c7e6dd28a 100644 --- a/server/src/sql-tools/from-database/readers/extension.reader.ts +++ b/server/src/sql-tools/readers/extension.reader.ts @@ -1,4 +1,4 @@ -import { DatabaseReader } from 'src/sql-tools/from-database/readers/type'; +import { DatabaseReader } from 'src/sql-tools/types'; export const readExtensions: DatabaseReader = async (schema, db) => { const extensions = await db diff --git a/server/src/sql-tools/from-database/readers/function.reader.ts b/server/src/sql-tools/readers/function.reader.ts similarity index 92% rename from server/src/sql-tools/from-database/readers/function.reader.ts rename to server/src/sql-tools/readers/function.reader.ts index 0c81117d7c..385d061181 100644 --- a/server/src/sql-tools/from-database/readers/function.reader.ts +++ b/server/src/sql-tools/readers/function.reader.ts @@ -1,5 +1,5 @@ import { sql } from 'kysely'; -import { DatabaseReader } from 'src/sql-tools/from-database/readers/type'; +import { DatabaseReader } from 'src/sql-tools/types'; export const readFunctions: DatabaseReader = async (schema, db) => { const routines = await db diff --git a/server/src/sql-tools/from-database/readers/index.reader.ts b/server/src/sql-tools/readers/index.reader.ts similarity index 96% rename from server/src/sql-tools/from-database/readers/index.reader.ts rename to server/src/sql-tools/readers/index.reader.ts index 681ba8f9ef..e3fea7b74f 100644 --- a/server/src/sql-tools/from-database/readers/index.reader.ts +++ b/server/src/sql-tools/readers/index.reader.ts @@ -1,5 +1,5 @@ import { sql } from 'kysely'; -import { DatabaseReader } from 'src/sql-tools/from-database/readers/type'; +import { DatabaseReader } from 'src/sql-tools/types'; export const readIndexes: DatabaseReader = async (schema, db) => { const indexes = await db diff --git a/server/src/sql-tools/readers/index.ts b/server/src/sql-tools/readers/index.ts new file mode 100644 index 0000000000..d710fb4acb --- /dev/null +++ b/server/src/sql-tools/readers/index.ts @@ -0,0 +1,25 @@ +import { readColumns } from 'src/sql-tools/readers/column.reader'; +import { readComments } from 'src/sql-tools/readers/comment.reader'; +import { readConstraints } from 'src/sql-tools/readers/constraint.reader'; +import { readExtensions } from 'src/sql-tools/readers/extension.reader'; +import { readFunctions } from 'src/sql-tools/readers/function.reader'; +import { readIndexes } from 'src/sql-tools/readers/index.reader'; +import { readName } from 'src/sql-tools/readers/name.reader'; +import { readParameters } from 'src/sql-tools/readers/parameter.reader'; +import { readTables } from 'src/sql-tools/readers/table.reader'; +import { readTriggers } from 'src/sql-tools/readers/trigger.reader'; +import { DatabaseReader } from 'src/sql-tools/types'; + +export const readers: DatabaseReader[] = [ + // + readName, + readParameters, + readExtensions, + readFunctions, + readTables, + readColumns, + readIndexes, + readConstraints, + readTriggers, + readComments, +]; diff --git a/server/src/sql-tools/from-database/readers/name.reader.ts b/server/src/sql-tools/readers/name.reader.ts similarity index 66% rename from server/src/sql-tools/from-database/readers/name.reader.ts rename to server/src/sql-tools/readers/name.reader.ts index dc3d4ec745..5989ccb4c9 100644 --- a/server/src/sql-tools/from-database/readers/name.reader.ts +++ b/server/src/sql-tools/readers/name.reader.ts @@ -1,8 +1,8 @@ import { QueryResult, sql } from 'kysely'; -import { DatabaseReader } from 'src/sql-tools/from-database/readers/type'; +import { DatabaseReader } from 'src/sql-tools/types'; export const readName: DatabaseReader = async (schema, db) => { const result = (await sql`SELECT current_database() as name`.execute(db)) as QueryResult<{ name: string }>; - schema.name = result.rows[0].name; + schema.databaseName = result.rows[0].name; }; diff --git a/server/src/sql-tools/from-database/readers/parameter.reader.ts b/server/src/sql-tools/readers/parameter.reader.ts similarity index 76% rename from server/src/sql-tools/from-database/readers/parameter.reader.ts rename to server/src/sql-tools/readers/parameter.reader.ts index f58718a6b6..7930c251f4 100644 --- a/server/src/sql-tools/from-database/readers/parameter.reader.ts +++ b/server/src/sql-tools/readers/parameter.reader.ts @@ -1,6 +1,5 @@ import { sql } from 'kysely'; -import { DatabaseReader } from 'src/sql-tools/from-database/readers/type'; -import { ParameterScope } from 'src/sql-tools/types'; +import { DatabaseReader, ParameterScope } from 'src/sql-tools/types'; export const readParameters: DatabaseReader = async (schema, db) => { const parameters = await db @@ -13,7 +12,7 @@ export const readParameters: DatabaseReader = async (schema, db) => { schema.parameters.push({ name: parameter.name, value: parameter.value, - databaseName: schema.name, + databaseName: schema.databaseName, scope: parameter.scope as ParameterScope, synchronize: true, }); diff --git a/server/src/sql-tools/from-database/readers/table.reader.ts b/server/src/sql-tools/readers/table.reader.ts similarity index 87% rename from server/src/sql-tools/from-database/readers/table.reader.ts rename to server/src/sql-tools/readers/table.reader.ts index 6dbf9fc8b8..793da20644 100644 --- a/server/src/sql-tools/from-database/readers/table.reader.ts +++ b/server/src/sql-tools/readers/table.reader.ts @@ -1,5 +1,5 @@ import { sql } from 'kysely'; -import { DatabaseReader } from 'src/sql-tools/from-database/readers/type'; +import { DatabaseReader } from 'src/sql-tools/types'; export const readTables: DatabaseReader = async (schema, db) => { const tables = await db diff --git a/server/src/sql-tools/from-database/readers/trigger.reader.ts b/server/src/sql-tools/readers/trigger.reader.ts similarity index 56% rename from server/src/sql-tools/from-database/readers/trigger.reader.ts rename to server/src/sql-tools/readers/trigger.reader.ts index a174a7502b..09e7ae9c8a 100644 --- a/server/src/sql-tools/from-database/readers/trigger.reader.ts +++ b/server/src/sql-tools/readers/trigger.reader.ts @@ -1,5 +1,4 @@ -import { DatabaseReader } from 'src/sql-tools/from-database/readers/type'; -import { parseTriggerType } from 'src/sql-tools/helpers'; +import { DatabaseReader, TriggerAction, TriggerScope, TriggerTiming } from 'src/sql-tools/types'; export const readTriggers: DatabaseReader = async (schema, db) => { const triggers = await db @@ -44,3 +43,44 @@ export const readTriggers: DatabaseReader = async (schema, db) => { }); } }; + +export const hasMask = (input: number, mask: number) => (input & mask) === mask; + +export const parseTriggerType = (type: number) => { + // eslint-disable-next-line unicorn/prefer-math-trunc + const scope: TriggerScope = hasMask(type, 1 << 0) ? 'row' : 'statement'; + + let timing: TriggerTiming = 'after'; + const timingMasks: Array<{ mask: number; value: TriggerTiming }> = [ + { mask: 1 << 1, value: 'before' }, + { mask: 1 << 6, value: 'instead of' }, + ]; + + for (const { mask, value } of timingMasks) { + if (hasMask(type, mask)) { + timing = value; + break; + } + } + + const actions: TriggerAction[] = []; + const actionMasks: Array<{ mask: number; value: TriggerAction }> = [ + { mask: 1 << 2, value: 'insert' }, + { mask: 1 << 3, value: 'delete' }, + { mask: 1 << 4, value: 'update' }, + { mask: 1 << 5, value: 'truncate' }, + ]; + + for (const { mask, value } of actionMasks) { + if (hasMask(type, mask)) { + actions.push(value); + break; + } + } + + if (actions.length === 0) { + throw new Error(`Unable to parse trigger type ${type}`); + } + + return { actions, timing, scope }; +}; diff --git a/server/src/sql-tools/from-code/register-enum.ts b/server/src/sql-tools/register-enum.ts similarity index 86% rename from server/src/sql-tools/from-code/register-enum.ts rename to server/src/sql-tools/register-enum.ts index e2415cebff..5e9b41adcb 100644 --- a/server/src/sql-tools/from-code/register-enum.ts +++ b/server/src/sql-tools/register-enum.ts @@ -1,4 +1,4 @@ -import { register } from 'src/sql-tools/from-code/register'; +import { register } from 'src/sql-tools/register'; import { DatabaseEnum } from 'src/sql-tools/types'; export type EnumOptions = { diff --git a/server/src/sql-tools/from-code/register-function.ts b/server/src/sql-tools/register-function.ts similarity index 96% rename from server/src/sql-tools/from-code/register-function.ts rename to server/src/sql-tools/register-function.ts index 3e1e7054be..9f1c84c4fa 100644 --- a/server/src/sql-tools/from-code/register-function.ts +++ b/server/src/sql-tools/register-function.ts @@ -1,4 +1,4 @@ -import { register } from 'src/sql-tools/from-code/register'; +import { register } from 'src/sql-tools/register'; import { ColumnType, DatabaseFunction } from 'src/sql-tools/types'; export type FunctionOptions = { diff --git a/server/src/sql-tools/from-code/register-item.ts b/server/src/sql-tools/register-item.ts similarity index 53% rename from server/src/sql-tools/from-code/register-item.ts rename to server/src/sql-tools/register-item.ts index 2f394cf9c1..fede281a1b 100644 --- a/server/src/sql-tools/from-code/register-item.ts +++ b/server/src/sql-tools/register-item.ts @@ -1,17 +1,17 @@ -import { CheckOptions } from 'src/sql-tools/from-code/decorators/check.decorator'; -import { ColumnOptions } from 'src/sql-tools/from-code/decorators/column.decorator'; -import { ConfigurationParameterOptions } from 'src/sql-tools/from-code/decorators/configuration-parameter.decorator'; -import { DatabaseOptions } from 'src/sql-tools/from-code/decorators/database.decorator'; -import { ExtensionOptions } from 'src/sql-tools/from-code/decorators/extension.decorator'; -import { ForeignKeyColumnOptions } from 'src/sql-tools/from-code/decorators/foreign-key-column.decorator'; -import { ForeignKeyConstraintOptions } from 'src/sql-tools/from-code/decorators/foreign-key-constraint.decorator'; -import { IndexOptions } from 'src/sql-tools/from-code/decorators/index.decorator'; -import { TableOptions } from 'src/sql-tools/from-code/decorators/table.decorator'; -import { TriggerOptions } from 'src/sql-tools/from-code/decorators/trigger.decorator'; -import { UniqueOptions } from 'src/sql-tools/from-code/decorators/unique.decorator'; +/* eslint-disable @typescript-eslint/no-unsafe-function-type */ +import { CheckOptions } from 'src/sql-tools/decorators/check.decorator'; +import { ColumnOptions } from 'src/sql-tools/decorators/column.decorator'; +import { ConfigurationParameterOptions } from 'src/sql-tools/decorators/configuration-parameter.decorator'; +import { DatabaseOptions } from 'src/sql-tools/decorators/database.decorator'; +import { ExtensionOptions } from 'src/sql-tools/decorators/extension.decorator'; +import { ForeignKeyColumnOptions } from 'src/sql-tools/decorators/foreign-key-column.decorator'; +import { ForeignKeyConstraintOptions } from 'src/sql-tools/decorators/foreign-key-constraint.decorator'; +import { IndexOptions } from 'src/sql-tools/decorators/index.decorator'; +import { TableOptions } from 'src/sql-tools/decorators/table.decorator'; +import { TriggerOptions } from 'src/sql-tools/decorators/trigger.decorator'; +import { UniqueOptions } from 'src/sql-tools/decorators/unique.decorator'; import { DatabaseEnum, DatabaseFunction } from 'src/sql-tools/types'; -// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type export type ClassBased = { object: Function } & T; export type PropertyBased = { object: object; propertyName: string | symbol } & T; export type RegisterItem = @@ -26,6 +26,6 @@ export type RegisterItem = | { type: 'trigger'; item: ClassBased<{ options: TriggerOptions }> } | { type: 'extension'; item: ClassBased<{ options: ExtensionOptions }> } | { type: 'configurationParameter'; item: ClassBased<{ options: ConfigurationParameterOptions }> } - | { type: 'foreignKeyColumn'; item: PropertyBased<{ options: ForeignKeyColumnOptions; target: () => object }> } + | { type: 'foreignKeyColumn'; item: PropertyBased<{ options: ForeignKeyColumnOptions; target: () => Function }> } | { type: 'foreignKeyConstraint'; item: ClassBased<{ options: ForeignKeyConstraintOptions }> }; export type RegisterItemType = Extract['item']; diff --git a/server/src/sql-tools/from-code/register.ts b/server/src/sql-tools/register.ts similarity index 76% rename from server/src/sql-tools/from-code/register.ts rename to server/src/sql-tools/register.ts index 824af28c52..4df04c935a 100644 --- a/server/src/sql-tools/from-code/register.ts +++ b/server/src/sql-tools/register.ts @@ -1,4 +1,4 @@ -import { RegisterItem } from 'src/sql-tools/from-code/register-item'; +import { RegisterItem } from 'src/sql-tools/register-item'; const items: RegisterItem[] = []; diff --git a/server/src/sql-tools/schema-builder.ts b/server/src/sql-tools/schema-builder.ts new file mode 100644 index 0000000000..e9d82b509a --- /dev/null +++ b/server/src/sql-tools/schema-builder.ts @@ -0,0 +1,121 @@ +/* eslint-disable @typescript-eslint/no-unsafe-function-type */ +import { ColumnOptions, TableOptions } from 'src/sql-tools/decorators'; +import { asKey } from 'src/sql-tools/helpers'; +import { + DatabaseColumn, + DatabaseEnum, + DatabaseExtension, + DatabaseFunction, + DatabaseParameter, + DatabaseSchema, + DatabaseTable, + SchemaFromCodeOptions, +} from 'src/sql-tools/types'; + +type TableMetadata = { options: TableOptions; object: Function; methodToColumn: Map }; + +export class SchemaBuilder { + databaseName: string; + schemaName: string; + tables: DatabaseTable[] = []; + functions: DatabaseFunction[] = []; + enums: DatabaseEnum[] = []; + extensions: DatabaseExtension[] = []; + parameters: DatabaseParameter[] = []; + warnings: string[] = []; + + classToTable: WeakMap = new WeakMap(); + tableToMetadata: WeakMap = new WeakMap(); + + constructor(options: SchemaFromCodeOptions) { + this.databaseName = options.databaseName ?? 'postgres'; + this.schemaName = options.schemaName ?? 'public'; + } + + getTableByObject(object: Function) { + return this.classToTable.get(object); + } + + getTableByName(name: string) { + return this.tables.find((table) => table.name === name); + } + + getTableMetadata(table: DatabaseTable) { + const metadata = this.tableToMetadata.get(table); + if (!metadata) { + throw new Error(`Table metadata not found for table: ${table.name}`); + } + return metadata; + } + + addTable(table: DatabaseTable, options: TableOptions, object: Function) { + this.tables.push(table); + this.classToTable.set(object, table); + this.tableToMetadata.set(table, { options, object, methodToColumn: new Map() }); + } + + getColumnByObjectAndPropertyName( + object: object, + propertyName: string | symbol, + ): { table?: DatabaseTable; column?: DatabaseColumn } { + const table = this.getTableByObject(object.constructor); + if (!table) { + return {}; + } + + const tableMetadata = this.tableToMetadata.get(table); + if (!tableMetadata) { + return {}; + } + + const column = tableMetadata.methodToColumn.get(propertyName); + + return { table, column }; + } + + addColumn(table: DatabaseTable, column: DatabaseColumn, options: ColumnOptions, propertyName: string | symbol) { + table.columns.push(column); + const tableMetadata = this.getTableMetadata(table); + tableMetadata.methodToColumn.set(propertyName, column); + } + + asIndexName(table: string, columns?: string[], where?: string) { + const items: string[] = []; + for (const columnName of columns ?? []) { + items.push(columnName); + } + + if (where) { + items.push(where); + } + + return asKey('IDX_', table, items); + } + + warn(context: string, message: string) { + this.warnings.push(`[${context}] ${message}`); + } + + warnMissingTable(context: string, object: object, propertyName?: symbol | string) { + const label = object.constructor.name + (propertyName ? '.' + String(propertyName) : ''); + this.warn(context, `Unable to find table (${label})`); + } + + warnMissingColumn(context: string, object: object, propertyName?: symbol | string) { + const label = object.constructor.name + (propertyName ? '.' + String(propertyName) : ''); + this.warn(context, `Unable to find column (${label})`); + } + + build(): DatabaseSchema { + return { + databaseName: this.databaseName, + schemaName: this.schemaName, + tables: this.tables, + functions: this.functions, + enums: this.enums, + extensions: this.extensions, + parameters: this.parameters, + warnings: this.warnings, + }; + } +} diff --git a/server/src/sql-tools/diff/index.spec.ts b/server/src/sql-tools/schema-diff.spec.ts similarity index 90% rename from server/src/sql-tools/diff/index.spec.ts rename to server/src/sql-tools/schema-diff.spec.ts index 7ffd3946f2..98153eabd9 100644 --- a/server/src/sql-tools/diff/index.spec.ts +++ b/server/src/sql-tools/schema-diff.spec.ts @@ -1,10 +1,10 @@ -import { schemaDiff } from 'src/sql-tools/diff'; +import { schemaDiff } from 'src/sql-tools/schema-diff'; import { + ActionType, ColumnType, - DatabaseActionType, + ConstraintType, DatabaseColumn, DatabaseConstraint, - DatabaseConstraintType, DatabaseIndex, DatabaseSchema, DatabaseTable, @@ -15,7 +15,7 @@ const fromColumn = (column: Partial>): Databas const tableName = 'table1'; return { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -49,7 +49,7 @@ const fromConstraint = (constraint?: DatabaseConstraint): DatabaseSchema => { const tableName = constraint?.tableName || 'table1'; return { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -82,7 +82,7 @@ const fromIndex = (index?: DatabaseIndex): DatabaseSchema => { const tableName = index?.tableName || 'table1'; return { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -155,7 +155,7 @@ const newSchema = (schema: { } return { - name: 'immich', + databaseName: 'immich', schemaName: schema?.name || 'public', functions: [], enums: [], @@ -166,14 +166,14 @@ const newSchema = (schema: { }; }; -describe('schemaDiff', () => { +describe(schemaDiff.name, () => { it('should work', () => { const diff = schemaDiff(newSchema({ tables: [] }), newSchema({ tables: [] })); expect(diff.items).toEqual([]); }); describe('table', () => { - describe('table.create', () => { + describe('TableCreate', () => { it('should find a missing table', () => { const column: DatabaseColumn = { type: 'character varying', @@ -190,7 +190,7 @@ describe('schemaDiff', () => { expect(diff.items).toHaveLength(1); expect(diff.items[0]).toEqual({ - type: 'table.create', + type: 'TableCreate', table: { name: 'table1', columns: [column], @@ -204,7 +204,7 @@ describe('schemaDiff', () => { }); }); - describe('table.drop', () => { + describe('TableDrop', () => { it('should find an extra table', () => { const diff = schemaDiff( newSchema({ tables: [] }), @@ -216,7 +216,7 @@ describe('schemaDiff', () => { expect(diff.items).toHaveLength(1); expect(diff.items[0]).toEqual({ - type: 'table.drop', + type: 'TableDrop', tableName: 'table1', reason: 'missing in source', }); @@ -238,7 +238,7 @@ describe('schemaDiff', () => { }); describe('column', () => { - describe('column.add', () => { + describe('ColumnAdd', () => { it('should find a new column', () => { const diff = schemaDiff( newSchema({ @@ -256,7 +256,7 @@ describe('schemaDiff', () => { expect(diff.items).toEqual([ { - type: 'column.add', + type: 'ColumnAdd', column: { tableName: 'table1', isArray: false, @@ -271,7 +271,7 @@ describe('schemaDiff', () => { }); }); - describe('column.drop', () => { + describe('ColumnDrop', () => { it('should find an extra column', () => { const diff = schemaDiff( newSchema({ @@ -289,7 +289,7 @@ describe('schemaDiff', () => { expect(diff.items).toEqual([ { - type: 'column.drop', + type: 'ColumnDrop', tableName: 'table1', columnName: 'column2', reason: 'missing in source', @@ -307,7 +307,7 @@ describe('schemaDiff', () => { expect(diff.items).toEqual([ { - type: 'column.alter', + type: 'ColumnAlter', tableName: 'table1', columnName: 'column1', changes: { @@ -326,7 +326,7 @@ describe('schemaDiff', () => { expect(diff.items).toEqual([ { - type: 'column.alter', + type: 'ColumnAlter', tableName: 'table1', columnName: 'column1', changes: { @@ -347,7 +347,7 @@ describe('schemaDiff', () => { expect(diff.items).toEqual([ { - type: 'column.alter', + type: 'ColumnAlter', tableName: 'table1', columnName: 'column1', changes: { @@ -388,12 +388,12 @@ describe('schemaDiff', () => { }); describe('constraint', () => { - describe('constraint.add', () => { + describe('ConstraintAdd', () => { it('should detect a new constraint', () => { const diff = schemaDiff( fromConstraint({ name: 'PK_test', - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, tableName: 'table1', columnNames: ['id'], synchronize: true, @@ -403,9 +403,9 @@ describe('schemaDiff', () => { expect(diff.items).toEqual([ { - type: 'constraint.add', + type: 'ConstraintAdd', constraint: { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_test', columnNames: ['id'], tableName: 'table1', @@ -417,13 +417,13 @@ describe('schemaDiff', () => { }); }); - describe('constraint.drop', () => { + describe('ConstraintDrop', () => { it('should detect an extra constraint', () => { const diff = schemaDiff( fromConstraint(), fromConstraint({ name: 'PK_test', - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, tableName: 'table1', columnNames: ['id'], synchronize: true, @@ -432,7 +432,7 @@ describe('schemaDiff', () => { expect(diff.items).toEqual([ { - type: 'constraint.drop', + type: 'ConstraintDrop', tableName: 'table1', constraintName: 'PK_test', reason: 'missing in source', @@ -444,7 +444,7 @@ describe('schemaDiff', () => { describe('primary key', () => { it('should skip identical primary key constraints', () => { const constraint: DatabaseConstraint = { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_test', tableName: 'table1', columnNames: ['id'], @@ -460,7 +460,7 @@ describe('schemaDiff', () => { describe('foreign key', () => { it('should skip identical foreign key constraints', () => { const constraint: DatabaseConstraint = { - type: DatabaseConstraintType.FOREIGN_KEY, + type: ConstraintType.FOREIGN_KEY, name: 'FK_test', tableName: 'table1', columnNames: ['parentId'], @@ -476,7 +476,7 @@ describe('schemaDiff', () => { it('should drop and recreate when the column changes', () => { const constraint: DatabaseConstraint = { - type: DatabaseConstraintType.FOREIGN_KEY, + type: ConstraintType.FOREIGN_KEY, name: 'FK_test', tableName: 'table1', columnNames: ['parentId'], @@ -495,7 +495,7 @@ describe('schemaDiff', () => { constraintName: 'FK_test', reason: 'columns are different (parentId vs parentId2)', tableName: 'table1', - type: 'constraint.drop', + type: 'ConstraintDrop', }, { constraint: { @@ -508,20 +508,20 @@ describe('schemaDiff', () => { type: 'foreign-key', }, reason: 'columns are different (parentId vs parentId2)', - type: 'constraint.add', + type: 'ConstraintAdd', }, ]); }); it('should drop and recreate when the ON DELETE action changes', () => { const constraint: DatabaseConstraint = { - type: DatabaseConstraintType.FOREIGN_KEY, + type: ConstraintType.FOREIGN_KEY, name: 'FK_test', tableName: 'table1', columnNames: ['parentId'], referenceTableName: 'table2', referenceColumnNames: ['id'], - onDelete: DatabaseActionType.CASCADE, + onDelete: ActionType.CASCADE, synchronize: true, }; @@ -532,7 +532,7 @@ describe('schemaDiff', () => { constraintName: 'FK_test', reason: 'ON DELETE action is different (CASCADE vs NO ACTION)', tableName: 'table1', - type: 'constraint.drop', + type: 'ConstraintDrop', }, { constraint: { @@ -540,13 +540,13 @@ describe('schemaDiff', () => { name: 'FK_test', referenceColumnNames: ['id'], referenceTableName: 'table2', - onDelete: DatabaseActionType.CASCADE, + onDelete: ActionType.CASCADE, synchronize: true, tableName: 'table1', type: 'foreign-key', }, reason: 'ON DELETE action is different (CASCADE vs NO ACTION)', - type: 'constraint.add', + type: 'ConstraintAdd', }, ]); }); @@ -555,7 +555,7 @@ describe('schemaDiff', () => { describe('unique', () => { it('should skip identical unique constraints', () => { const constraint: DatabaseConstraint = { - type: DatabaseConstraintType.UNIQUE, + type: ConstraintType.UNIQUE, name: 'UQ_test', tableName: 'table1', columnNames: ['id'], @@ -571,7 +571,7 @@ describe('schemaDiff', () => { describe('check', () => { it('should skip identical check constraints', () => { const constraint: DatabaseConstraint = { - type: DatabaseConstraintType.CHECK, + type: ConstraintType.CHECK, name: 'CHK_test', tableName: 'table1', expression: 'column1 > 0', @@ -586,7 +586,7 @@ describe('schemaDiff', () => { }); describe('index', () => { - describe('index.create', () => { + describe('IndexCreate', () => { it('should detect a new index', () => { const diff = schemaDiff( fromIndex({ @@ -601,7 +601,7 @@ describe('schemaDiff', () => { expect(diff.items).toEqual([ { - type: 'index.create', + type: 'IndexCreate', index: { name: 'IDX_test', columnNames: ['id'], @@ -615,7 +615,7 @@ describe('schemaDiff', () => { }); }); - describe('index.drop', () => { + describe('IndexDrop', () => { it('should detect an extra index', () => { const diff = schemaDiff( fromIndex(), @@ -630,7 +630,7 @@ describe('schemaDiff', () => { expect(diff.items).toEqual([ { - type: 'index.drop', + type: 'IndexDrop', indexName: 'IDX_test', reason: 'missing in source', }, @@ -650,12 +650,12 @@ describe('schemaDiff', () => { expect(diff.items).toEqual([ { - type: 'index.drop', + type: 'IndexDrop', indexName: 'IDX_test', reason: 'uniqueness is different (true vs false)', }, { - type: 'index.create', + type: 'IndexCreate', index, reason: 'uniqueness is different (true vs false)', }, diff --git a/server/src/sql-tools/schema-diff.ts b/server/src/sql-tools/schema-diff.ts new file mode 100644 index 0000000000..9708a11c4a --- /dev/null +++ b/server/src/sql-tools/schema-diff.ts @@ -0,0 +1,121 @@ +import { compareEnums } from 'src/sql-tools/comparers/enum.comparer'; +import { compareExtensions } from 'src/sql-tools/comparers/extension.comparer'; +import { compareFunctions } from 'src/sql-tools/comparers/function.comparer'; +import { compareParameters } from 'src/sql-tools/comparers/parameter.comparer'; +import { compareTables } from 'src/sql-tools/comparers/table.comparer'; +import { compare } from 'src/sql-tools/helpers'; +import { transformers } from 'src/sql-tools/transformers'; +import { + ConstraintType, + DatabaseSchema, + SchemaDiff, + SchemaDiffOptions, + SchemaDiffToSqlOptions, +} from 'src/sql-tools/types'; + +/** + * Compute the difference between two database schemas + */ +export const schemaDiff = (source: DatabaseSchema, target: DatabaseSchema, options: SchemaDiffOptions = {}) => { + const items = [ + ...compare(source.parameters, target.parameters, options.parameters, compareParameters), + ...compare(source.extensions, target.extensions, options.extension, compareExtensions), + ...compare(source.functions, target.functions, options.functions, compareFunctions), + ...compare(source.enums, target.enums, options.enums, compareEnums), + ...compare(source.tables, target.tables, options.tables, compareTables), + ]; + + type SchemaName = SchemaDiff['type']; + const itemMap: Record = { + EnumCreate: [], + EnumDrop: [], + ExtensionCreate: [], + ExtensionDrop: [], + FunctionCreate: [], + FunctionDrop: [], + TableCreate: [], + TableDrop: [], + ColumnAdd: [], + ColumnAlter: [], + ColumnDrop: [], + ConstraintAdd: [], + ConstraintDrop: [], + IndexCreate: [], + IndexDrop: [], + TriggerCreate: [], + TriggerDrop: [], + ParameterSet: [], + ParameterReset: [], + }; + + for (const item of items) { + itemMap[item.type].push(item); + } + + const constraintAdds = itemMap.ConstraintAdd.filter((item) => item.type === 'ConstraintAdd'); + + const orderedItems = [ + ...itemMap.ExtensionCreate, + ...itemMap.FunctionCreate, + ...itemMap.ParameterSet, + ...itemMap.ParameterReset, + ...itemMap.EnumCreate, + ...itemMap.TriggerDrop, + ...itemMap.IndexDrop, + ...itemMap.ConstraintDrop, + ...itemMap.TableCreate, + ...itemMap.ColumnAlter, + ...itemMap.ColumnAdd, + ...constraintAdds.filter(({ constraint }) => constraint.type === ConstraintType.PRIMARY_KEY), + ...constraintAdds.filter(({ constraint }) => constraint.type === ConstraintType.FOREIGN_KEY), + ...constraintAdds.filter(({ constraint }) => constraint.type === ConstraintType.UNIQUE), + ...constraintAdds.filter(({ constraint }) => constraint.type === ConstraintType.CHECK), + ...itemMap.IndexCreate, + ...itemMap.TriggerCreate, + ...itemMap.ColumnDrop, + ...itemMap.TableDrop, + ...itemMap.EnumDrop, + ...itemMap.FunctionDrop, + ]; + + return { + items: orderedItems, + asSql: (options?: SchemaDiffToSqlOptions) => schemaDiffToSql(orderedItems, options), + }; +}; + +/** + * Convert schema diffs into SQL statements + */ +export const schemaDiffToSql = (items: SchemaDiff[], options: SchemaDiffToSqlOptions = {}): string[] => { + return items.flatMap((item) => asSql(item).map((result) => result + withComments(options.comments, item))); +}; + +const asSql = (item: SchemaDiff): string[] => { + for (const transform of transformers) { + const result = transform(item); + if (!result) { + continue; + } + + return asArray(result); + } + + throw new Error(`Unhandled schema diff type: ${item.type}`); +}; + +const withComments = (comments: boolean | undefined, item: SchemaDiff): string => { + if (!comments) { + return ''; + } + + return ` -- ${item.reason}`; +}; + +const asArray = (items: T | T[]): T[] => { + if (Array.isArray(items)) { + return items; + } + + return [items]; +}; diff --git a/server/src/sql-tools/schema-from-code.spec.ts b/server/src/sql-tools/schema-from-code.spec.ts new file mode 100644 index 0000000000..55e64e0b4e --- /dev/null +++ b/server/src/sql-tools/schema-from-code.spec.ts @@ -0,0 +1,46 @@ +import { readdirSync } from 'node:fs'; +import { join } from 'node:path'; +import { schemaFromCode } from 'src/sql-tools/schema-from-code'; +import { describe, expect, it } from 'vitest'; + +describe(schemaFromCode.name, () => { + it('should work', () => { + expect(schemaFromCode({ reset: true })).toEqual({ + databaseName: 'postgres', + schemaName: 'public', + functions: [], + enums: [], + extensions: [], + parameters: [], + tables: [], + warnings: [], + }); + }); + + describe('test files', () => { + const errorStubs = readdirSync('test/sql-tools/errors', { withFileTypes: true }); + for (const file of errorStubs) { + const filePath = join(file.parentPath, file.name); + it(filePath, async () => { + const module = await import(filePath); + expect(module.message).toBeDefined(); + expect(() => schemaFromCode({ reset: true })).toThrowError(module.message); + }); + } + + const stubs = readdirSync('test/sql-tools', { withFileTypes: true }); + for (const file of stubs) { + if (file.isDirectory()) { + continue; + } + + const filePath = join(file.parentPath, file.name); + it(filePath, async () => { + const module = await import(filePath); + expect(module.description).toBeDefined(); + expect(module.schema).toBeDefined(); + expect(schemaFromCode({ reset: true }), module.description).toEqual(module.schema); + }); + } + }); +}); diff --git a/server/src/sql-tools/schema-from-code.ts b/server/src/sql-tools/schema-from-code.ts new file mode 100644 index 0000000000..f36e0ee07e --- /dev/null +++ b/server/src/sql-tools/schema-from-code.ts @@ -0,0 +1,29 @@ +import { processors } from 'src/sql-tools/processors'; +import { getRegisteredItems, resetRegisteredItems } from 'src/sql-tools/register'; +import { SchemaBuilder } from 'src/sql-tools/schema-builder'; +import { SchemaFromCodeOptions } from 'src/sql-tools/types'; + +/** + * Load schema from code (decorators, etc) + */ +export const schemaFromCode = (options: SchemaFromCodeOptions = {}) => { + try { + const globalOptions = { + createForeignKeyIndexes: options.createForeignKeyIndexes ?? true, + }; + + const builder = new SchemaBuilder(options); + const items = getRegisteredItems(); + for (const processor of processors) { + processor(builder, items, globalOptions); + } + + const newSchema = builder.build(); + + return newSchema; + } finally { + if (options.reset) { + resetRegisteredItems(); + } + } +}; diff --git a/server/src/sql-tools/schema-from-database.ts b/server/src/sql-tools/schema-from-database.ts new file mode 100644 index 0000000000..7122ec1105 --- /dev/null +++ b/server/src/sql-tools/schema-from-database.ts @@ -0,0 +1,33 @@ +import { Kysely } from 'kysely'; +import { PostgresJSDialect } from 'kysely-postgres-js'; +import { Sql } from 'postgres'; +import { readers } from 'src/sql-tools/readers'; +import { DatabaseSchema, PostgresDB, SchemaFromDatabaseOptions } from 'src/sql-tools/types'; + +/** + * Load schema from a database url + */ +export const schemaFromDatabase = async ( + postgres: Sql, + options: SchemaFromDatabaseOptions = {}, +): Promise => { + const schema: DatabaseSchema = { + databaseName: 'immich', + schemaName: options.schemaName || 'public', + parameters: [], + functions: [], + enums: [], + extensions: [], + tables: [], + warnings: [], + }; + + const db = new Kysely({ dialect: new PostgresJSDialect({ postgres }) }); + for (const reader of readers) { + await reader(schema, db); + } + + await db.destroy(); + + return schema; +}; diff --git a/server/src/sql-tools/to-sql/index.spec.ts b/server/src/sql-tools/to-sql/index.spec.ts deleted file mode 100644 index 509f44ebe5..0000000000 --- a/server/src/sql-tools/to-sql/index.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { schemaDiffToSql } from 'src/sql-tools'; -import { describe, expect, it } from 'vitest'; - -describe(schemaDiffToSql.name, () => { - describe('comments', () => { - it('should include the reason in a SQL comment', () => { - expect( - schemaDiffToSql( - [ - { - type: 'index.drop', - indexName: 'IDX_test', - reason: 'unknown', - }, - ], - { comments: true }, - ), - ).toEqual([`DROP INDEX "IDX_test"; -- unknown`]); - }); - }); -}); diff --git a/server/src/sql-tools/to-sql/index.ts b/server/src/sql-tools/to-sql/index.ts deleted file mode 100644 index 973c7ef287..0000000000 --- a/server/src/sql-tools/to-sql/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { transformColumns } from 'src/sql-tools/to-sql/transformers/column.transformer'; -import { transformConstraints } from 'src/sql-tools/to-sql/transformers/constraint.transformer'; -import { transformEnums } from 'src/sql-tools/to-sql/transformers/enum.transformer'; -import { transformExtensions } from 'src/sql-tools/to-sql/transformers/extension.transformer'; -import { transformFunctions } from 'src/sql-tools/to-sql/transformers/function.transformer'; -import { transformIndexes } from 'src/sql-tools/to-sql/transformers/index.transformer'; -import { transformParameters } from 'src/sql-tools/to-sql/transformers/parameter.transformer'; -import { transformTables } from 'src/sql-tools/to-sql/transformers/table.transformer'; -import { transformTriggers } from 'src/sql-tools/to-sql/transformers/trigger.transformer'; -import { SqlTransformer } from 'src/sql-tools/to-sql/transformers/types'; -import { SchemaDiff, SchemaDiffToSqlOptions } from 'src/sql-tools/types'; - -/** - * Convert schema diffs into SQL statements - */ -export const schemaDiffToSql = (items: SchemaDiff[], options: SchemaDiffToSqlOptions = {}): string[] => { - return items.flatMap((item) => asSql(item).map((result) => result + withComments(options.comments, item))); -}; - -const transformers: SqlTransformer[] = [ - transformColumns, - transformConstraints, - transformEnums, - transformExtensions, - transformFunctions, - transformIndexes, - transformParameters, - transformTables, - transformTriggers, -]; - -const asSql = (item: SchemaDiff): string[] => { - for (const transform of transformers) { - const result = transform(item); - if (!result) { - continue; - } - - return asArray(result); - } - - throw new Error(`Unhandled schema diff type: ${item.type}`); -}; - -const withComments = (comments: boolean | undefined, item: SchemaDiff): string => { - if (!comments) { - return ''; - } - - return ` -- ${item.reason}`; -}; - -const asArray = (items: T | T[]): T[] => { - if (Array.isArray(items)) { - return items; - } - - return [items]; -}; diff --git a/server/src/sql-tools/to-sql/transformers/column.transformer.spec.ts b/server/src/sql-tools/transformers/column.transformer.spec.ts similarity index 87% rename from server/src/sql-tools/to-sql/transformers/column.transformer.spec.ts rename to server/src/sql-tools/transformers/column.transformer.spec.ts index 8bf5ac3bc4..2f975381aa 100644 --- a/server/src/sql-tools/to-sql/transformers/column.transformer.spec.ts +++ b/server/src/sql-tools/transformers/column.transformer.spec.ts @@ -1,12 +1,12 @@ -import { transformColumns } from 'src/sql-tools/to-sql/transformers/column.transformer'; +import { transformColumns } from 'src/sql-tools/transformers/column.transformer'; import { describe, expect, it } from 'vitest'; describe(transformColumns.name, () => { - describe('column.add', () => { + describe('ColumnAdd', () => { it('should work', () => { expect( transformColumns({ - type: 'column.add', + type: 'ColumnAdd', column: { name: 'column1', tableName: 'table1', @@ -23,7 +23,7 @@ describe(transformColumns.name, () => { it('should add a nullable column', () => { expect( transformColumns({ - type: 'column.add', + type: 'ColumnAdd', column: { name: 'column1', tableName: 'table1', @@ -40,7 +40,7 @@ describe(transformColumns.name, () => { it('should add a column with an enum type', () => { expect( transformColumns({ - type: 'column.add', + type: 'ColumnAdd', column: { name: 'column1', tableName: 'table1', @@ -58,7 +58,7 @@ describe(transformColumns.name, () => { it('should add a column that is an array type', () => { expect( transformColumns({ - type: 'column.add', + type: 'ColumnAdd', column: { name: 'column1', tableName: 'table1', @@ -73,11 +73,11 @@ describe(transformColumns.name, () => { }); }); - describe('column.alter', () => { + describe('ColumnAlter', () => { it('should make a column nullable', () => { expect( transformColumns({ - type: 'column.alter', + type: 'ColumnAlter', tableName: 'table1', columnName: 'column1', changes: { nullable: true }, @@ -89,7 +89,7 @@ describe(transformColumns.name, () => { it('should make a column non-nullable', () => { expect( transformColumns({ - type: 'column.alter', + type: 'ColumnAlter', tableName: 'table1', columnName: 'column1', changes: { nullable: false }, @@ -101,7 +101,7 @@ describe(transformColumns.name, () => { it('should update the default value', () => { expect( transformColumns({ - type: 'column.alter', + type: 'ColumnAlter', tableName: 'table1', columnName: 'column1', changes: { default: 'uuid_generate_v4()' }, @@ -111,11 +111,11 @@ describe(transformColumns.name, () => { }); }); - describe('column.drop', () => { + describe('ColumnDrop', () => { it('should work', () => { expect( transformColumns({ - type: 'column.drop', + type: 'ColumnDrop', tableName: 'table1', columnName: 'column1', reason: 'unknown', diff --git a/server/src/sql-tools/to-sql/transformers/column.transformer.ts b/server/src/sql-tools/transformers/column.transformer.ts similarity index 91% rename from server/src/sql-tools/to-sql/transformers/column.transformer.ts rename to server/src/sql-tools/transformers/column.transformer.ts index 117b460938..ab1135379c 100644 --- a/server/src/sql-tools/to-sql/transformers/column.transformer.ts +++ b/server/src/sql-tools/transformers/column.transformer.ts @@ -1,18 +1,18 @@ import { asColumnComment, getColumnModifiers, getColumnType } from 'src/sql-tools/helpers'; -import { SqlTransformer } from 'src/sql-tools/to-sql/transformers/types'; +import { SqlTransformer } from 'src/sql-tools/transformers/types'; import { ColumnChanges, DatabaseColumn, SchemaDiff } from 'src/sql-tools/types'; export const transformColumns: SqlTransformer = (item: SchemaDiff) => { switch (item.type) { - case 'column.add': { + case 'ColumnAdd': { return asColumnAdd(item.column); } - case 'column.alter': { + case 'ColumnAlter': { return asColumnAlter(item.tableName, item.columnName, item.changes); } - case 'column.drop': { + case 'ColumnDrop': { return asColumnDrop(item.tableName, item.columnName); } diff --git a/server/src/sql-tools/to-sql/transformers/constraint.transformer.spec.ts b/server/src/sql-tools/transformers/constraint.transformer.spec.ts similarity index 78% rename from server/src/sql-tools/to-sql/transformers/constraint.transformer.spec.ts rename to server/src/sql-tools/transformers/constraint.transformer.spec.ts index 59d21e7b50..595db10a39 100644 --- a/server/src/sql-tools/to-sql/transformers/constraint.transformer.spec.ts +++ b/server/src/sql-tools/transformers/constraint.transformer.spec.ts @@ -1,16 +1,16 @@ -import { transformConstraints } from 'src/sql-tools/to-sql/transformers/constraint.transformer'; -import { DatabaseConstraintType } from 'src/sql-tools/types'; +import { transformConstraints } from 'src/sql-tools/transformers/constraint.transformer'; +import { ConstraintType } from 'src/sql-tools/types'; import { describe, expect, it } from 'vitest'; describe(transformConstraints.name, () => { - describe('constraint.add', () => { + describe('ConstraintAdd', () => { describe('primary keys', () => { it('should work', () => { expect( transformConstraints({ - type: 'constraint.add', + type: 'ConstraintAdd', constraint: { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_test', tableName: 'table1', columnNames: ['id'], @@ -26,9 +26,9 @@ describe(transformConstraints.name, () => { it('should work', () => { expect( transformConstraints({ - type: 'constraint.add', + type: 'ConstraintAdd', constraint: { - type: DatabaseConstraintType.FOREIGN_KEY, + type: ConstraintType.FOREIGN_KEY, name: 'FK_test', tableName: 'table1', columnNames: ['parentId'], @@ -48,9 +48,9 @@ describe(transformConstraints.name, () => { it('should work', () => { expect( transformConstraints({ - type: 'constraint.add', + type: 'ConstraintAdd', constraint: { - type: DatabaseConstraintType.UNIQUE, + type: ConstraintType.UNIQUE, name: 'UQ_test', tableName: 'table1', columnNames: ['id'], @@ -66,9 +66,9 @@ describe(transformConstraints.name, () => { it('should work', () => { expect( transformConstraints({ - type: 'constraint.add', + type: 'ConstraintAdd', constraint: { - type: DatabaseConstraintType.CHECK, + type: ConstraintType.CHECK, name: 'CHK_test', tableName: 'table1', expression: '"id" IS NOT NULL', @@ -81,11 +81,11 @@ describe(transformConstraints.name, () => { }); }); - describe('constraint.drop', () => { + describe('ConstraintDrop', () => { it('should work', () => { expect( transformConstraints({ - type: 'constraint.drop', + type: 'ConstraintDrop', tableName: 'table1', constraintName: 'PK_test', reason: 'unknown', diff --git a/server/src/sql-tools/to-sql/transformers/constraint.transformer.ts b/server/src/sql-tools/transformers/constraint.transformer.ts similarity index 68% rename from server/src/sql-tools/to-sql/transformers/constraint.transformer.ts rename to server/src/sql-tools/transformers/constraint.transformer.ts index ec65143eba..d9c2fcd8d1 100644 --- a/server/src/sql-tools/to-sql/transformers/constraint.transformer.ts +++ b/server/src/sql-tools/transformers/constraint.transformer.ts @@ -1,14 +1,14 @@ import { asColumnList } from 'src/sql-tools/helpers'; -import { SqlTransformer } from 'src/sql-tools/to-sql/transformers/types'; -import { DatabaseActionType, DatabaseConstraint, DatabaseConstraintType, SchemaDiff } from 'src/sql-tools/types'; +import { SqlTransformer } from 'src/sql-tools/transformers/types'; +import { ActionType, ConstraintType, DatabaseConstraint, SchemaDiff } from 'src/sql-tools/types'; export const transformConstraints: SqlTransformer = (item: SchemaDiff) => { switch (item.type) { - case 'constraint.add': { + case 'ConstraintAdd': { return asConstraintAdd(item.constraint); } - case 'constraint.drop': { + case 'ConstraintDrop': { return asConstraintDrop(item.tableName, item.constraintName); } default: { @@ -17,18 +17,18 @@ export const transformConstraints: SqlTransformer = (item: SchemaDiff) => { } }; -const withAction = (constraint: { onDelete?: DatabaseActionType; onUpdate?: DatabaseActionType }) => - ` ON UPDATE ${constraint.onUpdate ?? DatabaseActionType.NO_ACTION} ON DELETE ${constraint.onDelete ?? DatabaseActionType.NO_ACTION}`; +const withAction = (constraint: { onDelete?: ActionType; onUpdate?: ActionType }) => + ` ON UPDATE ${constraint.onUpdate ?? ActionType.NO_ACTION} ON DELETE ${constraint.onDelete ?? ActionType.NO_ACTION}`; export const asConstraintAdd = (constraint: DatabaseConstraint): string | string[] => { const base = `ALTER TABLE "${constraint.tableName}" ADD CONSTRAINT "${constraint.name}"`; switch (constraint.type) { - case DatabaseConstraintType.PRIMARY_KEY: { + case ConstraintType.PRIMARY_KEY: { const columnNames = asColumnList(constraint.columnNames); return `${base} PRIMARY KEY (${columnNames});`; } - case DatabaseConstraintType.FOREIGN_KEY: { + case ConstraintType.FOREIGN_KEY: { const columnNames = asColumnList(constraint.columnNames); const referenceColumnNames = asColumnList(constraint.referenceColumnNames); return ( @@ -38,12 +38,12 @@ export const asConstraintAdd = (constraint: DatabaseConstraint): string | string ); } - case DatabaseConstraintType.UNIQUE: { + case ConstraintType.UNIQUE: { const columnNames = asColumnList(constraint.columnNames); return `${base} UNIQUE (${columnNames});`; } - case DatabaseConstraintType.CHECK: { + case ConstraintType.CHECK: { return `${base} CHECK (${constraint.expression});`; } diff --git a/server/src/sql-tools/to-sql/transformers/enum.transformer.ts b/server/src/sql-tools/transformers/enum.transformer.ts similarity index 81% rename from server/src/sql-tools/to-sql/transformers/enum.transformer.ts rename to server/src/sql-tools/transformers/enum.transformer.ts index d5764d9b16..b012ff1a56 100644 --- a/server/src/sql-tools/to-sql/transformers/enum.transformer.ts +++ b/server/src/sql-tools/transformers/enum.transformer.ts @@ -1,13 +1,13 @@ -import { SqlTransformer } from 'src/sql-tools/to-sql/transformers/types'; +import { SqlTransformer } from 'src/sql-tools/transformers/types'; import { DatabaseEnum, SchemaDiff } from 'src/sql-tools/types'; export const transformEnums: SqlTransformer = (item: SchemaDiff) => { switch (item.type) { - case 'enum.create': { + case 'EnumCreate': { return asEnumCreate(item.enum); } - case 'enum.drop': { + case 'EnumDrop': { return asEnumDrop(item.enumName); } diff --git a/server/src/sql-tools/to-sql/transformers/extension.transformer.spec.ts b/server/src/sql-tools/transformers/extension.transformer.spec.ts similarity index 70% rename from server/src/sql-tools/to-sql/transformers/extension.transformer.spec.ts rename to server/src/sql-tools/transformers/extension.transformer.spec.ts index 81b2db4d27..655b6ad20d 100644 --- a/server/src/sql-tools/to-sql/transformers/extension.transformer.spec.ts +++ b/server/src/sql-tools/transformers/extension.transformer.spec.ts @@ -1,12 +1,12 @@ -import { transformExtensions } from 'src/sql-tools/to-sql/transformers/extension.transformer'; +import { transformExtensions } from 'src/sql-tools/transformers/extension.transformer'; import { describe, expect, it } from 'vitest'; describe(transformExtensions.name, () => { - describe('extension.drop', () => { + describe('ExtensionDrop', () => { it('should work', () => { expect( transformExtensions({ - type: 'extension.drop', + type: 'ExtensionDrop', extensionName: 'cube', reason: 'unknown', }), @@ -14,11 +14,11 @@ describe(transformExtensions.name, () => { }); }); - describe('extension.create', () => { + describe('ExtensionCreate', () => { it('should work', () => { expect( transformExtensions({ - type: 'extension.create', + type: 'ExtensionCreate', extension: { name: 'cube', synchronize: true, diff --git a/server/src/sql-tools/to-sql/transformers/extension.transformer.ts b/server/src/sql-tools/transformers/extension.transformer.ts similarity index 81% rename from server/src/sql-tools/to-sql/transformers/extension.transformer.ts rename to server/src/sql-tools/transformers/extension.transformer.ts index 2d51a26444..df1ee7da97 100644 --- a/server/src/sql-tools/to-sql/transformers/extension.transformer.ts +++ b/server/src/sql-tools/transformers/extension.transformer.ts @@ -1,13 +1,13 @@ -import { SqlTransformer } from 'src/sql-tools/to-sql/transformers/types'; +import { SqlTransformer } from 'src/sql-tools/transformers/types'; import { DatabaseExtension, SchemaDiff } from 'src/sql-tools/types'; export const transformExtensions: SqlTransformer = (item: SchemaDiff) => { switch (item.type) { - case 'extension.create': { + case 'ExtensionCreate': { return asExtensionCreate(item.extension); } - case 'extension.drop': { + case 'ExtensionDrop': { return asExtensionDrop(item.extensionName); } diff --git a/server/src/sql-tools/to-sql/transformers/function.transformer.spec.ts b/server/src/sql-tools/transformers/function.transformer.spec.ts similarity index 65% rename from server/src/sql-tools/to-sql/transformers/function.transformer.spec.ts rename to server/src/sql-tools/transformers/function.transformer.spec.ts index 6e9a5bac56..b24256cb38 100644 --- a/server/src/sql-tools/to-sql/transformers/function.transformer.spec.ts +++ b/server/src/sql-tools/transformers/function.transformer.spec.ts @@ -1,12 +1,12 @@ -import { transformFunctions } from 'src/sql-tools/to-sql/transformers/function.transformer'; +import { transformFunctions } from 'src/sql-tools/transformers/function.transformer'; import { describe, expect, it } from 'vitest'; describe(transformFunctions.name, () => { - describe('function.drop', () => { + describe('FunctionDrop', () => { it('should work', () => { expect( transformFunctions({ - type: 'function.drop', + type: 'FunctionDrop', functionName: 'test_func', reason: 'unknown', }), diff --git a/server/src/sql-tools/to-sql/transformers/function.transformer.ts b/server/src/sql-tools/transformers/function.transformer.ts similarity index 79% rename from server/src/sql-tools/to-sql/transformers/function.transformer.ts rename to server/src/sql-tools/transformers/function.transformer.ts index f05eca099a..01915349f0 100644 --- a/server/src/sql-tools/to-sql/transformers/function.transformer.ts +++ b/server/src/sql-tools/transformers/function.transformer.ts @@ -1,13 +1,13 @@ -import { SqlTransformer } from 'src/sql-tools/to-sql/transformers/types'; +import { SqlTransformer } from 'src/sql-tools/transformers/types'; import { DatabaseFunction, SchemaDiff } from 'src/sql-tools/types'; export const transformFunctions: SqlTransformer = (item: SchemaDiff) => { switch (item.type) { - case 'function.create': { + case 'FunctionCreate': { return asFunctionCreate(item.function); } - case 'function.drop': { + case 'FunctionDrop': { return asFunctionDrop(item.functionName); } diff --git a/server/src/sql-tools/to-sql/transformers/index.transformer.spec.ts b/server/src/sql-tools/transformers/index.transformer.spec.ts similarity index 87% rename from server/src/sql-tools/to-sql/transformers/index.transformer.spec.ts rename to server/src/sql-tools/transformers/index.transformer.spec.ts index af3cc0286c..0599b00bdd 100644 --- a/server/src/sql-tools/to-sql/transformers/index.transformer.spec.ts +++ b/server/src/sql-tools/transformers/index.transformer.spec.ts @@ -1,12 +1,12 @@ -import { transformIndexes } from 'src/sql-tools/to-sql/transformers/index.transformer'; +import { transformIndexes } from 'src/sql-tools/transformers/index.transformer'; import { describe, expect, it } from 'vitest'; describe(transformIndexes.name, () => { - describe('index.create', () => { + describe('IndexCreate', () => { it('should work', () => { expect( transformIndexes({ - type: 'index.create', + type: 'IndexCreate', index: { name: 'IDX_test', tableName: 'table1', @@ -22,7 +22,7 @@ describe(transformIndexes.name, () => { it('should create an unique index', () => { expect( transformIndexes({ - type: 'index.create', + type: 'IndexCreate', index: { name: 'IDX_test', tableName: 'table1', @@ -38,7 +38,7 @@ describe(transformIndexes.name, () => { it('should create an index with a custom expression', () => { expect( transformIndexes({ - type: 'index.create', + type: 'IndexCreate', index: { name: 'IDX_test', tableName: 'table1', @@ -54,7 +54,7 @@ describe(transformIndexes.name, () => { it('should create an index with a where clause', () => { expect( transformIndexes({ - type: 'index.create', + type: 'IndexCreate', index: { name: 'IDX_test', tableName: 'table1', @@ -71,7 +71,7 @@ describe(transformIndexes.name, () => { it('should create an index with a custom expression', () => { expect( transformIndexes({ - type: 'index.create', + type: 'IndexCreate', index: { name: 'IDX_test', tableName: 'table1', @@ -86,11 +86,11 @@ describe(transformIndexes.name, () => { }); }); - describe('index.drop', () => { + describe('IndexDrop', () => { it('should work', () => { expect( transformIndexes({ - type: 'index.drop', + type: 'IndexDrop', indexName: 'IDX_test', reason: 'unknown', }), diff --git a/server/src/sql-tools/to-sql/transformers/index.transformer.ts b/server/src/sql-tools/transformers/index.transformer.ts similarity index 89% rename from server/src/sql-tools/to-sql/transformers/index.transformer.ts rename to server/src/sql-tools/transformers/index.transformer.ts index 73d9ac9615..001b15315f 100644 --- a/server/src/sql-tools/to-sql/transformers/index.transformer.ts +++ b/server/src/sql-tools/transformers/index.transformer.ts @@ -1,14 +1,14 @@ import { asColumnList } from 'src/sql-tools/helpers'; -import { SqlTransformer } from 'src/sql-tools/to-sql/transformers/types'; +import { SqlTransformer } from 'src/sql-tools/transformers/types'; import { DatabaseIndex, SchemaDiff } from 'src/sql-tools/types'; export const transformIndexes: SqlTransformer = (item: SchemaDiff) => { switch (item.type) { - case 'index.create': { + case 'IndexCreate': { return asIndexCreate(item.index); } - case 'index.drop': { + case 'IndexDrop': { return asIndexDrop(item.indexName); } diff --git a/server/src/sql-tools/transformers/index.ts b/server/src/sql-tools/transformers/index.ts new file mode 100644 index 0000000000..2865a376d7 --- /dev/null +++ b/server/src/sql-tools/transformers/index.ts @@ -0,0 +1,22 @@ +import { transformColumns } from 'src/sql-tools/transformers/column.transformer'; +import { transformConstraints } from 'src/sql-tools/transformers/constraint.transformer'; +import { transformEnums } from 'src/sql-tools/transformers/enum.transformer'; +import { transformExtensions } from 'src/sql-tools/transformers/extension.transformer'; +import { transformFunctions } from 'src/sql-tools/transformers/function.transformer'; +import { transformIndexes } from 'src/sql-tools/transformers/index.transformer'; +import { transformParameters } from 'src/sql-tools/transformers/parameter.transformer'; +import { transformTables } from 'src/sql-tools/transformers/table.transformer'; +import { transformTriggers } from 'src/sql-tools/transformers/trigger.transformer'; +import { SqlTransformer } from 'src/sql-tools/transformers/types'; + +export const transformers: SqlTransformer[] = [ + transformColumns, + transformConstraints, + transformEnums, + transformExtensions, + transformFunctions, + transformIndexes, + transformParameters, + transformTables, + transformTriggers, +]; diff --git a/server/src/sql-tools/to-sql/transformers/parameter.transformer.ts b/server/src/sql-tools/transformers/parameter.transformer.ts similarity index 85% rename from server/src/sql-tools/to-sql/transformers/parameter.transformer.ts rename to server/src/sql-tools/transformers/parameter.transformer.ts index 0b12cdb27b..7768298d1d 100644 --- a/server/src/sql-tools/to-sql/transformers/parameter.transformer.ts +++ b/server/src/sql-tools/transformers/parameter.transformer.ts @@ -1,13 +1,13 @@ -import { SqlTransformer } from 'src/sql-tools/to-sql/transformers/types'; +import { SqlTransformer } from 'src/sql-tools/transformers/types'; import { DatabaseParameter, SchemaDiff } from 'src/sql-tools/types'; export const transformParameters: SqlTransformer = (item: SchemaDiff) => { switch (item.type) { - case 'parameter.set': { + case 'ParameterSet': { return asParameterSet(item.parameter); } - case 'parameter.reset': { + case 'ParameterReset': { return asParameterReset(item.databaseName, item.parameterName); } diff --git a/server/src/sql-tools/to-sql/transformers/table.transformer.spec.ts b/server/src/sql-tools/transformers/table.transformer.spec.ts similarity index 91% rename from server/src/sql-tools/to-sql/transformers/table.transformer.spec.ts rename to server/src/sql-tools/transformers/table.transformer.spec.ts index db3ffa22ec..c6f20b3da3 100644 --- a/server/src/sql-tools/to-sql/transformers/table.transformer.spec.ts +++ b/server/src/sql-tools/transformers/table.transformer.spec.ts @@ -1,12 +1,12 @@ -import { transformTables } from 'src/sql-tools/to-sql/transformers/table.transformer'; +import { transformTables } from 'src/sql-tools/transformers/table.transformer'; import { describe, expect, it } from 'vitest'; describe(transformTables.name, () => { - describe('table.drop', () => { + describe('TableDrop', () => { it('should work', () => { expect( transformTables({ - type: 'table.drop', + type: 'TableDrop', tableName: 'table1', reason: 'unknown', }), @@ -14,11 +14,11 @@ describe(transformTables.name, () => { }); }); - describe('table.create', () => { + describe('TableCreate', () => { it('should work', () => { expect( transformTables({ - type: 'table.create', + type: 'TableCreate', table: { name: 'table1', columns: [ @@ -44,7 +44,7 @@ describe(transformTables.name, () => { it('should handle a non-nullable column', () => { expect( transformTables({ - type: 'table.create', + type: 'TableCreate', table: { name: 'table1', columns: [ @@ -70,7 +70,7 @@ describe(transformTables.name, () => { it('should handle a default value', () => { expect( transformTables({ - type: 'table.create', + type: 'TableCreate', table: { name: 'table1', columns: [ @@ -97,7 +97,7 @@ describe(transformTables.name, () => { it('should handle a string with a fixed length', () => { expect( transformTables({ - type: 'table.create', + type: 'TableCreate', table: { name: 'table1', columns: [ @@ -124,7 +124,7 @@ describe(transformTables.name, () => { it('should handle an array type', () => { expect( transformTables({ - type: 'table.create', + type: 'TableCreate', table: { name: 'table1', columns: [ diff --git a/server/src/sql-tools/to-sql/transformers/table.transformer.ts b/server/src/sql-tools/transformers/table.transformer.ts similarity index 83% rename from server/src/sql-tools/to-sql/transformers/table.transformer.ts rename to server/src/sql-tools/transformers/table.transformer.ts index f376b65274..b1fefab3e0 100644 --- a/server/src/sql-tools/to-sql/transformers/table.transformer.ts +++ b/server/src/sql-tools/transformers/table.transformer.ts @@ -1,15 +1,15 @@ import { asColumnComment, getColumnModifiers, getColumnType } from 'src/sql-tools/helpers'; -import { asColumnAlter } from 'src/sql-tools/to-sql/transformers/column.transformer'; -import { SqlTransformer } from 'src/sql-tools/to-sql/transformers/types'; +import { asColumnAlter } from 'src/sql-tools/transformers/column.transformer'; +import { SqlTransformer } from 'src/sql-tools/transformers/types'; import { DatabaseTable, SchemaDiff } from 'src/sql-tools/types'; export const transformTables: SqlTransformer = (item: SchemaDiff) => { switch (item.type) { - case 'table.create': { + case 'TableCreate': { return asTableCreate(item.table); } - case 'table.drop': { + case 'TableDrop': { return asTableDrop(item.tableName); } diff --git a/server/src/sql-tools/to-sql/transformers/trigger.transformer.spec.ts b/server/src/sql-tools/transformers/trigger.transformer.spec.ts similarity index 87% rename from server/src/sql-tools/to-sql/transformers/trigger.transformer.spec.ts rename to server/src/sql-tools/transformers/trigger.transformer.spec.ts index 778de88cba..9fdb8d1c23 100644 --- a/server/src/sql-tools/to-sql/transformers/trigger.transformer.spec.ts +++ b/server/src/sql-tools/transformers/trigger.transformer.spec.ts @@ -1,12 +1,12 @@ -import { transformTriggers } from 'src/sql-tools/to-sql/transformers/trigger.transformer'; +import { transformTriggers } from 'src/sql-tools/transformers/trigger.transformer'; import { describe, expect, it } from 'vitest'; describe(transformTriggers.name, () => { - describe('trigger.create', () => { + describe('TriggerCreate', () => { it('should work', () => { expect( transformTriggers({ - type: 'trigger.create', + type: 'TriggerCreate', trigger: { name: 'trigger1', tableName: 'table1', @@ -29,7 +29,7 @@ describe(transformTriggers.name, () => { it('should work with multiple actions', () => { expect( transformTriggers({ - type: 'trigger.create', + type: 'TriggerCreate', trigger: { name: 'trigger1', tableName: 'table1', @@ -52,7 +52,7 @@ describe(transformTriggers.name, () => { it('should work with old/new reference table aliases', () => { expect( transformTriggers({ - type: 'trigger.create', + type: 'TriggerCreate', trigger: { name: 'trigger1', tableName: 'table1', @@ -76,11 +76,11 @@ describe(transformTriggers.name, () => { }); }); - describe('trigger.drop', () => { + describe('TriggerDrop', () => { it('should work', () => { expect( transformTriggers({ - type: 'trigger.drop', + type: 'TriggerDrop', tableName: 'table1', triggerName: 'trigger1', reason: 'unknown', diff --git a/server/src/sql-tools/to-sql/transformers/trigger.transformer.ts b/server/src/sql-tools/transformers/trigger.transformer.ts similarity index 91% rename from server/src/sql-tools/to-sql/transformers/trigger.transformer.ts rename to server/src/sql-tools/transformers/trigger.transformer.ts index c104a2ed6b..167449e249 100644 --- a/server/src/sql-tools/to-sql/transformers/trigger.transformer.ts +++ b/server/src/sql-tools/transformers/trigger.transformer.ts @@ -1,13 +1,13 @@ -import { SqlTransformer } from 'src/sql-tools/to-sql/transformers/types'; +import { SqlTransformer } from 'src/sql-tools/transformers/types'; import { DatabaseTrigger, SchemaDiff } from 'src/sql-tools/types'; export const transformTriggers: SqlTransformer = (item: SchemaDiff) => { switch (item.type) { - case 'trigger.create': { + case 'TriggerCreate': { return asTriggerCreate(item.trigger); } - case 'trigger.drop': { + case 'TriggerDrop': { return asTriggerDrop(item.tableName, item.triggerName); } diff --git a/server/src/sql-tools/to-sql/transformers/types.ts b/server/src/sql-tools/transformers/types.ts similarity index 100% rename from server/src/sql-tools/to-sql/transformers/types.ts rename to server/src/sql-tools/transformers/types.ts diff --git a/server/src/sql-tools/types.ts b/server/src/sql-tools/types.ts index 0641487e0e..a3aec7a061 100644 --- a/server/src/sql-tools/types.ts +++ b/server/src/sql-tools/types.ts @@ -1,4 +1,38 @@ import { Kysely, ColumnType as KyselyColumnType } from 'kysely'; +import { RegisterItem } from 'src/sql-tools/register-item'; +import { SchemaBuilder } from 'src/sql-tools/schema-builder'; + +export type SchemaFromCodeOptions = { + /** automatically create indexes on foreign key columns */ + createForeignKeyIndexes?: boolean; + databaseName?: string; + schemaName?: string; + reset?: boolean; +}; + +export type SchemaFromDatabaseOptions = { + schemaName?: string; +}; + +export type SchemaDiffToSqlOptions = { + comments?: boolean; +}; + +export type SchemaDiffOptions = { + tables?: IgnoreOptions; + functions?: IgnoreOptions; + enums?: IgnoreOptions; + extension?: IgnoreOptions; + parameters?: IgnoreOptions; +}; + +export type IgnoreOptions = { + ignoreExtra?: boolean; + ignoreMissing?: boolean; +}; + +export type Processor = (builder: SchemaBuilder, items: RegisterItem[], options: SchemaFromCodeOptions) => void; +export type DatabaseReader = (schema: DatabaseSchema, db: DatabaseClient) => Promise; export type PostgresDB = { pg_am: { @@ -237,14 +271,14 @@ type PostgresYesOrNo = 'YES' | 'NO'; export type DatabaseClient = Kysely; -export enum DatabaseConstraintType { +export enum ConstraintType { PRIMARY_KEY = 'primary-key', FOREIGN_KEY = 'foreign-key', UNIQUE = 'unique', CHECK = 'check', } -export enum DatabaseActionType { +export enum ActionType { NO_ACTION = 'NO ACTION', RESTRICT = 'RESTRICT', CASCADE = 'CASCADE', @@ -278,7 +312,7 @@ export type ColumnType = | 'serial'; export type DatabaseSchema = { - name: string; + databaseName: string; schemaName: string; functions: DatabaseFunction[]; enums: DatabaseEnum[]; @@ -288,19 +322,6 @@ export type DatabaseSchema = { warnings: string[]; }; -export type SchemaDiffOptions = { - tables?: DiffOptions; - functions?: DiffOptions; - enums?: DiffOptions; - extension?: DiffOptions; - parameters?: DiffOptions; -}; - -export type DiffOptions = { - ignoreExtra?: boolean; - ignoreMissing?: boolean; -}; - export type DatabaseParameter = { name: string; databaseName: string; @@ -381,26 +402,26 @@ type ColumBasedConstraint = { }; export type DatabasePrimaryKeyConstraint = ColumBasedConstraint & { - type: DatabaseConstraintType.PRIMARY_KEY; + type: ConstraintType.PRIMARY_KEY; synchronize: boolean; }; export type DatabaseUniqueConstraint = ColumBasedConstraint & { - type: DatabaseConstraintType.UNIQUE; + type: ConstraintType.UNIQUE; synchronize: boolean; }; export type DatabaseForeignKeyConstraint = ColumBasedConstraint & { - type: DatabaseConstraintType.FOREIGN_KEY; + type: ConstraintType.FOREIGN_KEY; referenceTableName: string; referenceColumnNames: string[]; - onUpdate?: DatabaseActionType; - onDelete?: DatabaseActionType; + onUpdate?: ActionType; + onDelete?: ActionType; synchronize: boolean; }; export type DatabaseCheckConstraint = { - type: DatabaseConstraintType.CHECK; + type: ConstraintType.CHECK; name: string; tableName: string; expression: string; @@ -435,34 +456,26 @@ export type DatabaseIndex = { synchronize: boolean; }; -export type LoadSchemaOptions = { - schemaName?: string; -}; - -export type SchemaDiffToSqlOptions = { - comments?: boolean; -}; - export type SchemaDiff = { reason: string } & ( - | { type: 'extension.create'; extension: DatabaseExtension } - | { type: 'extension.drop'; extensionName: string } - | { type: 'function.create'; function: DatabaseFunction } - | { type: 'function.drop'; functionName: string } - | { type: 'table.create'; table: DatabaseTable } - | { type: 'table.drop'; tableName: string } - | { type: 'column.add'; column: DatabaseColumn } - | { type: 'column.alter'; tableName: string; columnName: string; changes: ColumnChanges } - | { type: 'column.drop'; tableName: string; columnName: string } - | { type: 'constraint.add'; constraint: DatabaseConstraint } - | { type: 'constraint.drop'; tableName: string; constraintName: string } - | { type: 'index.create'; index: DatabaseIndex } - | { type: 'index.drop'; indexName: string } - | { type: 'trigger.create'; trigger: DatabaseTrigger } - | { type: 'trigger.drop'; tableName: string; triggerName: string } - | { type: 'parameter.set'; parameter: DatabaseParameter } - | { type: 'parameter.reset'; databaseName: string; parameterName: string } - | { type: 'enum.create'; enum: DatabaseEnum } - | { type: 'enum.drop'; enumName: string } + | { type: 'ExtensionCreate'; extension: DatabaseExtension } + | { type: 'ExtensionDrop'; extensionName: string } + | { type: 'FunctionCreate'; function: DatabaseFunction } + | { type: 'FunctionDrop'; functionName: string } + | { type: 'TableCreate'; table: DatabaseTable } + | { type: 'TableDrop'; tableName: string } + | { type: 'ColumnAdd'; column: DatabaseColumn } + | { type: 'ColumnAlter'; tableName: string; columnName: string; changes: ColumnChanges } + | { type: 'ColumnDrop'; tableName: string; columnName: string } + | { type: 'ConstraintAdd'; constraint: DatabaseConstraint } + | { type: 'ConstraintDrop'; tableName: string; constraintName: string } + | { type: 'IndexCreate'; index: DatabaseIndex } + | { type: 'IndexDrop'; indexName: string } + | { type: 'TriggerCreate'; trigger: DatabaseTrigger } + | { type: 'TriggerDrop'; tableName: string; triggerName: string } + | { type: 'ParameterSet'; parameter: DatabaseParameter } + | { type: 'ParameterReset'; databaseName: string; parameterName: string } + | { type: 'EnumCreate'; enum: DatabaseEnum } + | { type: 'EnumDrop'; enumName: string } ); export type CompareFunction = (source: T, target: T) => SchemaDiff[]; diff --git a/server/test/sql-tools/check-constraint-default-name.stub.ts b/server/test/sql-tools/check-constraint-default-name.stub.ts index af03e02a2e..4ed0e6ad3a 100644 --- a/server/test/sql-tools/check-constraint-default-name.stub.ts +++ b/server/test/sql-tools/check-constraint-default-name.stub.ts @@ -1,4 +1,4 @@ -import { Check, Column, DatabaseConstraintType, DatabaseSchema, Table } from 'src/sql-tools'; +import { Check, Column, ConstraintType, DatabaseSchema, Table } from 'src/sql-tools'; @Table() @Check({ expression: '1=1' }) @@ -9,7 +9,7 @@ export class Table1 { export const description = 'should create a check constraint with a default name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -33,7 +33,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.CHECK, + type: ConstraintType.CHECK, name: 'CHK_8d2ecfd49b984941f6b2589799', tableName: 'table1', expression: '1=1', diff --git a/server/test/sql-tools/check-constraint-override-name.stub.ts b/server/test/sql-tools/check-constraint-override-name.stub.ts index b30025e2fc..1dfcfe7566 100644 --- a/server/test/sql-tools/check-constraint-override-name.stub.ts +++ b/server/test/sql-tools/check-constraint-override-name.stub.ts @@ -1,4 +1,4 @@ -import { Check, Column, DatabaseConstraintType, DatabaseSchema, Table } from 'src/sql-tools'; +import { Check, Column, ConstraintType, DatabaseSchema, Table } from 'src/sql-tools'; @Table() @Check({ name: 'CHK_test', expression: '1=1' }) @@ -9,7 +9,7 @@ export class Table1 { export const description = 'should create a check constraint with a specific name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -33,7 +33,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.CHECK, + type: ConstraintType.CHECK, name: 'CHK_test', tableName: 'table1', expression: '1=1', diff --git a/server/test/sql-tools/column-create-date.stub.ts b/server/test/sql-tools/column-create-date.stub.ts index 7a284c674c..42fe3f98ae 100644 --- a/server/test/sql-tools/column-create-date.stub.ts +++ b/server/test/sql-tools/column-create-date.stub.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should register a table with an created at date column'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-default-boolean.stub.ts b/server/test/sql-tools/column-default-boolean.stub.ts index 962b023a25..eafefc524e 100644 --- a/server/test/sql-tools/column-default-boolean.stub.ts +++ b/server/test/sql-tools/column-default-boolean.stub.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should register a table with a column with a default value (boolean)'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-default-date.stub.ts b/server/test/sql-tools/column-default-date.stub.ts index 00f2db2c27..b1998b47b2 100644 --- a/server/test/sql-tools/column-default-date.stub.ts +++ b/server/test/sql-tools/column-default-date.stub.ts @@ -10,7 +10,7 @@ export class Table1 { export const description = 'should register a table with a column with a default value (date)'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-default-function.stub.ts b/server/test/sql-tools/column-default-function.stub.ts index b13bd14c93..57579cb230 100644 --- a/server/test/sql-tools/column-default-function.stub.ts +++ b/server/test/sql-tools/column-default-function.stub.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should register a table with a column with a default function'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-default-null.stub.ts b/server/test/sql-tools/column-default-null.stub.ts index c88ed218b3..869d02cf94 100644 --- a/server/test/sql-tools/column-default-null.stub.ts +++ b/server/test/sql-tools/column-default-null.stub.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should register a nullable column from a default of null'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-default-number.stub.ts b/server/test/sql-tools/column-default-number.stub.ts index 36d0af5273..afec0da8fb 100644 --- a/server/test/sql-tools/column-default-number.stub.ts +++ b/server/test/sql-tools/column-default-number.stub.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should register a table with a column with a default value (number)'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-default-string.stub.ts b/server/test/sql-tools/column-default-string.stub.ts index 04a00a4dfe..4b989a3f3b 100644 --- a/server/test/sql-tools/column-default-string.stub.ts +++ b/server/test/sql-tools/column-default-string.stub.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should register a table with a column with a default value (string)'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-delete-date.stub.ts b/server/test/sql-tools/column-delete-date.stub.ts index facbfb0328..437407e33a 100644 --- a/server/test/sql-tools/column-delete-date.stub.ts +++ b/server/test/sql-tools/column-delete-date.stub.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should register a table with a deleted at date column'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-enum-type.stub.ts b/server/test/sql-tools/column-enum-type.stub.ts index 878910dcdb..02fbeeabde 100644 --- a/server/test/sql-tools/column-enum-type.stub.ts +++ b/server/test/sql-tools/column-enum-type.stub.ts @@ -15,7 +15,7 @@ export class Table1 { export const description = 'should accept an enum type'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [ diff --git a/server/test/sql-tools/column-generated-identity.ts b/server/test/sql-tools/column-generated-identity.ts index 98b0f582a6..a6525fe9ac 100644 --- a/server/test/sql-tools/column-generated-identity.ts +++ b/server/test/sql-tools/column-generated-identity.ts @@ -1,4 +1,4 @@ -import { DatabaseConstraintType, DatabaseSchema, PrimaryGeneratedColumn, Table } from 'src/sql-tools'; +import { ConstraintType, DatabaseSchema, PrimaryGeneratedColumn, Table } from 'src/sql-tools'; @Table() export class Table1 { @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should register a table with a generated identity column'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -33,7 +33,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_50c4f9905061b1e506d38a2a380', tableName: 'table1', columnNames: ['column1'], diff --git a/server/test/sql-tools/column-generated-uuid.stub.ts b/server/test/sql-tools/column-generated-uuid.stub.ts index 69cc59530e..4d355f2cf6 100644 --- a/server/test/sql-tools/column-generated-uuid.stub.ts +++ b/server/test/sql-tools/column-generated-uuid.stub.ts @@ -1,4 +1,4 @@ -import { DatabaseConstraintType, DatabaseSchema, PrimaryGeneratedColumn, Table } from 'src/sql-tools'; +import { ConstraintType, DatabaseSchema, PrimaryGeneratedColumn, Table } from 'src/sql-tools'; @Table() export class Table1 { @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should register a table with a primary generated uuid column'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -33,7 +33,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_50c4f9905061b1e506d38a2a380', tableName: 'table1', columnNames: ['column1'], diff --git a/server/test/sql-tools/column-index-name-default.ts b/server/test/sql-tools/column-index-name-default.ts index cedae006be..703a526285 100644 --- a/server/test/sql-tools/column-index-name-default.ts +++ b/server/test/sql-tools/column-index-name-default.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should create a column with an index'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-index-name.ts b/server/test/sql-tools/column-index-name.ts index 8ba18a8851..c72759c74d 100644 --- a/server/test/sql-tools/column-index-name.ts +++ b/server/test/sql-tools/column-index-name.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should create a column with an index if a name is provided'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-inferred-nullable.stub.ts b/server/test/sql-tools/column-inferred-nullable.stub.ts index 70495db800..64c6a8be99 100644 --- a/server/test/sql-tools/column-inferred-nullable.stub.ts +++ b/server/test/sql-tools/column-inferred-nullable.stub.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should infer nullable from the default value'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-name-default.stub.ts b/server/test/sql-tools/column-name-default.stub.ts index e1db458e4b..fc16c7540d 100644 --- a/server/test/sql-tools/column-name-default.stub.ts +++ b/server/test/sql-tools/column-name-default.stub.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should register a table with a column with a default name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-name-override.stub.ts b/server/test/sql-tools/column-name-override.stub.ts index 250e295280..5e5594a166 100644 --- a/server/test/sql-tools/column-name-override.stub.ts +++ b/server/test/sql-tools/column-name-override.stub.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should register a table with a column with a specific name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-name-string.stub.ts b/server/test/sql-tools/column-name-string.stub.ts index 12f8b4a537..d0455ee773 100644 --- a/server/test/sql-tools/column-name-string.stub.ts +++ b/server/test/sql-tools/column-name-string.stub.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should register a table with a column with a specific name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-nullable.stub.ts b/server/test/sql-tools/column-nullable.stub.ts index 2b82a3de13..037b2db948 100644 --- a/server/test/sql-tools/column-nullable.stub.ts +++ b/server/test/sql-tools/column-nullable.stub.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should set nullable correctly'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-string-length.stub.ts b/server/test/sql-tools/column-string-length.stub.ts index 47400f25e0..00d7264914 100644 --- a/server/test/sql-tools/column-string-length.stub.ts +++ b/server/test/sql-tools/column-string-length.stub.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should use create a string column with a fixed length'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/column-unique-constraint-name-default.stub.ts b/server/test/sql-tools/column-unique-constraint-name-default.stub.ts index e1e1619679..b1dfaf4308 100644 --- a/server/test/sql-tools/column-unique-constraint-name-default.stub.ts +++ b/server/test/sql-tools/column-unique-constraint-name-default.stub.ts @@ -1,4 +1,4 @@ -import { Column, DatabaseConstraintType, DatabaseSchema, Table } from 'src/sql-tools'; +import { Column, ConstraintType, DatabaseSchema, Table } from 'src/sql-tools'; @Table() export class Table1 { @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should create a unique key constraint with a default name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -32,7 +32,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.UNIQUE, + type: ConstraintType.UNIQUE, name: 'UQ_b249cc64cf63b8a22557cdc8537', tableName: 'table1', columnNames: ['id'], diff --git a/server/test/sql-tools/column-unique-constraint-name-override.stub.ts b/server/test/sql-tools/column-unique-constraint-name-override.stub.ts index 36ce80efb6..b873085fa0 100644 --- a/server/test/sql-tools/column-unique-constraint-name-override.stub.ts +++ b/server/test/sql-tools/column-unique-constraint-name-override.stub.ts @@ -1,4 +1,4 @@ -import { Column, DatabaseConstraintType, DatabaseSchema, Table } from 'src/sql-tools'; +import { Column, ConstraintType, DatabaseSchema, Table } from 'src/sql-tools'; @Table() export class Table1 { @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should create a unique key constraint with a specific name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -32,7 +32,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.UNIQUE, + type: ConstraintType.UNIQUE, name: 'UQ_test', tableName: 'table1', columnNames: ['id'], diff --git a/server/test/sql-tools/column-update-date.stub.ts b/server/test/sql-tools/column-update-date.stub.ts index bbdb6df923..f63f568cf6 100644 --- a/server/test/sql-tools/column-update-date.stub.ts +++ b/server/test/sql-tools/column-update-date.stub.ts @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should register a table with an updated at date column'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/errors/table-duplicate-decorator.stub.ts b/server/test/sql-tools/errors/table-duplicate-decorator.stub.ts new file mode 100644 index 0000000000..3b7a8781b9 --- /dev/null +++ b/server/test/sql-tools/errors/table-duplicate-decorator.stub.ts @@ -0,0 +1,7 @@ +import { Table } from 'src/sql-tools'; + +@Table({ name: 'table-1' }) +@Table({ name: 'table-2' }) +export class Table1 {} + +export const message = 'Table table-2 has already been registered'; diff --git a/server/test/sql-tools/foreign-key-constraint-column-order.stub.ts b/server/test/sql-tools/foreign-key-constraint-column-order.stub.ts index b211620343..c31a00c186 100644 --- a/server/test/sql-tools/foreign-key-constraint-column-order.stub.ts +++ b/server/test/sql-tools/foreign-key-constraint-column-order.stub.ts @@ -1,11 +1,4 @@ -import { - Column, - DatabaseConstraintType, - DatabaseSchema, - ForeignKeyConstraint, - PrimaryColumn, - Table, -} from 'src/sql-tools'; +import { Column, ConstraintType, DatabaseSchema, ForeignKeyConstraint, PrimaryColumn, Table } from 'src/sql-tools'; @Table() export class Table1 { @@ -32,7 +25,7 @@ export class Table2 { export const description = 'should create a foreign key constraint to the target table'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -65,7 +58,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_e457e8b1301b7bc06ef78188ee4', tableName: 'table1', columnNames: ['id1', 'id2'], @@ -108,7 +101,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.FOREIGN_KEY, + type: ConstraintType.FOREIGN_KEY, name: 'FK_aed36d04470eba20161aa8b1dc6', tableName: 'table2', columnNames: ['parentId1', 'parentId2'], diff --git a/server/test/sql-tools/foreign-key-constraint-missing-column.stub.ts b/server/test/sql-tools/foreign-key-constraint-missing-column.stub.ts index 5bb335030a..f42f1251f4 100644 --- a/server/test/sql-tools/foreign-key-constraint-missing-column.stub.ts +++ b/server/test/sql-tools/foreign-key-constraint-missing-column.stub.ts @@ -1,11 +1,4 @@ -import { - Column, - DatabaseConstraintType, - DatabaseSchema, - ForeignKeyConstraint, - PrimaryColumn, - Table, -} from 'src/sql-tools'; +import { Column, ConstraintType, DatabaseSchema, ForeignKeyConstraint, PrimaryColumn, Table } from 'src/sql-tools'; @Table() export class Table1 { @@ -22,7 +15,7 @@ export class Table2 { export const description = 'should warn against missing column in foreign key constraint'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -46,7 +39,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_b249cc64cf63b8a22557cdc8537', tableName: 'table1', columnNames: ['id'], diff --git a/server/test/sql-tools/foreign-key-constraint-missing-reference-column.stub.ts b/server/test/sql-tools/foreign-key-constraint-missing-reference-column.stub.ts index 83c3adeb6d..8eb7b5cde8 100644 --- a/server/test/sql-tools/foreign-key-constraint-missing-reference-column.stub.ts +++ b/server/test/sql-tools/foreign-key-constraint-missing-reference-column.stub.ts @@ -1,11 +1,4 @@ -import { - Column, - DatabaseConstraintType, - DatabaseSchema, - ForeignKeyConstraint, - PrimaryColumn, - Table, -} from 'src/sql-tools'; +import { Column, ConstraintType, DatabaseSchema, ForeignKeyConstraint, PrimaryColumn, Table } from 'src/sql-tools'; @Table() export class Table1 { @@ -22,7 +15,7 @@ export class Table2 { export const description = 'should warn against missing reference column in foreign key constraint'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -46,7 +39,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_b249cc64cf63b8a22557cdc8537', tableName: 'table1', columnNames: ['id'], diff --git a/server/test/sql-tools/foreign-key-constraint-missing-reference-table.stub.ts b/server/test/sql-tools/foreign-key-constraint-missing-reference-table.stub.ts index 54cf731479..47c9f18e1d 100644 --- a/server/test/sql-tools/foreign-key-constraint-missing-reference-table.stub.ts +++ b/server/test/sql-tools/foreign-key-constraint-missing-reference-table.stub.ts @@ -14,7 +14,7 @@ export class Table1 { export const description = 'should warn against missing reference table'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/foreign-key-constraint-multiple-columns.stub.ts b/server/test/sql-tools/foreign-key-constraint-multiple-columns.stub.ts index 30f18eaf9d..6e2ee7741e 100644 --- a/server/test/sql-tools/foreign-key-constraint-multiple-columns.stub.ts +++ b/server/test/sql-tools/foreign-key-constraint-multiple-columns.stub.ts @@ -1,11 +1,4 @@ -import { - Column, - DatabaseConstraintType, - DatabaseSchema, - ForeignKeyConstraint, - PrimaryColumn, - Table, -} from 'src/sql-tools'; +import { Column, ConstraintType, DatabaseSchema, ForeignKeyConstraint, PrimaryColumn, Table } from 'src/sql-tools'; @Table() export class Table1 { @@ -28,7 +21,7 @@ export class Table2 { export const description = 'should create a foreign key constraint to the target table'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -61,7 +54,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_e457e8b1301b7bc06ef78188ee4', tableName: 'table1', columnNames: ['id1', 'id2'], @@ -104,7 +97,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.FOREIGN_KEY, + type: ConstraintType.FOREIGN_KEY, name: 'FK_aed36d04470eba20161aa8b1dc6', tableName: 'table2', columnNames: ['parentId1', 'parentId2'], diff --git a/server/test/sql-tools/foreign-key-constraint-no-index.stub.ts b/server/test/sql-tools/foreign-key-constraint-no-index.stub.ts index 5ad0aa7a6b..508110f687 100644 --- a/server/test/sql-tools/foreign-key-constraint-no-index.stub.ts +++ b/server/test/sql-tools/foreign-key-constraint-no-index.stub.ts @@ -1,11 +1,4 @@ -import { - Column, - DatabaseConstraintType, - DatabaseSchema, - ForeignKeyConstraint, - PrimaryColumn, - Table, -} from 'src/sql-tools'; +import { Column, ConstraintType, DatabaseSchema, ForeignKeyConstraint, PrimaryColumn, Table } from 'src/sql-tools'; @Table() export class Table1 { @@ -22,7 +15,7 @@ export class Table2 { export const description = 'should create a foreign key constraint to the target table without an index'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -46,7 +39,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_b249cc64cf63b8a22557cdc8537', tableName: 'table1', columnNames: ['id'], @@ -72,7 +65,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.FOREIGN_KEY, + type: ConstraintType.FOREIGN_KEY, name: 'FK_3fcca5cc563abf256fc346e3ff4', tableName: 'table2', columnNames: ['parentId'], diff --git a/server/test/sql-tools/foreign-key-constraint-no-primary.stub.ts b/server/test/sql-tools/foreign-key-constraint-no-primary.stub.ts index 645b0e76f2..dbcefa1d39 100644 --- a/server/test/sql-tools/foreign-key-constraint-no-primary.stub.ts +++ b/server/test/sql-tools/foreign-key-constraint-no-primary.stub.ts @@ -1,4 +1,4 @@ -import { Column, DatabaseConstraintType, DatabaseSchema, ForeignKeyConstraint, Table } from 'src/sql-tools'; +import { Column, ConstraintType, DatabaseSchema, ForeignKeyConstraint, Table } from 'src/sql-tools'; @Table() export class Table1 { @@ -19,7 +19,7 @@ export class Table2 { export const description = 'should create a foreign key constraint to the target table without a primary key'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -69,7 +69,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.FOREIGN_KEY, + type: ConstraintType.FOREIGN_KEY, name: 'FK_7d9c784c98d12365d198d52e4e6', tableName: 'table2', columnNames: ['bar'], diff --git a/server/test/sql-tools/foreign-key-constraint.stub.ts b/server/test/sql-tools/foreign-key-constraint.stub.ts index c8117bd96d..81f6292782 100644 --- a/server/test/sql-tools/foreign-key-constraint.stub.ts +++ b/server/test/sql-tools/foreign-key-constraint.stub.ts @@ -1,11 +1,4 @@ -import { - Column, - DatabaseConstraintType, - DatabaseSchema, - ForeignKeyConstraint, - PrimaryColumn, - Table, -} from 'src/sql-tools'; +import { Column, ConstraintType, DatabaseSchema, ForeignKeyConstraint, PrimaryColumn, Table } from 'src/sql-tools'; @Table() export class Table1 { @@ -22,7 +15,7 @@ export class Table2 { export const description = 'should create a foreign key constraint to the target table'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -46,7 +39,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_b249cc64cf63b8a22557cdc8537', tableName: 'table1', columnNames: ['id'], @@ -80,7 +73,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.FOREIGN_KEY, + type: ConstraintType.FOREIGN_KEY, name: 'FK_3fcca5cc563abf256fc346e3ff4', tableName: 'table2', columnNames: ['parentId'], diff --git a/server/test/sql-tools/foreign-key-inferred-type.stub.ts b/server/test/sql-tools/foreign-key-inferred-type.stub.ts index 0b66a1acd4..626bb2a752 100644 --- a/server/test/sql-tools/foreign-key-inferred-type.stub.ts +++ b/server/test/sql-tools/foreign-key-inferred-type.stub.ts @@ -1,4 +1,4 @@ -import { DatabaseConstraintType, DatabaseSchema, ForeignKeyColumn, PrimaryColumn, Table } from 'src/sql-tools'; +import { ConstraintType, DatabaseSchema, ForeignKeyColumn, PrimaryColumn, Table } from 'src/sql-tools'; @Table() export class Table1 { @@ -14,7 +14,7 @@ export class Table2 { export const description = 'should infer the column type from the reference column'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -38,7 +38,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_b249cc64cf63b8a22557cdc8537', tableName: 'table1', columnNames: ['id'], @@ -72,7 +72,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.FOREIGN_KEY, + type: ConstraintType.FOREIGN_KEY, name: 'FK_3fcca5cc563abf256fc346e3ff4', tableName: 'table2', columnNames: ['parentId'], diff --git a/server/test/sql-tools/foreign-key-with-unique-constraint.stub.ts b/server/test/sql-tools/foreign-key-with-unique-constraint.stub.ts index 109a3dfc85..ad4142a503 100644 --- a/server/test/sql-tools/foreign-key-with-unique-constraint.stub.ts +++ b/server/test/sql-tools/foreign-key-with-unique-constraint.stub.ts @@ -1,4 +1,4 @@ -import { DatabaseConstraintType, DatabaseSchema, ForeignKeyColumn, PrimaryColumn, Table } from 'src/sql-tools'; +import { ConstraintType, DatabaseSchema, ForeignKeyColumn, PrimaryColumn, Table } from 'src/sql-tools'; @Table() export class Table1 { @@ -14,7 +14,7 @@ export class Table2 { export const description = 'should create a foreign key constraint with a unique constraint'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -38,7 +38,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_b249cc64cf63b8a22557cdc8537', tableName: 'table1', columnNames: ['id'], @@ -72,7 +72,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.FOREIGN_KEY, + type: ConstraintType.FOREIGN_KEY, name: 'FK_3fcca5cc563abf256fc346e3ff4', tableName: 'table2', columnNames: ['parentId'], @@ -81,7 +81,7 @@ export const schema: DatabaseSchema = { synchronize: true, }, { - type: DatabaseConstraintType.UNIQUE, + type: ConstraintType.UNIQUE, name: 'REL_3fcca5cc563abf256fc346e3ff', tableName: 'table2', columnNames: ['parentId'], diff --git a/server/test/sql-tools/index-name-default.stub.ts b/server/test/sql-tools/index-name-default.stub.ts index 06ccd7e173..b309e93d66 100644 --- a/server/test/sql-tools/index-name-default.stub.ts +++ b/server/test/sql-tools/index-name-default.stub.ts @@ -9,7 +9,7 @@ export class Table1 { export const description = 'should create an index with a default name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/index-name-override.stub.ts b/server/test/sql-tools/index-name-override.stub.ts index afdc26dcc0..daa4fa208b 100644 --- a/server/test/sql-tools/index-name-override.stub.ts +++ b/server/test/sql-tools/index-name-override.stub.ts @@ -9,7 +9,7 @@ export class Table1 { export const description = 'should create an index with a specific name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/index-with-expression.ts b/server/test/sql-tools/index-with-expression.ts index dec31ebe02..57bce5731b 100644 --- a/server/test/sql-tools/index-with-expression.ts +++ b/server/test/sql-tools/index-with-expression.ts @@ -9,7 +9,7 @@ export class Table1 { export const description = 'should create an index based off of an expression'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/index-with-where.stub.ts b/server/test/sql-tools/index-with-where.stub.ts index ce4236e490..f88702010a 100644 --- a/server/test/sql-tools/index-with-where.stub.ts +++ b/server/test/sql-tools/index-with-where.stub.ts @@ -9,7 +9,7 @@ export class Table1 { export const description = 'should create an index with a where clause'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/primary-key-constraint-name-default.stub.ts b/server/test/sql-tools/primary-key-constraint-name-default.stub.ts index 22a515735a..3047605b8e 100644 --- a/server/test/sql-tools/primary-key-constraint-name-default.stub.ts +++ b/server/test/sql-tools/primary-key-constraint-name-default.stub.ts @@ -1,4 +1,4 @@ -import { DatabaseConstraintType, DatabaseSchema, PrimaryColumn, Table } from 'src/sql-tools'; +import { ConstraintType, DatabaseSchema, PrimaryColumn, Table } from 'src/sql-tools'; @Table() export class Table1 { @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should add a primary key constraint to the table with a default name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -32,7 +32,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_b249cc64cf63b8a22557cdc8537', tableName: 'table1', columnNames: ['id'], diff --git a/server/test/sql-tools/primary-key-constraint-name-override.stub.ts b/server/test/sql-tools/primary-key-constraint-name-override.stub.ts index e1e0daa82e..ffc014c91f 100644 --- a/server/test/sql-tools/primary-key-constraint-name-override.stub.ts +++ b/server/test/sql-tools/primary-key-constraint-name-override.stub.ts @@ -1,4 +1,4 @@ -import { DatabaseConstraintType, DatabaseSchema, PrimaryColumn, Table } from 'src/sql-tools'; +import { ConstraintType, DatabaseSchema, PrimaryColumn, Table } from 'src/sql-tools'; @Table({ primaryConstraintName: 'PK_test' }) export class Table1 { @@ -8,7 +8,7 @@ export class Table1 { export const description = 'should add a primary key constraint to the table with a specific name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -32,7 +32,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.PRIMARY_KEY, + type: ConstraintType.PRIMARY_KEY, name: 'PK_test', tableName: 'table1', columnNames: ['id'], diff --git a/server/test/sql-tools/table-name-default.stub.ts b/server/test/sql-tools/table-name-default.stub.ts index 6ecc042a58..0cb84f518d 100644 --- a/server/test/sql-tools/table-name-default.stub.ts +++ b/server/test/sql-tools/table-name-default.stub.ts @@ -5,7 +5,7 @@ export class Table1 {} export const description = 'should register a table with a default name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/table-name-override.stub.ts b/server/test/sql-tools/table-name-override.stub.ts index 929a4c4b28..1d66e0b7fb 100644 --- a/server/test/sql-tools/table-name-override.stub.ts +++ b/server/test/sql-tools/table-name-override.stub.ts @@ -5,7 +5,7 @@ export class Table1 {} export const description = 'should register a table with a specific name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/table-name-string-option.stub.ts b/server/test/sql-tools/table-name-string-option.stub.ts index 33e582fb6b..ae1dfe1b2d 100644 --- a/server/test/sql-tools/table-name-string-option.stub.ts +++ b/server/test/sql-tools/table-name-string-option.stub.ts @@ -5,7 +5,7 @@ export class Table1 {} export const description = 'should register a table with a specific name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/trigger-after-delete.stub.ts b/server/test/sql-tools/trigger-after-delete.stub.ts index 1eb063c968..56c5d5f267 100644 --- a/server/test/sql-tools/trigger-after-delete.stub.ts +++ b/server/test/sql-tools/trigger-after-delete.stub.ts @@ -16,7 +16,7 @@ export class Table1 {} export const description = 'should create a trigger'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [expect.any(Object)], enums: [], diff --git a/server/test/sql-tools/trigger-before-update.stub.ts b/server/test/sql-tools/trigger-before-update.stub.ts index a88675a9ef..cbc3454a0b 100644 --- a/server/test/sql-tools/trigger-before-update.stub.ts +++ b/server/test/sql-tools/trigger-before-update.stub.ts @@ -16,7 +16,7 @@ export class Table1 {} export const description = 'should create a trigger '; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [expect.any(Object)], enums: [], diff --git a/server/test/sql-tools/trigger-name-default.stub.ts b/server/test/sql-tools/trigger-name-default.stub.ts index a9951aef18..a6cf8d237a 100644 --- a/server/test/sql-tools/trigger-name-default.stub.ts +++ b/server/test/sql-tools/trigger-name-default.stub.ts @@ -11,7 +11,7 @@ export class Table1 {} export const description = 'should register a trigger with a default name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/trigger-name-override.stub.ts b/server/test/sql-tools/trigger-name-override.stub.ts index 3fba0e12ab..aba0addb71 100644 --- a/server/test/sql-tools/trigger-name-override.stub.ts +++ b/server/test/sql-tools/trigger-name-override.stub.ts @@ -12,7 +12,7 @@ export class Table1 {} export const description = 'should a trigger with a specific name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], diff --git a/server/test/sql-tools/unique-constraint-name-default.stub.ts b/server/test/sql-tools/unique-constraint-name-default.stub.ts index a3b9c512c5..bf302412c1 100644 --- a/server/test/sql-tools/unique-constraint-name-default.stub.ts +++ b/server/test/sql-tools/unique-constraint-name-default.stub.ts @@ -1,4 +1,4 @@ -import { Column, DatabaseConstraintType, DatabaseSchema, Table, Unique } from 'src/sql-tools'; +import { Column, ConstraintType, DatabaseSchema, Table, Unique } from 'src/sql-tools'; @Table() @Unique({ columns: ['id'] }) @@ -9,7 +9,7 @@ export class Table1 { export const description = 'should add a unique constraint to the table with a default name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -33,7 +33,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.UNIQUE, + type: ConstraintType.UNIQUE, name: 'UQ_b249cc64cf63b8a22557cdc8537', tableName: 'table1', columnNames: ['id'], diff --git a/server/test/sql-tools/unique-constraint-name-override.stub.ts b/server/test/sql-tools/unique-constraint-name-override.stub.ts index 4def45043f..189c0c38d6 100644 --- a/server/test/sql-tools/unique-constraint-name-override.stub.ts +++ b/server/test/sql-tools/unique-constraint-name-override.stub.ts @@ -1,4 +1,4 @@ -import { Column, DatabaseConstraintType, DatabaseSchema, Table, Unique } from 'src/sql-tools'; +import { Column, ConstraintType, DatabaseSchema, Table, Unique } from 'src/sql-tools'; @Table() @Unique({ name: 'UQ_test', columns: ['id'] }) @@ -9,7 +9,7 @@ export class Table1 { export const description = 'should add a unique constraint to the table with a specific name'; export const schema: DatabaseSchema = { - name: 'postgres', + databaseName: 'postgres', schemaName: 'public', functions: [], enums: [], @@ -33,7 +33,7 @@ export const schema: DatabaseSchema = { triggers: [], constraints: [ { - type: DatabaseConstraintType.UNIQUE, + type: ConstraintType.UNIQUE, name: 'UQ_test', tableName: 'table1', columnNames: ['id'], diff --git a/server/test/vitest.config.mjs b/server/test/vitest.config.mjs index a22a6751c3..6d9ee3a564 100644 --- a/server/test/vitest.config.mjs +++ b/server/test/vitest.config.mjs @@ -18,7 +18,6 @@ export default defineConfig({ 'src/services/api.service.ts', 'src/services/microservices.service.ts', 'src/services/index.ts', - 'src/sql-tools/from-database/index.ts', ], }, server: {