DBX-6689 Support Diagram View as code syntax#849
DBX-6689 Support Diagram View as code syntax#849huyphung1602 wants to merge 26 commits intomasterfrom
Conversation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add two new symbol classes for DiagramView support: - DiagramViewSymbol: Represents a DiagramView block with its own symbol table - DiagramViewFieldSymbol: Represents DiagramView fields (table/note/group/schema references) Follows the same pattern as TableGroupSymbol and TableGroupFieldSymbol. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add DiagramView and DiagramViewField to SymbolKind enum - Add createDiagramViewSymbolIndex and createDiagramViewFieldSymbolIndex - Update createNodeSymbolIndex to handle new symbol kinds Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add DiagramViewValidator class with validation for: - Name requirements (required, single identifier) - Top-level context validation - Allowed sub-blocks (Tables, Notes, TableGroups, Schemas) - Wildcard usage warning - Duplicate field detection - Add error codes for DiagramView validation - Register validator in pickValidator switch Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add DiagramViewBinder for binding references in DiagramView blocks - Add DiagramViewInterpreter for interpreting DiagramView syntax - Add FilterConfig and DiagramView types to interpreter types - Register binder in pickBinder function - Register interpreter in main interpreter - Update Database type to include diagramViews array - Update snapshots Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add syncDiagramView function for synchronizing DiagramView blocks - Support create, update, delete operations on DiagramView blocks - Generate properly formatted DiagramView DBML syntax - Export DiagramView, FilterConfig, and DiagramViewSyncOperation types - Export syncDiagramView from package index Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When at least one Trinity dimension (tables/tableGroups/schemas) is explicitly specified with a non-null value in a DiagramView body, omitted Trinity dims are promoted from null → [] (show all). The Notes dimension remains independent and stays null when omitted. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ilterConfig from dbml-core types
When a table has an alias (e.g., Table users as U), the DiagramView
interpreter now resolves the alias to the real table name via the
bound referee symbol. Previously, using an alias in Tables { U }
would output { name: 'U' } which the renderer couldn't find.
Now the interpreter checks field.callee's referee (set by the binder)
and extracts the real name from the original declaration, falling back
to the literal text for unbound references.
Coverage ReportCommit: 0301572 Overall Coverage
Package Coverage
|
| File | Lines | Statements | Functions | Branches |
|---|---|---|---|---|
src/connectors/bigquery/index.ts |
0.00% | 0.00% | 0.00% | 0.00% |
src/utils/credential-loader.ts |
0.00% | 0.00% | 0.00% | 0.00% |
src/utils/helpers.ts |
0.00% | 0.00% | 0.00% | 0.00% |
src/connectors/snowflake/index.ts |
10.56% | 10.31% | 0.00% | 0.00% |
src/utils/parseSchema.ts |
46.15% | 42.85% | 28.57% | 27.27% |
src/connectors/connector.ts |
66.66% | 66.66% | 100.00% | 57.14% |
src/connectors/oracle/tables.ts |
71.25% | 67.39% | 100.00% | 56.96% |
src/connectors/oracle/index.ts |
80.00% | 80.76% | 100.00% | 25.00% |
src/connectors/oracle/utils.ts |
85.71% | 85.71% | 100.00% | 71.42% |
@dbml/dbml-parse
42 file(s) below 80% coverage
| File | Lines | Statements | Functions | Branches |
|---|---|---|---|---|
src/compiler/queries/container/scope.ts |
0.00% | 0.00% | 0.00% | 100.00% |
src/services/diagnostics/provider.ts |
0.00% | 0.00% | 0.00% | 0.00% |
src/core/interpreter/elementInterpreter/project.ts |
51.42% | 51.42% | 100.00% | 36.36% |
src/core/analyzer/symbol/utils.ts |
52.00% | 52.00% | 100.00% | 45.45% |
src/core/interpreter/records/utils/data/sqlTypes.ts |
56.25% | 58.82% | 75.00% | 46.55% |
src/core/analyzer/binder/elementBinder/diagramView.ts |
62.02% | 64.28% | 79.16% | 48.57% |
src/core/analyzer/binder/elementBinder/note.ts |
62.50% | 64.70% | 83.33% | 50.00% |
src/compiler/queries/utils.ts |
64.83% | 65.95% | 88.88% | 47.43% |
src/core/interpreter/records/utils/data/values.ts |
65.13% | 57.14% | 72.72% | 50.37% |
src/compiler/queries/parse.ts |
66.66% | 66.66% | 66.66% | 100.00% |
src/compiler/queries/token.ts |
66.66% | 66.66% | 66.66% | 100.00% |
src/core/analyzer/validator/elementValidators/indexes.ts |
72.00% | 74.07% | 90.90% | 59.61% |
src/core/analyzer/binder/elementBinder/enum.ts |
72.22% | 73.68% | 100.00% | 62.50% |
src/core/analyzer/validator/elementValidators/note.ts |
73.07% | 71.42% | 76.92% | 68.75% |
src/core/analyzer/symbol/symbolIndex.ts |
73.91% | 74.46% | 93.75% | 42.85% |
src/core/analyzer/analyzer.ts |
75.00% | 66.66% | 60.00% | 100.00% |
src/core/analyzer/utils.ts |
77.77% | 76.36% | 88.88% | 75.24% |
src/core/analyzer/binder/elementBinder/project.ts |
77.77% | 78.94% | 100.00% | 50.00% |
src/core/analyzer/validator/elementValidators/checks.ts |
77.77% | 79.31% | 93.75% | 71.87% |
src/core/analyzer/validator/elementValidators/records.ts |
78.08% | 78.66% | 93.75% | 74.19% |
src/core/analyzer/validator/elementValidators/project.ts |
78.12% | 78.78% | 100.00% | 56.25% |
src/core/utils.ts |
78.57% | 77.41% | 80.00% | 60.71% |
src/core/analyzer/binder/elementBinder/ref.ts |
78.78% | 80.00% | 90.90% | 75.00% |
src/core/analyzer/binder/elementBinder/indexes.ts |
79.06% | 77.77% | 90.90% | 68.18% |
src/core/analyzer/binder/elementBinder/tableGroup.ts |
80.00% | 80.64% | 100.00% | 50.00% |
src/compiler/queries/transform/syncDiagramView.ts |
81.11% | 76.69% | 82.35% | 69.35% |
src/core/interpreter/records/utils/constraints/pk.ts |
82.00% | 80.00% | 92.30% | 54.54% |
src/services/suggestions/utils.ts |
82.35% | 76.78% | 92.85% | 70.37% |
src/core/analyzer/binder/elementBinder/records.ts |
82.79% | 83.33% | 93.75% | 71.42% |
src/core/analyzer/validator/elementValidators/diagramView.ts |
82.95% | 80.85% | 89.47% | 70.83% |
src/compiler/queries/container/token.ts |
83.33% | 85.71% | 100.00% | 75.00% |
src/core/analyzer/validator/elementValidators/tablePartial.ts |
83.43% | 80.72% | 87.23% | 64.28% |
src/core/analyzer/binder/elementBinder/tablePartial.ts |
86.00% | 86.53% | 100.00% | 69.23% |
src/core/interpreter/elementInterpreter/diagramView.ts |
86.51% | 82.69% | 70.00% | 67.94% |
src/core/parser/parser.ts |
86.93% | 87.08% | 100.00% | 79.25% |
src/services/suggestions/recordRowSnippet.ts |
88.67% | 84.61% | 100.00% | 78.00% |
src/compiler/queries/symbol.ts |
91.30% | 92.00% | 100.00% | 78.57% |
__tests__/utils/compiler.ts |
92.92% | 92.75% | 100.00% | 71.42% |
src/core/interpreter/elementInterpreter/sticky_note.ts |
95.00% | 95.23% | 100.00% | 66.66% |
src/core/interpreter/records/utils/constraints/unique.ts |
96.15% | 93.93% | 100.00% | 66.66% |
src/core/analyzer/symbol/symbolTable.ts |
100.00% | 100.00% | 100.00% | 75.00% |
src/services/definition/provider.ts |
100.00% | 100.00% | 100.00% | 75.00% |
| let leftExpression: NormalExpressionNode | undefined; | ||
|
|
||
| if (isOpToken(this.peek())) { | ||
| // Allow * as a wildcard identifier (e.g. DiagramView Tables { * }) |
There was a problem hiding this comment.
Problem: This change means that we allow * as regular table names, which is likely unintended.
Suggested fix:
- Support in Lexer instead: Lex
*as a WildcardToken instead of an operator token. - In parser, add WildcardNode and allow WildcardNode in function application node's callee.
- In all validator except for DiagramViewValidator, flag an error when WildcardNode is encountered.
| @@ -1,5 +1,5 @@ | |||
| { | |||
| "version": "6.5.0", | |||
| "version": "7.0.0-alpha.0", | |||
There was a problem hiding this comment.
remember to revert the version back into 6.5.0 when we merge the PR nhe anh :v
| ); | ||
|
|
||
| if (hasWildcard && hasSpecificItems) { | ||
| errors.push(new CompileWarning( |
There was a problem hiding this comment.
CompilerWarning should go into a separate warnings array. we do not mix warnings inside errors 👀
| validateFields (fields: FunctionApplicationNode[]): (CompileError | CompileWarning)[] { | ||
| return fields.flatMap((field) => { | ||
| // Body-level {*} is valid shorthand for "show all entities" | ||
| if (isWildcardExpression(field.callee)) { |
| return errors; | ||
| } | ||
|
|
||
| registerSubBlockFields (sub: ElementDeclarationNode): CompileError[] { |
There was a problem hiding this comment.
can registerSubBlockFields be called inside validateSubblock instead of validateSubElement? 🤔 it makes more sense to me
| if (symbol instanceof PartialInjectionSymbol) { | ||
| return SymbolKind.PartialInjection; | ||
| } | ||
| if (symbol instanceof StickyNoteSymbol) { |
There was a problem hiding this comment.
I think these are missing DiagramViewSymbol and DiagramViewFieldSymbol
| return bindees.flatMap((bindee) => { | ||
| const schemaBindee = bindee.variables.pop(); | ||
| if (!schemaBindee) { | ||
| return []; | ||
| } | ||
|
|
||
| return lookupAndBindInScope(this.ast, [ | ||
| { node: schemaBindee, kind: SymbolKind.Schema }, | ||
| ]); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
| return bindees.flatMap((bindee) => { | |
| const schemaBindee = bindee.variables.pop(); | |
| if (!schemaBindee) { | |
| return []; | |
| } | |
| return lookupAndBindInScope(this.ast, [ | |
| { node: schemaBindee, kind: SymbolKind.Schema }, | |
| ]); | |
| }); | |
| }); | |
| return lookupAndBindInScope(this.ast, bindees.map((b) => ({ node: b, kind: SymbolKind.Schema }))); | |
| }); | |
| }); |
something like this would be better, as in binder, schemas can be nested such as schema1.schema2.schema3.... We should bind all of them.
| /** Internal: tracks which dims used explicit wildcard (*). Stripped before output. */ | ||
| _explicitWildcards?: Set<string>; | ||
| /** Internal: tracks which dims were explicitly declared in the DBML. Stripped before output. */ | ||
| _explicitlySet?: Set<string>; | ||
| } |
There was a problem hiding this comment.
I suggest in IntepreterDatabase, we have a Map<DiagramView, explicitWildcards/explicitlySet> instead. instead of DiagramView._explicitWildcards, we can access via map.get(DiagramView)
InterpreterDatabase is meant for dirty working state.
| if ( | ||
| element instanceof ElementDeclarationNode | ||
| && element.parent instanceof ElementDeclarationNode | ||
| && element.parent.type?.value.toLowerCase() === 'diagramview' |
There was a problem hiding this comment.
We should use ElementKind.DiagramView instead of diagramview here 👀
| suggestions: [...compiler.parse.publicSymbolTable().entries()].flatMap(([index]) => { | ||
| const res = destructureIndex(index).unwrap_or(undefined); | ||
| if (res === undefined) return []; | ||
| const { kind, name } = res; | ||
| if (kind !== SymbolKind.Table && kind !== SymbolKind.Schema) return []; | ||
| return { | ||
| label: name, | ||
| insertText: name, | ||
| insertTextRules: CompletionItemInsertTextRule.KeepWhitespace, | ||
| kind: pickCompletionItemKind(kind), | ||
| range: undefined as any, | ||
| }; | ||
| }), | ||
| }).suggestions, | ||
| ], | ||
| }; |
There was a problem hiding this comment.
We have suggestNamesInScope for this. See suggestInRefField function for example usage. Similar to Tablegroups, Schemas suggestions below
| type FilterConfig, | ||
| } from '@/core/interpreter/types'; | ||
|
|
||
| // Export syncDiagramView transform |
There was a problem hiding this comment.
Our convention is that compiler queries should be exposed via Compiler's methods instead of bare function like this. You can check renameTable :v
| lines.push(' Tables { * }'); | ||
| } else { | ||
| const tableNames = visibleEntities.tables.map((t) => | ||
| t.schemaName === 'public' ? t.name : `${t.schemaName}.${t.name}`, |
There was a problem hiding this comment.
We have DEFAULT_SCHEMA_NAME for public nhe a
| return applyTextEdits(dbml, edits); | ||
| } | ||
|
|
||
| function applyDelete ( |
There was a problem hiding this comment.
can applyDelete reuse applyTextEdits khong ta 🤔
| recordsElements: ElementDeclarationNode[]; | ||
| cachedMergedTables: Map<Table, Table>; // map Table to Table that has been merged with table partials | ||
| source: string; | ||
| diagramViews?: Map<ElementDeclarationNode, DiagramView>; |
There was a problem hiding this comment.
This need not be optional 🤔 just initialize it in the interpreter?

Summary
Support Diagram View as code syntaxes
Issue
(issue link here)
Lasting Changes (Technical)
Checklist
Please check directly on the box once each of these are done