From 88da70209684aa2ff10907265826b9e9324b7ad2 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Mon, 1 Jun 2026 11:07:45 +0700 Subject: [PATCH 1/9] doc: add comments to indexes nodeReferee --- packages/dbml-parse/src/core/global_modules/indexes/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/dbml-parse/src/core/global_modules/indexes/index.ts b/packages/dbml-parse/src/core/global_modules/indexes/index.ts index 616913db8..f7490694b 100644 --- a/packages/dbml-parse/src/core/global_modules/indexes/index.ts +++ b/packages/dbml-parse/src/core/global_modules/indexes/index.ts @@ -36,17 +36,20 @@ export const indexesModule: GlobalModule = { // Skip variables inside index settings (e.g. [type: btree]) if (isInsideSettingList(node)) return Report.create(PASS_THROUGH); + // Check that node belongs to an indexes element let indexesNode: SyntaxNode | undefined = node; while (indexesNode && !isElementNode(indexesNode, ElementKind.Indexes)) { indexesNode = indexesNode.parent; } if (!indexesNode) return Report.create(PASS_THROUGH); + // Ensure the indexes node belong to a table node const tableNode = indexesNode.parent; if (!tableNode || (!isElementNode(tableNode, ElementKind.Table) && !isElementNode(tableNode, ElementKind.TablePartial))) return Report.create(PASS_THROUGH); const tableSymbol = compiler.nodeSymbol(tableNode).getFiltered(UNHANDLED); - if (!tableSymbol) return new Report(undefined); + if (!tableSymbol) return new Report(undefined); // If no table found, return nothing + // Extract the column this node refer to const varName = isExpressionAVariableNode(node) ? (node.expression.variable?.value ?? '') : ''; const symbol = compiler.lookupMembers(tableSymbol, SymbolKind.Column, varName); if (symbol) { From 0e22743ecb4e8a4d80511ea8923c0f1afbee01b9 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Mon, 1 Jun 2026 11:34:12 +0700 Subject: [PATCH 2/9] fix: do not skip binding of indexes when tupleElements is present --- .../src/core/global_modules/indexes/bind.ts | 13 ++++++++++++- .../dbml-parse/src/core/global_modules/utils.ts | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/dbml-parse/src/core/global_modules/indexes/bind.ts b/packages/dbml-parse/src/core/global_modules/indexes/bind.ts index 25ceebd63..08b56d4f9 100644 --- a/packages/dbml-parse/src/core/global_modules/indexes/bind.ts +++ b/packages/dbml-parse/src/core/global_modules/indexes/bind.ts @@ -68,13 +68,24 @@ export default class IndexesBinder { ]; const bindees = args.flatMap(scanNonListNodeForBinding) .flatMap((bindee) => { - if (bindee.variables.length + bindee.tupleElements.length > 1) { + // Indexes can never be schema-qualified (they reference table columns) + if (bindee.variables.length > 1) { return []; } + // Indexes is a simple column + // e.g + // Indexes { + // id + // } if (bindee.variables.length) { return bindee.variables[0]; } + // Indexes is a tuple + // e.g + // Indexes { + // (id, name, age) + // } return bindee.tupleElements; }); diff --git a/packages/dbml-parse/src/core/global_modules/utils.ts b/packages/dbml-parse/src/core/global_modules/utils.ts index c75d41e59..94d19d100 100644 --- a/packages/dbml-parse/src/core/global_modules/utils.ts +++ b/packages/dbml-parse/src/core/global_modules/utils.ts @@ -48,7 +48,7 @@ export function getNodeMemberSymbols (compiler: Compiler, node: SyntaxNode): Rep ); } -// Scan for variable node and member access expression in the node except ListExpressionNo +// Scan for variable node and member access expression in the node except ListExpressionNode export function scanNonListNodeForBinding (node?: SyntaxNode): { variables: (PrimaryExpressionNode & { expression: VariableNode })[]; tupleElements: (PrimaryExpressionNode & { expression: VariableNode })[]; }[] { if (!node) return []; From 896812e4bb03317dd3d5afc208cbe3373fba8926 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Mon, 1 Jun 2026 11:38:27 +0700 Subject: [PATCH 3/9] test: add testcase for non-existing column in index --- .../__tests__/examples/binder/binder.test.ts | 19 ++++ .../examples/interpreter/interpreter.test.ts | 19 ++++ .../input/nonexisting_column_in_index.in.dbml | 10 ++ .../nonexisting_column_in_index.out.json | 99 +++++++++++++++++++ 4 files changed, 147 insertions(+) create mode 100644 packages/dbml-parse/__tests__/snapshots/binder/input/nonexisting_column_in_index.in.dbml create mode 100644 packages/dbml-parse/__tests__/snapshots/binder/output/nonexisting_column_in_index.out.json diff --git a/packages/dbml-parse/__tests__/examples/binder/binder.test.ts b/packages/dbml-parse/__tests__/examples/binder/binder.test.ts index 2f97fd0fa..37aa2b9bd 100644 --- a/packages/dbml-parse/__tests__/examples/binder/binder.test.ts +++ b/packages/dbml-parse/__tests__/examples/binder/binder.test.ts @@ -272,6 +272,25 @@ describe('[example] binder', () => { expect(errors[0].diagnostic).toBe("No column named 'nonexistent_column' inside Table 'users'"); }); + test('should detect unknown columns in composite indexes', () => { + const source = ` + Table posts { + id int [pk] + title varchar [not null] + content text + user_id int + created_at timestamp [default: \`now()\`] + indexes { + (id, non_existent_column) + } + } + `; + const errors = analyze(source).getErrors(); + + expect(errors).toHaveLength(1); + expect(errors[0].diagnostic).toBe("No column named 'non_existent_column' inside Table 'posts'"); + }); + test('should bind composite indexes with settings', () => { const source = ` Table users { diff --git a/packages/dbml-parse/__tests__/examples/interpreter/interpreter.test.ts b/packages/dbml-parse/__tests__/examples/interpreter/interpreter.test.ts index 2227d6556..8b65898e9 100644 --- a/packages/dbml-parse/__tests__/examples/interpreter/interpreter.test.ts +++ b/packages/dbml-parse/__tests__/examples/interpreter/interpreter.test.ts @@ -186,6 +186,25 @@ describe('[example] interpreter', () => { expect(index.columns).toHaveLength(2); }); + + test('should report error for non-existent column in composite index', () => { + const source = ` + Table posts { + id int [pk] + title varchar [not null] + content text + user_id int + created_at timestamp [default: \`now()\`] + indexes { + (id, non_existent_column) + } + } + `; + const errors = analyze(source).getErrors(); + + expect(errors).toHaveLength(1); + expect(errors[0].diagnostic).toBe("No column named 'non_existent_column' inside Table 'posts'"); + }); }); describe('ref interpretation', () => { diff --git a/packages/dbml-parse/__tests__/snapshots/binder/input/nonexisting_column_in_index.in.dbml b/packages/dbml-parse/__tests__/snapshots/binder/input/nonexisting_column_in_index.in.dbml new file mode 100644 index 000000000..78eb36ded --- /dev/null +++ b/packages/dbml-parse/__tests__/snapshots/binder/input/nonexisting_column_in_index.in.dbml @@ -0,0 +1,10 @@ +Table posts { + id int [pk] + title varchar [not null] + content text + user_id int + created_at timestamp [default: `now()`] + indexes { + (id, non_existent_column) + } +} diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/nonexisting_column_in_index.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/nonexisting_column_in_index.out.json new file mode 100644 index 000000000..8d4286fd8 --- /dev/null +++ b/packages/dbml-parse/__tests__/snapshots/binder/output/nonexisting_column_in_index.out.json @@ -0,0 +1,99 @@ +{ + "errors": [ + { + "code": "BINDING_ERROR", + "diagnostic": "No column named 'non_existent_column' inside Table 'posts'", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@non_existent_column@[L7:C9, L7:C28]", + "snippet": "non_existent_column" + } + } + } + ], + "nodeReferees": [ + { + "context": { + "id": "node@@id@[L7:C5, L7:C7]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L7:C5, L7:C7]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L7:C5, L7:C7]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "id" + } + }, + "fullEnd": 145, + "fullStart": 143 + } + }, + "fullEnd": 145, + "fullStart": 143, + "referee": { + "context": { + "id": "symbol@Column@id@[L1:C2, L1:C13]", + "snippet": "id int [pk]" + } + } + } + ], + "program": { + "publicSchema": [ + { + "context": { + "id": "symbol@Table@posts@[L0:C0, L9:C1]", + "snippet": "Table post...umn)\n }\n}" + }, + "declaration": { + "id": "node@@posts@[L0:C0, L9:C1]", + "snippet": "Table post...umn)\n }\n}" + }, + "members": [ + { + "context": { + "id": "symbol@Column@content@[L3:C2, L3:C14]", + "snippet": "content text" + } + }, + { + "context": { + "id": "symbol@Column@created_at@[L5:C2, L5:C41]", + "snippet": "created_at...: `now()`]" + } + }, + { + "context": { + "id": "symbol@Column@id@[L1:C2, L1:C13]", + "snippet": "id int [pk]" + } + }, + { + "context": { + "id": "symbol@Column@title@[L2:C2, L2:C26]", + "snippet": "title varc...[not null]" + } + }, + { + "context": { + "id": "symbol@Column@user_id@[L4:C2, L4:C13]", + "snippet": "user_id int" + } + } + ] + } + ] + } +} \ No newline at end of file From 05cb81bc08c4eada1998376898c346ed4db70ba1 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Mon, 1 Jun 2026 11:46:18 +0700 Subject: [PATCH 4/9] fix: handle indexes binding in tablepartial --- .../__tests__/examples/binder/binder.test.ts | 19 ++++ .../examples/interpreter/interpreter.test.ts | 19 ++++ ...ting_column_in_index_table_partial.in.dbml | 10 ++ ...ing_column_in_index_table_partial.out.json | 99 +++++++++++++++++++ .../src/core/global_modules/indexes/bind.ts | 11 ++- 5 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 packages/dbml-parse/__tests__/snapshots/binder/input/nonexisting_column_in_index_table_partial.in.dbml create mode 100644 packages/dbml-parse/__tests__/snapshots/binder/output/nonexisting_column_in_index_table_partial.out.json diff --git a/packages/dbml-parse/__tests__/examples/binder/binder.test.ts b/packages/dbml-parse/__tests__/examples/binder/binder.test.ts index 37aa2b9bd..fe0506d01 100644 --- a/packages/dbml-parse/__tests__/examples/binder/binder.test.ts +++ b/packages/dbml-parse/__tests__/examples/binder/binder.test.ts @@ -934,6 +934,25 @@ describe('[example] binder', () => { expect(errors.length).toBe(0); }); + test('should detect unknown columns in composite indexes of TablePartial', () => { + const source = ` + TablePartial posts_partial { + id int [pk] + title varchar [not null] + content text + user_id int + created_at timestamp [default: \`now()\`] + indexes { + (id, non_existent_column) + } + } + `; + const errors = analyze(source).getErrors(); + + expect(errors).toHaveLength(1); + expect(errors[0].diagnostic).toBe("No column named 'non_existent_column' inside TablePartial 'posts_partial'"); + }); + test('should bind refs in table partial columns when injected', () => { const source = ` TablePartial common { diff --git a/packages/dbml-parse/__tests__/examples/interpreter/interpreter.test.ts b/packages/dbml-parse/__tests__/examples/interpreter/interpreter.test.ts index 8b65898e9..5caba6dc6 100644 --- a/packages/dbml-parse/__tests__/examples/interpreter/interpreter.test.ts +++ b/packages/dbml-parse/__tests__/examples/interpreter/interpreter.test.ts @@ -394,6 +394,25 @@ describe('[example] interpreter', () => { expect(db.tablePartials[0].fields[0].name).toBe('created_at'); expect(db.tablePartials[0].fields[1].name).toBe('updated_at'); }); + + test('should report error for non-existent column in composite index of TablePartial', () => { + const source = ` + TablePartial posts_partial { + id int [pk] + title varchar [not null] + content text + user_id int + created_at timestamp [default: \`now()\`] + indexes { + (id, non_existent_column) + } + } + `; + const errors = analyze(source).getErrors(); + + expect(errors).toHaveLength(1); + expect(errors[0].diagnostic).toBe("No column named 'non_existent_column' inside TablePartial 'posts_partial'"); + }); }); describe('complex scenarios', () => { diff --git a/packages/dbml-parse/__tests__/snapshots/binder/input/nonexisting_column_in_index_table_partial.in.dbml b/packages/dbml-parse/__tests__/snapshots/binder/input/nonexisting_column_in_index_table_partial.in.dbml new file mode 100644 index 000000000..63e60e209 --- /dev/null +++ b/packages/dbml-parse/__tests__/snapshots/binder/input/nonexisting_column_in_index_table_partial.in.dbml @@ -0,0 +1,10 @@ +TablePartial posts_partial { + id int [pk] + title varchar [not null] + content text + user_id int + created_at timestamp [default: `now()`] + indexes { + (id, non_existent_column) + } +} diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/nonexisting_column_in_index_table_partial.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/nonexisting_column_in_index_table_partial.out.json new file mode 100644 index 000000000..4c2d7bf1a --- /dev/null +++ b/packages/dbml-parse/__tests__/snapshots/binder/output/nonexisting_column_in_index_table_partial.out.json @@ -0,0 +1,99 @@ +{ + "errors": [ + { + "code": "BINDING_ERROR", + "diagnostic": "No column named 'non_existent_column' inside TablePartial 'posts_partial'", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@non_existent_column@[L7:C9, L7:C28]", + "snippet": "non_existent_column" + } + } + } + ], + "nodeReferees": [ + { + "context": { + "id": "node@@id@[L7:C5, L7:C7]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L7:C5, L7:C7]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L7:C5, L7:C7]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "id" + } + }, + "fullEnd": 160, + "fullStart": 158 + } + }, + "fullEnd": 160, + "fullStart": 158, + "referee": { + "context": { + "id": "symbol@Column@id@[L1:C2, L1:C13]", + "snippet": "id int [pk]" + } + } + } + ], + "program": { + "publicSchema": [ + { + "context": { + "id": "symbol@TablePartial@posts_partial@[L0:C0, L9:C1]", + "snippet": "TableParti...umn)\n }\n}" + }, + "declaration": { + "id": "node@@posts_partial@[L0:C0, L9:C1]", + "snippet": "TableParti...umn)\n }\n}" + }, + "members": [ + { + "context": { + "id": "symbol@Column@content@[L3:C2, L3:C14]", + "snippet": "content text" + } + }, + { + "context": { + "id": "symbol@Column@created_at@[L5:C2, L5:C41]", + "snippet": "created_at...: `now()`]" + } + }, + { + "context": { + "id": "symbol@Column@id@[L1:C2, L1:C13]", + "snippet": "id int [pk]" + } + }, + { + "context": { + "id": "symbol@Column@title@[L2:C2, L2:C26]", + "snippet": "title varc...[not null]" + } + }, + { + "context": { + "id": "symbol@Column@user_id@[L4:C2, L4:C13]", + "snippet": "user_id int" + } + } + ] + } + ] + } +} \ No newline at end of file diff --git a/packages/dbml-parse/src/core/global_modules/indexes/bind.ts b/packages/dbml-parse/src/core/global_modules/indexes/bind.ts index 08b56d4f9..35a7da2f9 100644 --- a/packages/dbml-parse/src/core/global_modules/indexes/bind.ts +++ b/packages/dbml-parse/src/core/global_modules/indexes/bind.ts @@ -21,7 +21,9 @@ export default class IndexesBinder { } bind (): CompileError[] { - if (!(this.declarationNode.parent instanceof ElementDeclarationNode) || !this.declarationNode.parent.isKind(ElementKind.Table)) { + if (!(this.declarationNode.parent instanceof ElementDeclarationNode) + || (!this.declarationNode.parent.isKind(ElementKind.Table) + && !this.declarationNode.parent.isKind(ElementKind.TablePartial))) { return []; } @@ -32,6 +34,11 @@ export default class IndexesBinder { return this.bindBody(this.declarationNode.body); } + private get ownerContainerKind (): string { + const parent = this.declarationNode.parent as ElementDeclarationNode; + return parent.isKind(ElementKind.TablePartial) ? 'TablePartial' : 'Table'; + } + private bindBody (body?: FunctionApplicationNode | BlockExpressionNode): CompileError[] { if (!body) { return []; @@ -94,7 +101,7 @@ export default class IndexesBinder { if (columnName === undefined) return []; const column = this.compiler.nodeReferee(bindee); if (!column.getValue() || column.hasValue(UNHANDLED)) { - return new CompileError(CompileErrorCode.BINDING_ERROR, `No column named '${columnName}' inside Table '${ownerTableName}'`, bindee); + return new CompileError(CompileErrorCode.BINDING_ERROR, `No column named '${columnName}' inside ${this.ownerContainerKind} '${ownerTableName}'`, bindee); } return []; From ed6c947ebf3515bcb621ae4a56778c8ac0bea7d8 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Mon, 1 Jun 2026 11:56:26 +0700 Subject: [PATCH 5/9] test: update tests --- .../output/index_table_partial.out.json | 548 +--------- .../interpreter/output/table_partial.out.json | 995 +----------------- 2 files changed, 22 insertions(+), 1521 deletions(-) diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/index_table_partial.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/index_table_partial.out.json index 000d1e4b1..45c9221b6 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/index_table_partial.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/index_table_partial.out.json @@ -1,542 +1,16 @@ { - "database": { - "tablePartials": [ - { - "fields": [ - { - "name": "full_name", - "token": { - "end": { - "column": 20, - "line": 2, - "offset": 47 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 2, - "offset": 30 - } - }, - "type": { - "type_name": "varchar" - } - }, - { - "name": "email", - "token": { - "end": { - "column": 25, - "line": 3, - "offset": 72 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 3, - "offset": 50 - } - }, - "type": { - "type_name": "varchar" - }, - "unique": true - }, - { - "name": "gender", - "token": { - "end": { - "column": 17, - "line": 4, - "offset": 89 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 4, - "offset": 75 - } - }, - "type": { - "type_name": "varchar" - } - }, - { - "name": "date_of_birth", - "token": { - "end": { - "column": 24, - "line": 5, - "offset": 113 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 5, - "offset": 92 - } - }, - "type": { - "type_name": "varchar" - } - }, - { - "name": "created_at", - "token": { - "end": { - "column": 21, - "line": 6, - "offset": 134 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 6, - "offset": 116 - } - }, - "type": { - "type_name": "varchar" - } - }, - { - "name": "country_code", - "token": { - "end": { - "column": 19, - "line": 7, - "offset": 153 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 7, - "offset": 137 - } - }, - "type": { - "type_name": "int" - } - }, - { - "name": "active", - "not_null": true, - "token": { - "end": { - "column": 28, - "line": 8, - "offset": 182 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 8, - "offset": 157 - } - }, - "type": { - "type_name": "boolean" - } - } - ], - "indexes": [ - { - "columns": [ - { - "token": { - "end": { - "column": 8, - "line": 11, - "offset": 203 - }, - "filepath": "/main.dbml", - "start": { - "column": 6, - "line": 11, - "offset": 201 - } - }, - "type": "column", - "value": "id" - } - ], - "note": { - "token": { - "end": { - "column": 37, - "line": 11, - "offset": 232 - }, - "filepath": "/main.dbml", - "start": { - "column": 19, - "line": 11, - "offset": 214 - } - }, - "value": "index note" - }, - "token": { - "end": { - "column": 38, - "line": 11, - "offset": 233 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 11, - "offset": 200 - } - }, - "unique": true - }, - { - "columns": [ - { - "token": { - "end": { - "column": 14, - "line": 12, - "offset": 247 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 12, - "offset": 238 - } - }, - "type": "column", - "value": "full_name" - } - ], - "name": "User Name", - "token": { - "end": { - "column": 34, - "line": 12, - "offset": 267 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 12, - "offset": 238 - } - } - }, - { - "columns": [ - { - "token": { - "end": { - "column": 11, - "line": 13, - "offset": 278 - }, - "filepath": "/main.dbml", - "start": { - "column": 6, - "line": 13, - "offset": 273 - } - }, - "type": "column", - "value": "email" - }, - { - "token": { - "end": { - "column": 22, - "line": 13, - "offset": 289 - }, - "filepath": "/main.dbml", - "start": { - "column": 12, - "line": 13, - "offset": 279 - } - }, - "type": "column", - "value": "created_at" - } - ], - "token": { - "end": { - "column": 36, - "line": 13, - "offset": 303 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 13, - "offset": 272 - } - }, - "type": "hash" - }, - { - "columns": [ - { - "token": { - "end": { - "column": 12, - "line": 14, - "offset": 315 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 14, - "offset": 308 - } - }, - "type": "expression", - "value": "now()" - } - ], - "token": { - "end": { - "column": 12, - "line": 14, - "offset": 315 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 14, - "offset": 308 - } - } - }, - { - "columns": [ - { - "token": { - "end": { - "column": 12, - "line": 15, - "offset": 327 - }, - "filepath": "/main.dbml", - "start": { - "column": 6, - "line": 15, - "offset": 321 - } - }, - "type": "column", - "value": "active" - }, - { - "token": { - "end": { - "column": 32, - "line": 15, - "offset": 347 - }, - "filepath": "/main.dbml", - "start": { - "column": 14, - "line": 15, - "offset": 329 - } - }, - "type": "expression", - "value": "lower(full_name)" - } - ], - "token": { - "end": { - "column": 33, - "line": 15, - "offset": 348 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 15, - "offset": 320 - } - } - }, - { - "columns": [ - { - "token": { - "end": { - "column": 17, - "line": 16, - "offset": 365 - }, - "filepath": "/main.dbml", - "start": { - "column": 6, - "line": 16, - "offset": 354 - } - }, - "type": "expression", - "value": "getdate()" - }, - { - "token": { - "end": { - "column": 34, - "line": 16, - "offset": 382 - }, - "filepath": "/main.dbml", - "start": { - "column": 19, - "line": 16, - "offset": 367 - } - }, - "type": "expression", - "value": "upper(gender)" - } - ], - "token": { - "end": { - "column": 35, - "line": 16, - "offset": 383 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 16, - "offset": 353 - } - } - }, - { - "columns": [ - { - "token": { - "end": { - "column": 29, - "line": 17, - "offset": 412 - }, - "filepath": "/main.dbml", - "start": { - "column": 6, - "line": 17, - "offset": 389 - } - }, - "type": "expression", - "value": "reverse(country_code)" - } - ], - "token": { - "end": { - "column": 30, - "line": 17, - "offset": 413 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 17, - "offset": 388 - } - } - } - ], - "name": "user_partial", - "token": { - "end": { - "column": 2, - "line": 19, - "offset": 419 - }, - "filepath": "/main.dbml", - "start": { - "column": 1, - "line": 1, - "offset": 0 - } - } - } - ], - "tables": [ - { - "fields": [ - { - "name": "id", - "pk": true, - "token": { - "end": { - "column": 14, - "line": 22, - "offset": 448 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 22, - "offset": 437 - } - }, - "type": { - "type_name": "int" - }, - "unique": false - } - ], - "name": "users", - "partials": [ - { - "name": "user_partial", - "order": 1, - "token": { - "end": { - "column": 16, - "line": 23, - "offset": 464 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 23, - "offset": 451 - } - } - } - ], - "token": { - "end": { - "column": 2, - "line": 24, - "offset": 466 - }, - "filepath": "/main.dbml", - "start": { - "column": 1, - "line": 21, - "offset": 421 - } - } - } - ], - "token": { - "end": { - "column": 1, - "line": 25, - "offset": 467 - }, + "errors": [ + { + "code": "BINDING_ERROR", + "diagnostic": "No column named 'id' inside TablePartial 'user_partial'", "filepath": "/main.dbml", - "start": { - "column": 1, - "line": 1, - "offset": 0 + "level": "error", + "node": { + "context": { + "id": "node@@id@[L10:C5, L10:C7]", + "snippet": "id" + } } } - } + ] } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_partial.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_partial.out.json index 947153ea1..aeb60d70b 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_partial.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_partial.out.json @@ -1,989 +1,16 @@ { - "database": { - "refs": [ - { - "endpoints": [ - { - "fieldNames": [ - "email" - ], - "relation": "1", - "tableName": "customer", - "token": { - "end": { - "column": 112, - "line": 54, - "offset": 1100 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 54, - "offset": 991 - } - } - }, - { - "fieldNames": [ - "email" - ], - "relation": "1", - "tableName": "user", - "token": { - "end": { - "column": 36, - "line": 54, - "offset": 1024 - }, - "filepath": "/main.dbml", - "start": { - "column": 19, - "line": 54, - "offset": 1007 - } - } - } - ], - "token": { - "end": { - "column": 36, - "line": 54, - "offset": 1024 - }, - "filepath": "/main.dbml", - "start": { - "column": 19, - "line": 54, - "offset": 1007 - } - } - }, - { - "endpoints": [ - { - "fieldNames": [ - "id" - ], - "relation": "1", - "tableName": "user", - "token": { - "end": { - "column": 16, - "line": 57, - "offset": 1119 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 57, - "offset": 1108 - } - } - }, - { - "fieldNames": [ - "user_id" - ], - "relation": "*", - "tableName": "merchant", - "token": { - "end": { - "column": 39, - "line": 57, - "offset": 1142 - }, - "filepath": "/main.dbml", - "start": { - "column": 19, - "line": 57, - "offset": 1122 - } - } - } - ], - "token": { - "end": { - "column": 39, - "line": 57, - "offset": 1142 - }, - "filepath": "/main.dbml", - "start": { - "column": 1, - "line": 57, - "offset": 1104 - } - } - }, - { - "endpoints": [ - { - "fieldNames": [ - "id" - ], - "relation": "1", - "tableName": "product", - "token": { - "end": { - "column": 19, - "line": 59, - "offset": 1162 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 59, - "offset": 1148 - } - } - }, - { - "fieldNames": [ - "product_id" - ], - "relation": "*", - "tableName": "merchant", - "token": { - "end": { - "column": 45, - "line": 59, - "offset": 1188 - }, - "filepath": "/main.dbml", - "start": { - "column": 22, - "line": 59, - "offset": 1165 - } - } - } - ], - "token": { - "end": { - "column": 45, - "line": 59, - "offset": 1188 - }, - "filepath": "/main.dbml", - "start": { - "column": 1, - "line": 59, - "offset": 1144 - } - } - } - ], - "tablePartials": [ - { - "fields": [ - { - "name": "id", - "pk": true, - "token": { - "end": { - "column": 16, - "line": 2, - "offset": 89 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 2, - "offset": 76 - } - }, - "type": { - "type_name": "int" - } - }, - { - "inline_refs": [ - { - "fieldNames": [ - "id" - ], - "relation": ">", - "tableName": "user", - "token": { - "end": { - "column": 37, - "line": 3, - "offset": 126 - }, - "filepath": "/main.dbml", - "start": { - "column": 23, - "line": 3, - "offset": 112 - } - } - } - ], - "name": "to_table_ref", - "token": { - "end": { - "column": 38, - "line": 3, - "offset": 127 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 3, - "offset": 92 - } - }, - "type": { - "type_name": "int" - } - } - ], - "indexes": [ - { - "columns": [ - { - "token": { - "end": { - "column": 9, - "line": 6, - "offset": 149 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 6, - "offset": 145 - } - }, - "type": "column", - "value": "name" - } - ], - "note": { - "token": { - "end": { - "column": 56, - "line": 6, - "offset": 196 - }, - "filepath": "/main.dbml", - "start": { - "column": 11, - "line": 6, - "offset": 151 - } - }, - "value": "should have error in \"merchant\" table" - }, - "token": { - "end": { - "column": 57, - "line": 6, - "offset": 197 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 6, - "offset": 145 - } - } - } - ], - "name": "id", - "note": { - "token": { - "end": { - "column": 71, - "line": 1, - "offset": 70 - }, - "filepath": "/main.dbml", - "start": { - "column": 18, - "line": 1, - "offset": 17 - } - }, - "value": "this table is injected with TablePartial \"id\"" - }, - "token": { - "end": { - "column": 2, - "line": 8, - "offset": 203 - }, - "filepath": "/main.dbml", - "start": { - "column": 1, - "line": 1, - "offset": 0 - } - } - }, - { - "headerColor": "#ccc", - "name": "sameHeaderColor", - "note": { - "token": { - "end": { - "column": 4, - "line": 13, - "offset": 340 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 11, - "offset": 258 - } - }, - "value": "This TablePartial only used to inject headerColor for some tables" - }, - "token": { - "end": { - "column": 2, - "line": 14, - "offset": 342 - }, - "filepath": "/main.dbml", - "start": { - "column": 1, - "line": 10, - "offset": 205 - } - } - }, - { - "fields": [ - { - "name": "email", - "token": { - "end": { - "column": 17, - "line": 17, - "offset": 388 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 17, - "offset": 374 - } - }, - "type": { - "type_name": "string" - } - } - ], - "name": "injectForRef", - "token": { - "end": { - "column": 2, - "line": 18, - "offset": 390 - }, - "filepath": "/main.dbml", - "start": { - "column": 1, - "line": 16, - "offset": 344 - } - } - } - ], - "tables": [ - { - "fields": [ - { - "name": "name", - "pk": false, - "token": { - "end": { - "column": 16, - "line": 22, - "offset": 428 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 22, - "offset": 415 - } - }, - "type": { - "type_name": "string" - }, - "unique": false - } - ], - "indexes": [ - { - "columns": [ - { - "token": { - "end": { - "column": 10, - "line": 27, - "offset": 486 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 27, - "offset": 481 - } - }, - "type": "column", - "value": "email" - } - ], - "note": { - "token": { - "end": { - "column": 49, - "line": 27, - "offset": 525 - }, - "filepath": "/main.dbml", - "start": { - "column": 12, - "line": 27, - "offset": 488 - } - }, - "value": "should interpret successfully" - }, - "token": { - "end": { - "column": 50, - "line": 27, - "offset": 526 - }, - "filepath": "/main.dbml", - "start": { - "column": 5, - "line": 27, - "offset": 481 - } - } - } - ], - "name": "user", - "partials": [ - { - "name": "id", - "order": 0, - "token": { - "end": { - "column": 6, - "line": 21, - "offset": 412 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 21, - "offset": 409 - } - } - }, - { - "name": "injectForRef", - "order": 2, - "token": { - "end": { - "column": 16, - "line": 23, - "offset": 444 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 23, - "offset": 431 - } - } - }, - { - "name": "sameHeaderColor", - "order": 3, - "token": { - "end": { - "column": 19, - "line": 24, - "offset": 463 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 24, - "offset": 447 - } - } - } - ], - "token": { - "end": { - "column": 2, - "line": 29, - "offset": 532 - }, - "filepath": "/main.dbml", - "start": { - "column": 1, - "line": 20, - "offset": 392 - } - } - }, - { - "fields": [ - { - "name": "name", - "not_null": true, - "pk": false, - "token": { - "end": { - "column": 30, - "line": 34, - "offset": 633 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 34, - "offset": 606 - } - }, - "type": { - "args": "255", - "type_name": "char(255)" - }, - "unique": false - } - ], - "name": "country", - "note": { - "token": { - "end": { - "column": 42, - "line": 31, - "offset": 575 - }, - "filepath": "/main.dbml", - "start": { - "column": 18, - "line": 31, - "offset": 551 - } - }, - "value": "name is required" - }, - "partials": [ - { - "name": "id", - "order": 0, - "token": { - "end": { - "column": 6, - "line": 32, - "offset": 584 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 32, - "offset": 581 - } - } - }, - { - "name": "sameHeaderColor", - "order": 1, - "token": { - "end": { - "column": 19, - "line": 33, - "offset": 603 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 33, - "offset": 587 - } - } - } - ], - "token": { - "end": { - "column": 2, - "line": 35, - "offset": 635 - }, - "filepath": "/main.dbml", - "start": { - "column": 1, - "line": 31, - "offset": 534 - } - } - }, - { - "fields": [ - { - "name": "name", - "pk": false, - "token": { - "end": { - "column": 16, - "line": 39, - "offset": 732 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 39, - "offset": 719 - } - }, - "type": { - "type_name": "string" - }, - "unique": false - }, - { - "name": "price", - "not_null": true, - "pk": false, - "token": { - "end": { - "column": 29, - "line": 40, - "offset": 761 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 40, - "offset": 735 - } - }, - "type": { - "type_name": "decimal" - }, - "unique": false - } - ], - "headerColor": "#17DACC", - "name": "product", - "note": { - "token": { - "end": { - "column": 71, - "line": 37, - "offset": 707 - }, - "filepath": "/main.dbml", - "start": { - "column": 40, - "line": 37, - "offset": 676 - } - }, - "value": "product must have price" - }, - "partials": [ - { - "name": "id", - "order": 0, - "token": { - "end": { - "column": 6, - "line": 38, - "offset": 716 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 38, - "offset": 713 - } - } - }, - { - "name": "sameHeaderColor", - "order": 3, - "token": { - "end": { - "column": 19, - "line": 41, - "offset": 780 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 41, - "offset": 764 - } - } - } - ], - "token": { - "end": { - "column": 2, - "line": 42, - "offset": 782 - }, - "filepath": "/main.dbml", - "start": { - "column": 1, - "line": 37, - "offset": 637 - } - } - }, - { - "fields": [ - { - "name": "user_id", - "pk": false, - "token": { - "end": { - "column": 16, - "line": 46, - "offset": 877 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 46, - "offset": 864 - } - }, - "type": { - "type_name": "int" - }, - "unique": false - }, - { - "name": "product_id", - "pk": false, - "token": { - "end": { - "column": 19, - "line": 47, - "offset": 896 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 47, - "offset": 880 - } - }, - "type": { - "type_name": "int" - }, - "unique": false - }, - { - "name": "address", - "pk": false, - "token": { - "end": { - "column": 19, - "line": 49, - "offset": 934 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 49, - "offset": 918 - } - }, - "type": { - "type_name": "string" - }, - "unique": false - } - ], - "headerColor": "#08DAFF", - "name": "merchant", - "note": { - "token": { - "end": { - "column": 69, - "line": 44, - "offset": 852 - }, - "filepath": "/main.dbml", - "start": { - "column": 41, - "line": 44, - "offset": 824 - } - }, - "value": "merchants sell a lot" - }, - "partials": [ - { - "name": "id", - "order": 0, - "token": { - "end": { - "column": 6, - "line": 45, - "offset": 861 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 45, - "offset": 858 - } - } - }, - { - "name": "sameHeaderColor", - "order": 3, - "token": { - "end": { - "column": 19, - "line": 48, - "offset": 915 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 48, - "offset": 899 - } - } - } - ], - "token": { - "end": { - "column": 2, - "line": 50, - "offset": 936 - }, - "filepath": "/main.dbml", - "start": { - "column": 1, - "line": 44, - "offset": 784 - } - } - }, - { - "fields": [ - { - "inline_refs": [ - { - "fieldNames": [ - "email" - ], - "relation": "-", - "tableName": "user", - "token": { - "end": { - "column": 36, - "line": 54, - "offset": 1024 - }, - "filepath": "/main.dbml", - "start": { - "column": 19, - "line": 54, - "offset": 1007 - } - } - } - ], - "name": "email", - "note": { - "token": { - "end": { - "column": 111, - "line": 54, - "offset": 1099 - }, - "filepath": "/main.dbml", - "start": { - "column": 38, - "line": 54, - "offset": 1026 - } - }, - "value": "the ref of this column should be bound and interpret successfully" - }, - "pk": false, - "token": { - "end": { - "column": 112, - "line": 54, - "offset": 1100 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 54, - "offset": 991 - } - }, - "type": { - "type_name": "string" - }, - "unique": false - } - ], - "name": "customer", - "note": { - "token": { - "end": { - "column": 32, - "line": 53, - "offset": 988 - }, - "filepath": "/main.dbml", - "start": { - "column": 3, - "line": 53, - "offset": 959 - } - }, - "value": "a customer is a user?" - }, - "token": { - "end": { - "column": 2, - "line": 55, - "offset": 1102 - }, - "filepath": "/main.dbml", - "start": { - "column": 1, - "line": 52, - "offset": 938 - } - } - } - ], - "token": { - "end": { - "column": 1, - "line": 60, - "offset": 1189 - }, + "errors": [ + { + "code": "BINDING_ERROR", + "diagnostic": "No column named 'name' inside TablePartial 'id'", "filepath": "/main.dbml", - "start": { - "column": 1, - "line": 1, - "offset": 0 + "level": "error", + "node": { + "context": { + "id": "node@@name@[L5:C4, L5:C8]", + "snippet": "name" + } } } - } + ] } \ No newline at end of file From c1fb3de789059f828e1bd5043d219d9c6c64188a Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Mon, 1 Jun 2026 11:56:51 +0700 Subject: [PATCH 6/9] fix: respect original uri scheme of model in go-to-definition and go-to-references --- packages/dbml-parse/src/services/definition/provider.ts | 2 +- packages/dbml-parse/src/services/references/provider.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dbml-parse/src/services/definition/provider.ts b/packages/dbml-parse/src/services/definition/provider.ts index d944e4307..d53d3d94e 100644 --- a/packages/dbml-parse/src/services/definition/provider.ts +++ b/packages/dbml-parse/src/services/definition/provider.ts @@ -51,7 +51,7 @@ export default class DBMLDefinitionProvider implements DefinitionProvider { // Use filepath from declaration if available and in multi-file mode (uri is set) let definitionUri = uri; if (uri && declaration.filepath) { - definitionUri = Uri.parse(declaration.filepath.toUri()); + definitionUri = Uri.parse(declaration.filepath.toUri({ protocol: uri.scheme })); } return [ diff --git a/packages/dbml-parse/src/services/references/provider.ts b/packages/dbml-parse/src/services/references/provider.ts index 74a324b09..6320d37f5 100644 --- a/packages/dbml-parse/src/services/references/provider.ts +++ b/packages/dbml-parse/src/services/references/provider.ts @@ -45,7 +45,7 @@ export default class DBMLReferencesProvider implements ReferenceProvider { // Use filepath from reference node if available and in multi-file mode (uri is set) let refUri: Uri = uri; if (uri && refNode.filepath) { - refUri = Uri.parse(refNode.filepath.toUri()); + refUri = Uri.parse(refNode.filepath.toUri({ protocol: uri.scheme })); } return { From 4cfab0cfdd315b17cfdbe69f4b0f1cc518953890 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Mon, 1 Jun 2026 12:05:36 +0700 Subject: [PATCH 7/9] fix: improve scheme handling --- packages/dbml-parse/src/core/types/filepath.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dbml-parse/src/core/types/filepath.ts b/packages/dbml-parse/src/core/types/filepath.ts index 5910bc4d8..eded1125e 100644 --- a/packages/dbml-parse/src/core/types/filepath.ts +++ b/packages/dbml-parse/src/core/types/filepath.ts @@ -109,7 +109,7 @@ export class Filepath implements Internable { // Convert filepath to monaco URI toUri (options: { protocol?: string } = {}): string { const protocol = options.protocol ?? this.protocol; - if (protocol === undefined) { + if (!protocol) { return this.path; } // Windows: C:/path needs an extra leading slash -> file:///C:/path From bf9b3b233bd2553514121288be90d35a986f61ab Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Mon, 1 Jun 2026 12:14:51 +0700 Subject: [PATCH 8/9] test: update tablepartial test --- .../normalized_model/table_partial.in.dbml | 3 + .../normalized_model/table_partial.out.json | 524 ++++++++++++------ 2 files changed, 361 insertions(+), 166 deletions(-) diff --git a/packages/dbml-core/__tests__/examples/normalized_model/table_partial.in.dbml b/packages/dbml-core/__tests__/examples/normalized_model/table_partial.in.dbml index ed05cc523..e1d493898 100644 --- a/packages/dbml-core/__tests__/examples/normalized_model/table_partial.in.dbml +++ b/packages/dbml-core/__tests__/examples/normalized_model/table_partial.in.dbml @@ -37,6 +37,9 @@ TablePartial test2 { } TablePartial testIndex { + email varchar + id int + indexes { email [unique] id [pk] diff --git a/packages/dbml-core/__tests__/examples/normalized_model/table_partial.out.json b/packages/dbml-core/__tests__/examples/normalized_model/table_partial.out.json index 8fec83c57..ff045a25f 100644 --- a/packages/dbml-core/__tests__/examples/normalized_model/table_partial.out.json +++ b/packages/dbml-core/__tests__/examples/normalized_model/table_partial.out.json @@ -47,7 +47,6 @@ "refs": { "1": { "id": 1, - "name": null, "endpointIds": [ 1, 2 @@ -73,11 +72,13 @@ "alias": null, "note": null, "partials": [], + "recordIds": [], "fieldIds": [ 1, 2 ], "indexIds": [], + "checkIds": [], "schemaId": 1, "groupId": null }, @@ -87,11 +88,13 @@ "alias": null, "note": null, "partials": [], + "recordIds": [], "fieldIds": [ 3, 4 ], "indexIds": [], + "checkIds": [], "schemaId": 2, "groupId": null }, @@ -106,14 +109,17 @@ "order": 7, "token": { "start": { - "offset": 823, - "line": 55, + "offset": 849, + "line": 58, "column": 3 }, "end": { - "offset": 829, - "line": 55, + "offset": 855, + "line": 58, "column": 9 + }, + "filepath": { + "path": "/main.dbml" } }, "name": "test2", @@ -123,14 +129,17 @@ "order": 6, "token": { "start": { - "offset": 815, - "line": 54, + "offset": 841, + "line": 57, "column": 3 }, "end": { - "offset": 820, - "line": 54, + "offset": 846, + "line": 57, "column": 8 + }, + "filepath": { + "path": "/main.dbml" } }, "name": "test", @@ -140,14 +149,17 @@ "order": 4, "token": { "start": { - "offset": 759, - "line": 52, + "offset": 785, + "line": 55, "column": 3 }, "end": { - "offset": 780, - "line": 52, + "offset": 806, + "line": 55, "column": 24 + }, + "filepath": { + "path": "/main.dbml" } }, "name": "soft_delete_template", @@ -157,14 +169,17 @@ "order": 3, "token": { "start": { - "offset": 746, - "line": 51, + "offset": 772, + "line": 54, "column": 3 }, "end": { - "offset": 756, - "line": 51, + "offset": 782, + "line": 54, "column": 13 + }, + "filepath": { + "path": "/main.dbml" } }, "name": "testIndex", @@ -174,14 +189,17 @@ "order": 1, "token": { "start": { - "offset": 716, - "line": 49, + "offset": 742, + "line": 52, "column": 3 }, "end": { - "offset": 728, - "line": 49, + "offset": 754, + "line": 52, "column": 15 + }, + "filepath": { + "path": "/main.dbml" } }, "name": "email_index", @@ -191,26 +209,30 @@ "order": 0, "token": { "start": { - "offset": 699, - "line": 48, + "offset": 725, + "line": 51, "column": 3 }, "end": { - "offset": 713, - "line": 48, + "offset": 739, + "line": 51, "column": 17 + }, + "filepath": { + "path": "/main.dbml" } }, "name": "base_template", "id": 1 } ], + "recordIds": [], "fieldIds": [ - 11, 12, 13, - 10, 5, + 10, + 11, 8, 9, 6, @@ -222,6 +244,7 @@ 3, 4 ], + "checkIds": [], "schemaId": 1, "groupId": null }, @@ -235,26 +258,31 @@ "order": 1, "token": { "start": { - "offset": 1133, - "line": 65, + "offset": 1159, + "line": 68, "column": 3 }, "end": { - "offset": 1157, - "line": 65, + "offset": 1183, + "line": 68, "column": 27 + }, + "filepath": { + "path": "/main.dbml" } }, "name": "testColWithTypeHaveArgs", "id": 7 } ], + "recordIds": [], "fieldIds": [ 14, 15, 16 ], "indexIds": [], + "checkIds": [], "schemaId": 1, "groupId": null }, @@ -268,26 +296,31 @@ "order": 0, "token": { "start": { - "offset": 1200, - "line": 69, + "offset": 1226, + "line": 72, "column": 3 }, "end": { - "offset": 1212, - "line": 69, + "offset": 1238, + "line": 72, "column": 15 + }, + "filepath": { + "path": "/main.dbml" } }, "name": "email_index", "id": 3 } ], + "recordIds": [], "fieldIds": [ 17 ], "indexIds": [ 5 ], + "checkIds": [], "schemaId": 1, "groupId": null } @@ -438,6 +471,7 @@ "indexId": 5 } }, + "checks": {}, "fields": { "1": { "id": 1, @@ -450,6 +484,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 1, "enumId": null @@ -465,6 +501,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [ 1 ], @@ -482,6 +520,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [ 4 ], @@ -499,6 +539,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 2, "enumId": null @@ -514,6 +556,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 3, "enumId": null @@ -529,6 +573,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [ 2 ], @@ -545,6 +591,7 @@ }, "note": null, "injectedPartialId": 5, + "checkIds": [], "endpointIds": [ 3 ], @@ -562,6 +609,7 @@ "not_null": true, "note": null, "injectedPartialId": 2, + "checkIds": [], "endpointIds": [], "tableId": 3, "enumId": null @@ -580,6 +628,7 @@ "type": "expression" }, "injectedPartialId": 2, + "checkIds": [], "endpointIds": [], "tableId": 3, "enumId": null @@ -592,9 +641,9 @@ "type_name": "varchar", "args": null }, - "unique": true, "note": null, - "injectedPartialId": 3, + "injectedPartialId": 6, + "checkIds": [], "endpointIds": [], "tableId": 3, "enumId": null @@ -607,10 +656,9 @@ "type_name": "int", "args": null }, - "pk": true, - "not_null": true, "note": null, - "injectedPartialId": 1, + "injectedPartialId": 6, + "checkIds": [], "endpointIds": [], "tableId": 3, "enumId": null @@ -629,6 +677,7 @@ "type": "expression" }, "injectedPartialId": 1, + "checkIds": [], "endpointIds": [], "tableId": 3, "enumId": null @@ -647,6 +696,7 @@ "type": "expression" }, "injectedPartialId": 1, + "checkIds": [], "endpointIds": [], "tableId": 3, "enumId": null @@ -662,6 +712,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 4, "enumId": null @@ -676,6 +728,7 @@ }, "note": "simple column with call expression as type", "injectedPartialId": 7, + "checkIds": [], "endpointIds": [], "tableId": 4, "enumId": null @@ -694,6 +747,7 @@ "value": 0.1 }, "injectedPartialId": 7, + "checkIds": [], "endpointIds": [], "tableId": 4, "enumId": null @@ -709,6 +763,7 @@ "unique": true, "note": null, "injectedPartialId": 3, + "checkIds": [], "endpointIds": [], "tableId": 5, "enumId": null @@ -724,11 +779,6 @@ "fields": [ { "name": "id", - "type": { - "schemaName": null, - "type_name": "int", - "args": null - }, "token": { "start": { "offset": 120, @@ -739,19 +789,23 @@ "offset": 141, "line": 12, "column": 24 + }, + "filepath": { + "path": "/main.dbml" } }, - "inline_refs": [], - "pk": true, - "not_null": true - }, - { - "name": "created_at", "type": { "schemaName": null, - "type_name": "timestamp", + "type_name": "int", "args": null }, + "pk": true, + "not_null": true, + "inline_refs": [], + "checks": [] + }, + { + "name": "created_at", "token": { "start": { "offset": 144, @@ -762,21 +816,25 @@ "offset": 183, "line": 13, "column": 42 + }, + "filepath": { + "path": "/main.dbml" } }, - "inline_refs": [], + "type": { + "schemaName": null, + "type_name": "timestamp", + "args": null + }, "dbdefault": { "value": "now()", "type": "expression" - } + }, + "inline_refs": [], + "checks": [] }, { "name": "updated_at", - "type": { - "schemaName": null, - "type_name": "timestamp", - "args": null - }, "token": { "start": { "offset": 186, @@ -787,13 +845,22 @@ "offset": 225, "line": 14, "column": 42 + }, + "filepath": { + "path": "/main.dbml" } }, - "inline_refs": [], + "type": { + "schemaName": null, + "type_name": "timestamp", + "args": null + }, "dbdefault": { "value": "now()", "type": "expression" - } + }, + "inline_refs": [], + "checks": [] } ], "indexes": [] @@ -805,11 +872,6 @@ "fields": [ { "name": "delete_status", - "type": { - "schemaName": null, - "type_name": "boolean", - "args": null - }, "token": { "start": { "offset": 267, @@ -820,18 +882,22 @@ "offset": 299, "line": 18, "column": 35 + }, + "filepath": { + "path": "/main.dbml" } }, - "inline_refs": [], - "not_null": true - }, - { - "name": "deleted_at", "type": { "schemaName": null, - "type_name": "timestamp", + "type_name": "boolean", "args": null }, + "not_null": true, + "inline_refs": [], + "checks": [] + }, + { + "name": "deleted_at", "token": { "start": { "offset": 302, @@ -842,13 +908,22 @@ "offset": 341, "line": 19, "column": 42 + }, + "filepath": { + "path": "/main.dbml" } }, - "inline_refs": [], + "type": { + "schemaName": null, + "type_name": "timestamp", + "args": null + }, "dbdefault": { "value": "now()", "type": "expression" - } + }, + "inline_refs": [], + "checks": [] } ], "indexes": [] @@ -860,11 +935,6 @@ "fields": [ { "name": "email", - "type": { - "schemaName": null, - "type_name": "varchar", - "args": null - }, "token": { "start": { "offset": 374, @@ -875,10 +945,19 @@ "offset": 396, "line": 23, "column": 25 + }, + "filepath": { + "path": "/main.dbml" } }, + "type": { + "schemaName": null, + "type_name": "varchar", + "args": null + }, + "unique": true, "inline_refs": [], - "unique": true + "checks": [] } ], "indexes": [ @@ -897,10 +976,14 @@ "offset": 419, "line": 26, "column": 10 + }, + "filepath": { + "path": "/main.dbml" } } } ], + "unique": true, "token": { "start": { "offset": 414, @@ -911,9 +994,11 @@ "offset": 428, "line": 26, "column": 19 + }, + "filepath": { + "path": "/main.dbml" } - }, - "unique": true + } } ] }, @@ -924,11 +1009,6 @@ "fields": [ { "name": "refColumn1", - "type": { - "schemaName": null, - "type_name": "int", - "args": null - }, "token": { "start": { "offset": 458, @@ -939,8 +1019,16 @@ "offset": 486, "line": 31, "column": 31 + }, + "filepath": { + "path": "/main.dbml" } }, + "type": { + "schemaName": null, + "type_name": "int", + "args": null + }, "inline_refs": [ { "schemaName": null, @@ -959,18 +1047,17 @@ "offset": 485, "line": 31, "column": 30 + }, + "filepath": { + "path": "/main.dbml" } } } - ] + ], + "checks": [] }, { "name": "refColumn2", - "type": { - "schemaName": null, - "type_name": "varchar", - "args": null - }, "token": { "start": { "offset": 489, @@ -981,8 +1068,16 @@ "offset": 521, "line": 32, "column": 35 + }, + "filepath": { + "path": "/main.dbml" } }, + "type": { + "schemaName": null, + "type_name": "varchar", + "args": null + }, "inline_refs": [ { "schemaName": null, @@ -1001,10 +1096,14 @@ "offset": 520, "line": 32, "column": 34 + }, + "filepath": { + "path": "/main.dbml" } } } - ] + ], + "checks": [] } ], "indexes": [] @@ -1016,11 +1115,6 @@ "fields": [ { "name": "refColumn2", - "type": { - "schemaName": null, - "type_name": "bool", - "args": null - }, "token": { "start": { "offset": 548, @@ -1031,8 +1125,16 @@ "offset": 579, "line": 36, "column": 34 + }, + "filepath": { + "path": "/main.dbml" } }, + "type": { + "schemaName": null, + "type_name": "bool", + "args": null + }, "inline_refs": [ { "schemaName": "a", @@ -1051,10 +1153,14 @@ "offset": 578, "line": 36, "column": 33 + }, + "filepath": { + "path": "/main.dbml" } } } - ] + ], + "checks": [] } ], "indexes": [] @@ -1063,7 +1169,58 @@ "id": 6, "name": "testIndex", "note": null, - "fields": [], + "fields": [ + { + "name": "email", + "token": { + "start": { + "offset": 610, + "line": 40, + "column": 3 + }, + "end": { + "offset": 623, + "line": 40, + "column": 16 + }, + "filepath": { + "path": "/main.dbml" + } + }, + "type": { + "schemaName": null, + "type_name": "varchar", + "args": null + }, + "inline_refs": [], + "checks": [] + }, + { + "name": "id", + "token": { + "start": { + "offset": 626, + "line": 41, + "column": 3 + }, + "end": { + "offset": 632, + "line": 41, + "column": 9 + }, + "filepath": { + "path": "/main.dbml" + } + }, + "type": { + "schemaName": null, + "type_name": "int", + "args": null + }, + "inline_refs": [], + "checks": [] + } + ], "indexes": [ { "columns": [ @@ -1072,31 +1229,37 @@ "type": "column", "token": { "start": { - "offset": 624, - "line": 41, + "offset": 650, + "line": 44, "column": 5 }, "end": { - "offset": 629, - "line": 41, + "offset": 655, + "line": 44, "column": 10 + }, + "filepath": { + "path": "/main.dbml" } } } ], + "unique": true, "token": { "start": { - "offset": 624, - "line": 41, + "offset": 650, + "line": 44, "column": 5 }, "end": { - "offset": 638, - "line": 41, + "offset": 664, + "line": 44, "column": 19 + }, + "filepath": { + "path": "/main.dbml" } - }, - "unique": true + } }, { "columns": [ @@ -1105,31 +1268,37 @@ "type": "column", "token": { "start": { - "offset": 643, - "line": 42, + "offset": 669, + "line": 45, "column": 5 }, "end": { - "offset": 645, - "line": 42, + "offset": 671, + "line": 45, "column": 7 + }, + "filepath": { + "path": "/main.dbml" } } } ], + "pk": true, "token": { "start": { - "offset": 643, - "line": 42, + "offset": 669, + "line": 45, "column": 5 }, "end": { - "offset": 650, - "line": 42, + "offset": 676, + "line": 45, "column": 12 + }, + "filepath": { + "path": "/main.dbml" } - }, - "pk": true + } }, { "columns": [ @@ -1138,14 +1307,17 @@ "type": "column", "token": { "start": { - "offset": 656, - "line": 43, + "offset": 682, + "line": 46, "column": 6 }, "end": { - "offset": 661, - "line": 43, + "offset": 687, + "line": 46, "column": 11 + }, + "filepath": { + "path": "/main.dbml" } } }, @@ -1154,31 +1326,37 @@ "type": "column", "token": { "start": { - "offset": 663, - "line": 43, + "offset": 689, + "line": 46, "column": 13 }, "end": { - "offset": 665, - "line": 43, + "offset": 691, + "line": 46, "column": 15 + }, + "filepath": { + "path": "/main.dbml" } } } ], + "unique": true, "token": { "start": { - "offset": 655, - "line": 43, + "offset": 681, + "line": 46, "column": 5 }, "end": { - "offset": 675, - "line": 43, + "offset": 701, + "line": 46, "column": 25 + }, + "filepath": { + "path": "/main.dbml" } - }, - "unique": true + } } ] }, @@ -1189,60 +1367,69 @@ "fields": [ { "name": "callExpColInTest1", - "type": { - "schemaName": null, - "type_name": "int(4)", - "args": "4" - }, "token": { "start": { - "offset": 874, - "line": 59, + "offset": 900, + "line": 62, "column": 3 }, "end": { - "offset": 951, - "line": 59, + "offset": 977, + "line": 62, "column": 80 + }, + "filepath": { + "path": "/main.dbml" } }, - "inline_refs": [], + "type": { + "schemaName": null, + "type_name": "int(4)", + "args": "4" + }, "note": { "value": "simple column with call expression as type", "token": { "start": { - "offset": 900, - "line": 59, + "offset": 926, + "line": 62, "column": 29 }, "end": { - "offset": 950, - "line": 59, + "offset": 976, + "line": 62, "column": 79 + }, + "filepath": { + "path": "/main.dbml" } } - } + }, + "inline_refs": [], + "checks": [] }, { "name": "callExpColInTest2", - "type": { - "schemaName": null, - "type_name": "decimal(10,2)", - "args": "10,2" - }, "token": { "start": { - "offset": 954, - "line": 60, + "offset": 980, + "line": 63, "column": 3 }, "end": { - "offset": 1086, - "line": 60, + "offset": 1112, + "line": 63, "column": 135 + }, + "filepath": { + "path": "/main.dbml" } }, - "inline_refs": [], + "type": { + "schemaName": null, + "type_name": "decimal(10,2)", + "args": "10,2" + }, "dbdefault": { "type": "number", "value": 0.1 @@ -1251,20 +1438,25 @@ "value": "column with type that have arguments and default value being a float number", "token": { "start": { - "offset": 1002, - "line": 60, + "offset": 1028, + "line": 63, "column": 51 }, "end": { - "offset": 1085, - "line": 60, + "offset": 1111, + "line": 63, "column": 134 + }, + "filepath": { + "path": "/main.dbml" } } - } + }, + "inline_refs": [], + "checks": [] } ], "indexes": [] } } -} +} \ No newline at end of file From cd1a85d22adbe1f98015a66557183a394113f17a Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Mon, 1 Jun 2026 12:18:50 +0700 Subject: [PATCH 9/9] fix: improve scheme handling --- packages/dbml-parse/src/core/types/filepath.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dbml-parse/src/core/types/filepath.ts b/packages/dbml-parse/src/core/types/filepath.ts index eded1125e..7d0326a1c 100644 --- a/packages/dbml-parse/src/core/types/filepath.ts +++ b/packages/dbml-parse/src/core/types/filepath.ts @@ -108,7 +108,7 @@ export class Filepath implements Internable { // Convert filepath to monaco URI toUri (options: { protocol?: string } = {}): string { - const protocol = options.protocol ?? this.protocol; + const protocol = options.protocol || this.protocol; if (!protocol) { return this.path; }