diff --git a/packages/dbml-core/__tests__/examples/parser/mssql-parse/input/with_check_add_constraint.in.sql b/packages/dbml-core/__tests__/examples/parser/mssql-parse/input/with_check_add_constraint.in.sql new file mode 100644 index 000000000..285a7cbfc --- /dev/null +++ b/packages/dbml-core/__tests__/examples/parser/mssql-parse/input/with_check_add_constraint.in.sql @@ -0,0 +1,23 @@ +CREATE TABLE [users] ( + [id] int PRIMARY KEY, + [name] varchar(255) +) +GO + +CREATE TABLE [orders] ( + [id] int PRIMARY KEY, + [user_id] int +) +GO + +CREATE TABLE [audit_logs] ( + [id] int PRIMARY KEY, + [order_id] int +) +GO + +ALTER TABLE [orders] WITH CHECK ADD CONSTRAINT [FK_orders_users] FOREIGN KEY ([user_id]) REFERENCES [users] ([id]) ON DELETE CASCADE ON UPDATE SET NULL +GO + +ALTER TABLE [audit_logs] WITH NOCHECK ADD CONSTRAINT [FK_audit_logs_orders] FOREIGN KEY ([order_id]) REFERENCES [orders] ([id]) +GO diff --git a/packages/dbml-core/__tests__/examples/parser/mssql-parse/output/with_check_add_constraint.out.json b/packages/dbml-core/__tests__/examples/parser/mssql-parse/output/with_check_add_constraint.out.json new file mode 100644 index 000000000..1b42e2099 --- /dev/null +++ b/packages/dbml-core/__tests__/examples/parser/mssql-parse/output/with_check_add_constraint.out.json @@ -0,0 +1,100 @@ +{ + "tables": [ + { + "name": "users", + "fields": [ + { + "name": "id", + "type": { + "type_name": "int" + }, + "pk": true + }, + { + "name": "name", + "type": { + "type_name": "varchar(255)" + } + } + ] + }, + { + "name": "orders", + "fields": [ + { + "name": "id", + "type": { + "type_name": "int" + }, + "pk": true + }, + { + "name": "user_id", + "type": { + "type_name": "int" + } + } + ] + }, + { + "name": "audit_logs", + "fields": [ + { + "name": "id", + "type": { + "type_name": "int" + }, + "pk": true + }, + { + "name": "order_id", + "type": { + "type_name": "int" + } + } + ] + } + ], + "refs": [ + { + "name": "FK_orders_users", + "endpoints": [ + { + "tableName": "orders", + "fieldNames": [ + "user_id" + ], + "relation": "*" + }, + { + "tableName": "users", + "fieldNames": [ + "id" + ], + "relation": "1" + } + ], + "onDelete": "CASCADE", + "onUpdate": "SET NULL" + }, + { + "name": "FK_audit_logs_orders", + "endpoints": [ + { + "tableName": "audit_logs", + "fieldNames": [ + "order_id" + ], + "relation": "*" + }, + { + "tableName": "orders", + "fieldNames": [ + "id" + ], + "relation": "1" + } + ] + } + ] +} diff --git a/packages/dbml-core/src/parse/ANTLR/ASTGeneration/mssql/MssqlASTGen.js b/packages/dbml-core/src/parse/ANTLR/ASTGeneration/mssql/MssqlASTGen.js index d4c383030..e09e65f12 100644 --- a/packages/dbml-core/src/parse/ANTLR/ASTGeneration/mssql/MssqlASTGen.js +++ b/packages/dbml-core/src/parse/ANTLR/ASTGeneration/mssql/MssqlASTGen.js @@ -1045,6 +1045,45 @@ export default class MssqlASTGen extends TSqlParserVisitor { const table = this.data.tables.find((t) => t.name === tableName && t.schemaName === schemaName); if (!table) return; // ALTER TABLE should appear after CREATE TABLE, so skip if table is not created yet + // Handle WITH CHECK/NOCHECK ADD CONSTRAINT FK + if (ctx.WITH() && (ctx.CHECK() || ctx.NOCHECK()) && ctx.FOREIGN()) { + const constraintName = ctx.constraint ? ctx.constraint.accept(this) : undefined; + const localColumns = ctx.fk.accept(this); + + // table_name()[1] is the referenced table (table_name()[0] is the table being altered) + const refTableNames = ctx.table_name()[1].accept(this); + const { schemaName: refSchemaName, tableName: refTableName } = getSchemaAndTableName(refTableNames); + + // pk is optional - if not specified, assume same column names as fk + const refColumns = ctx.pk ? ctx.pk.accept(this) : localColumns; + + const onDelete = ctx.on_delete().length > 0 ? ctx.on_delete()[0].accept(this) : null; + const onUpdate = ctx.on_update().length > 0 ? ctx.on_update()[0].accept(this) : null; + + const ref = { + name: constraintName, + endpoints: [ + { + tableName, + schemaName, + fieldNames: localColumns, + relation: '*', + }, + { + tableName: refTableName, + schemaName: refSchemaName, + fieldNames: refColumns, + relation: '1', + }, + ], + onDelete, + onUpdate, + }; + + this.data.refs.push(ref); + return; + } + const columnDefTableConstraints = ctx.column_def_table_constraints() ? ctx.column_def_table_constraints().accept(this) : []; const { fieldsData, indexes, tableRefs, columnDefaults, checkConstraints,