From a2e8df6dfcf5292f67daaaceab0233c797650dfe Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 10:02:43 +0700 Subject: [PATCH 01/35] feat: improve relationship cardinality and enrich with optionality --- .../core/global_modules/program/interpret.ts | 2 +- .../src/core/global_modules/ref/interpret.ts | 2 +- .../src/core/global_modules/utils.ts | 29 --- .../dbml-parse/src/core/types/relation.ts | 193 ++++++++++++++++++ .../dbml-parse/src/core/types/schemaJson.ts | 5 +- packages/dbml-parse/src/index.ts | 6 +- 6 files changed, 202 insertions(+), 35 deletions(-) create mode 100644 packages/dbml-parse/src/core/types/relation.ts diff --git a/packages/dbml-parse/src/core/global_modules/program/interpret.ts b/packages/dbml-parse/src/core/global_modules/program/interpret.ts index 7f6f0ab04..e7d9f32ca 100644 --- a/packages/dbml-parse/src/core/global_modules/program/interpret.ts +++ b/packages/dbml-parse/src/core/global_modules/program/interpret.ts @@ -29,7 +29,7 @@ import type { ElementRef } from '@/core/types/schemaJson'; import { validateForeignKeys, validatePrimaryKey, validateUnique } from '../records/utils/constraints'; import type { TableInfo } from '../records/utils/constraints/fk'; import { getTokenPosition } from '@/core/utils/interpret'; -import { getMultiplicities } from '../utils'; +import { getMultiplicities } from '@/core/types/relation'; export default class ProgramInterpreter { private compiler: Compiler; diff --git a/packages/dbml-parse/src/core/global_modules/ref/interpret.ts b/packages/dbml-parse/src/core/global_modules/ref/interpret.ts index 60a0764a4..eb13667bf 100644 --- a/packages/dbml-parse/src/core/global_modules/ref/interpret.ts +++ b/packages/dbml-parse/src/core/global_modules/ref/interpret.ts @@ -23,7 +23,7 @@ import { getTokenPosition, } from '@/core/utils/interpret'; import Report from '@/core/types/report'; -import { getMultiplicities } from '../utils'; +import { getMultiplicities } from '@/core/types/relation'; import { zip } from 'lodash-es'; export class RefInterpreter { diff --git a/packages/dbml-parse/src/core/global_modules/utils.ts b/packages/dbml-parse/src/core/global_modules/utils.ts index 94d19d100..930926a22 100644 --- a/packages/dbml-parse/src/core/global_modules/utils.ts +++ b/packages/dbml-parse/src/core/global_modules/utils.ts @@ -1,6 +1,5 @@ import type Compiler from '@/compiler'; import { getMemberChain } from '@/core/parser/utils'; -import type { RelationCardinality } from '@/core/types'; import { UNHANDLED } from '@/core/types/module'; import { InfixExpressionNode, PostfixExpressionNode, PrefixExpressionNode, PrimaryExpressionNode, SyntaxNode, TupleExpressionNode, VariableNode, @@ -111,31 +110,3 @@ export function nodeRefereeOfLeftExpression (compiler: Compiler, node: SyntaxNod return compiler.nodeReferee(leftExpr).getFiltered(UNHANDLED) ?? undefined; } -export function getMultiplicities ( - op: string, -): [RelationCardinality, RelationCardinality] | undefined { - switch (op) { - case '<': - return [ - '1', - '*', - ]; - case '<>': - return [ - '*', - '*', - ]; - case '>': - return [ - '*', - '1', - ]; - case '-': - return [ - '1', - '1', - ]; - default: - return undefined; - } -} diff --git a/packages/dbml-parse/src/core/types/relation.ts b/packages/dbml-parse/src/core/types/relation.ts new file mode 100644 index 000000000..3452353d6 --- /dev/null +++ b/packages/dbml-parse/src/core/types/relation.ts @@ -0,0 +1,193 @@ +type BaseRelationshipOp = '>' | '<' | '-' | '<>'; +export type RelationshipOp = `${'?' | ''}${BaseRelationshipOp}${'?' | ''}`; + +// A cardinality is either: +// - a single number: exactly N (for backwards compatibility) +// - '*': shorthand for 1..* (for backwards compatibility) +// - 'min..max': a range (e.g. '0..1', '1..*', '2..5') +export type RelationCardinality = `${number}` | '*' | `${number}..${number | '*'}`; + +// Collapse 'N..N' to just 'N' when min equals max +// This is for backwards compatibility: +// - '1': 1..1 +// - '*': 1..many +export function normalizeCardinality (c: RelationCardinality): RelationCardinality { + if (c === '*') return c; + const { min, max } = parseCardinality(c); + if (typeof max === 'number' && min === max) return `${min}` as RelationCardinality; + return c; +} + +// Parse any RelationCardinality into its numeric min/max +export function parseCardinality (c: RelationCardinality): { min: number; max: number | '*' } { + if (c === '*') return { min: 1, max: '*' }; + const [ + min, + max, + ] = c.split('..'); + const minNum = Number(min); + if (max === undefined) return { min: minNum, max: minNum }; + return { + min: minNum, + max: max === '*' ? '*' : Number(max), + }; +} + +export const RELATIONSHIP_OPS: ReadonlySet = new Set([ + '-', + '<>', + '>', + '<', + '-?', + '?-', + '?-?', + '?>', + '>?', + '?>?', + '?<', + '', + '<>?', + '?<>?', +]); + +export const CARDINALITY_ONE: RelationCardinality = '1'; +export const CARDINALITY_MAYBE: RelationCardinality = '0..1'; +export const CARDINALITY_SOME: RelationCardinality = '*'; // Equivalent to '1..*' +export const CARDINALITY_MANY: RelationCardinality = '0..*'; + +export function getMultiplicities ( + op: string, +): [RelationCardinality, RelationCardinality] | undefined { + switch (op) { + case '-': return [ + CARDINALITY_ONE, + CARDINALITY_ONE, + ]; + case '<>': return [ + CARDINALITY_SOME, + CARDINALITY_SOME, + ]; + case '>': return [ + CARDINALITY_SOME, + CARDINALITY_ONE, + ]; + case '<': return [ + CARDINALITY_ONE, + CARDINALITY_SOME, + ]; + // optional-one variants + case '-?': return [ + CARDINALITY_ONE, + CARDINALITY_MAYBE, + ]; + case '?-': return [ + CARDINALITY_MAYBE, + CARDINALITY_ONE, + ]; + case '?-?': return [ + CARDINALITY_MAYBE, + CARDINALITY_MAYBE, + ]; + // directional with optional side + case '?>': return [ + CARDINALITY_MANY, + CARDINALITY_ONE, + ]; + case '>?': return [ + CARDINALITY_SOME, + CARDINALITY_MAYBE, + ]; + case '?>?': return [ + CARDINALITY_MANY, + CARDINALITY_MAYBE, + ]; + case '?<': return [ + CARDINALITY_MAYBE, + CARDINALITY_SOME, + ]; + case '': return [ + CARDINALITY_MANY, + CARDINALITY_SOME, + ]; + case '<>?': return [ + CARDINALITY_SOME, + CARDINALITY_MANY, + ]; + case '?<>?': return [ + CARDINALITY_MANY, + CARDINALITY_MANY, + ]; + default: + return undefined; + } +} + +// Reverse of getMultiplicities: cardinality pair -> operator. +export function getRelationshipOp ( + left: RelationCardinality, + right: RelationCardinality, +): RelationshipOp { + const { min: leftMin, max: leftMax } = parseCardinality(left); + const { min: rightMin, max: rightMax } = parseCardinality(right); + + // Left is [1..1] (CARDINALITY_ONE) + if (leftMin === 1 && leftMax === 1) { + // Right is [1..1] (CARDINALITY_ONE) + if (rightMin === 1 && rightMax === 1) return '-'; + // Right is [0..1] (CARDINALITY_MAYBE) + if (rightMin === 0 && rightMax === 1) return '-?'; + // Right is [0..*] (CARDINALITY_MANY) + if (rightMin === 0 && rightMax === '*') return '= 1 && rightMax === '*') return '<'; + } + + // Left is [0..1] (CARDINALITY_MAYBE) + if (leftMin === 0 && leftMax === 1) { + // Right is [1..1] (CARDINALITY_ONE) + if (rightMin === 1 && rightMax === 1) return '?-'; + // Right is [0..1] (CARDINALITY_MAYBE) + if (rightMin === 0 && rightMax === 1) return '?-?'; + // Right is [0..*] (CARDINALITY_MANY) + if (rightMin === 0 && rightMax === '*') return '?= 1 && rightMax === '*') return '?<'; + } + + // Left is [0..*] + if (leftMin === 0 && leftMax === '*') { + // Right is [1..1] (CARDINALITY_ONE) + if (rightMin === 1 && rightMax === 1) return '?>'; + // Right is [0..1] (CARDINALITY_MAYBE) + if (rightMin === 0 && rightMax === 1) return '?>?'; + // Right is [0..*] + if (rightMin === 0 && rightMax === '*') return '?<>?'; + // Right is [1..*] (CARDINALITY_SOME) + if (rightMin >= 1 && rightMax === '*') return '?<>'; + } + + // Left is [1..*] (CARDINALITY_SOME / CARDINALITY_MANY) + if (leftMin >= 1 && leftMax === '*') { + // Right is [1..1] (CARDINALITY_ONE) + if (rightMin === 1 && rightMax === 1) return '>'; + // Right is [0..1] (CARDINALITY_MAYBE) + if (rightMin === 0 && rightMax === 1) return '>?'; + // Right is [0..*] + if (rightMin === 0 && rightMax === '*') return '<>?'; + // Right is [1..*] (CARDINALITY_SOME) + if (rightMin >= 1 && rightMax === '*') return '<>'; + } + + return '<>'; +} diff --git a/packages/dbml-parse/src/core/types/schemaJson.ts b/packages/dbml-parse/src/core/types/schemaJson.ts index 4713e6479..dceb00b06 100644 --- a/packages/dbml-parse/src/core/types/schemaJson.ts +++ b/packages/dbml-parse/src/core/types/schemaJson.ts @@ -1,6 +1,7 @@ import { NONE_COLOR } from '@/constants'; import type { Filepath } from './filepath'; import type { Position } from './position'; +import type { RelationshipOp, RelationCardinality } from './relation'; export type Color = `#${string}` | typeof NONE_COLOR; @@ -164,7 +165,7 @@ export interface InlineRef { schemaName: string | null; tableName: string; fieldNames: string[]; - relation: '>' | '<' | '-' | '<>'; + relation: RelationshipOp; token: TokenPosition; } @@ -189,8 +190,6 @@ export interface RefEndpoint { token: TokenPosition; } -export type RelationCardinality = '1' | '*'; - export interface Enum { name: string; schemaName: string | null; diff --git a/packages/dbml-parse/src/index.ts b/packages/dbml-parse/src/index.ts index 45b7e8efc..cef913a04 100644 --- a/packages/dbml-parse/src/index.ts +++ b/packages/dbml-parse/src/index.ts @@ -67,7 +67,6 @@ export { type Ref, type RefEndpointPair, type RefEndpoint, - type RelationCardinality, type Enum, type EnumField, type TableGroup, @@ -87,6 +86,11 @@ export { type DiagramView, } from '@/core/types/schemaJson'; +export { + type RelationCardinality, + type RelationshipOp, +} from '@/core/types/relation'; + // DiagramView types export type { DiagramViewSyncOperation, DiagramViewBlock, From 6465cc35dd29cf3cb4955a91bea6943f3822255d Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 10:05:45 +0700 Subject: [PATCH 02/35] feat: support lexing and parsing relationship op --- packages/dbml-parse/src/core/lexer/lexer.ts | 39 ++++++-- packages/dbml-parse/src/core/parser/parser.ts | 96 +++++++++++++++++++ 2 files changed, 128 insertions(+), 7 deletions(-) diff --git a/packages/dbml-parse/src/core/lexer/lexer.ts b/packages/dbml-parse/src/core/lexer/lexer.ts index a12c70990..49d79f964 100644 --- a/packages/dbml-parse/src/core/lexer/lexer.ts +++ b/packages/dbml-parse/src/core/lexer/lexer.ts @@ -386,19 +386,44 @@ export default class Lexer { operator (c: string) { switch (c) { case '<': - if ([ - '>', - '=', - ].includes(this.peek()!)) this.advance(); // <, >, <= + if (this.peek() === '=') { + this.advance(); // <= + } else if (this.peek() === '>') { + this.advance(); // <> + if (this.peek() === '?') this.advance(); // <>? + } else if (this.peek() === '?') { + this.advance(); // ': - if (this.peek() === '=') this.advance(); // >, >= + if (this.peek() === '=') this.advance(); // >= + else if (this.peek() === '?') this.advance(); // >? + break; + case '-': + if (this.peek() === '?') this.advance(); // -? + break; + case '?': + if (this.peek() === '-') { + this.advance(); // ?- + if (this.peek() === '?') this.advance(); // ?-? + } else if (this.peek() === '<') { + this.advance(); // ?< + if (this.peek() === '>') { + this.advance(); // ?<> + if (this.peek() === '?') this.advance(); // ?<>? + } else if (this.peek() === '?') { + this.advance(); // ?') { + this.advance(); // ?> + if (this.peek() === '?') this.advance(); // ?>? + } break; case '=': - if (this.peek() === '=') this.advance(); // =, == + if (this.peek() === '=') this.advance(); // == break; case '!': - if (this.peek() === '=') this.advance(); // !, != + if (this.peek() === '=') this.advance(); // != break; default: break; diff --git a/packages/dbml-parse/src/core/parser/parser.ts b/packages/dbml-parse/src/core/parser/parser.ts index 20acd45a1..54dff968c 100644 --- a/packages/dbml-parse/src/core/parser/parser.ts +++ b/packages/dbml-parse/src/core/parser/parser.ts @@ -1545,6 +1545,54 @@ const infixBindingPowerMap: { left: 7, right: 8, }, + '-?': { + left: 9, + right: 10, + }, + '?-': { + left: 9, + right: 10, + }, + '?-?': { + left: 9, + right: 10, + }, + '?>': { + left: 7, + right: 8, + }, + '>?': { + left: 7, + right: 8, + }, + '?<': { + left: 7, + right: 8, + }, + '?': { + left: 7, + right: 8, + }, + '?': { + left: 7, + right: 8, + }, + '<>?': { + left: 7, + right: 8, + }, + '?<>?': { + left: 7, + right: 8, + }, '=': { left: 2, right: 3, @@ -1600,6 +1648,54 @@ const prefixBindingPowerMap: { left: null, right: 15, }, + '-?': { + left: null, + right: 15, + }, + '?-': { + left: null, + right: 15, + }, + '?-?': { + left: null, + right: 15, + }, + '?>': { + left: null, + right: 15, + }, + '>?': { + left: null, + right: 15, + }, + '?<': { + left: null, + right: 15, + }, + '?': { + left: null, + right: 15, + }, + '?': { + left: null, + right: 15, + }, + '<>?': { + left: null, + right: 15, + }, + '?<>?': { + left: null, + right: 15, + }, '!': { left: null, right: 15, From cef0cbfd325c56fb4c7634a8edf5032c4d9f7fef Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 10:07:25 +0700 Subject: [PATCH 03/35] feat: support optional relationship op in isRelationshipOp --- packages/dbml-parse/src/core/utils/validate.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/dbml-parse/src/core/utils/validate.ts b/packages/dbml-parse/src/core/utils/validate.ts index d73fbb8ce..f682410e5 100644 --- a/packages/dbml-parse/src/core/utils/validate.ts +++ b/packages/dbml-parse/src/core/utils/validate.ts @@ -1,4 +1,5 @@ import { NUMERIC_LITERAL_PREFIX } from '@/constants'; +import { RELATIONSHIP_OPS } from '@/core/types/relation'; import { CompileError, CompileErrorCode } from '@/core/types/errors'; import { ArrayNode, @@ -85,7 +86,7 @@ export function isValidPartialInjection ( } export function isRelationshipOp (op?: string): boolean { - return op === '-' || op === '<>' || op === '>' || op === '<'; + return op !== undefined && (RELATIONSHIP_OPS as Set).has(op); } export function isValidColor (value?: SyntaxNode): boolean { From 14dbf4a79cb4a5b3e34b809ba43e28fa85d907e0 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 10:09:46 +0700 Subject: [PATCH 04/35] fix: add ? to the lex case of operators & add tests --- .../lexer/input/optional_ref.in.dbml | 4 + .../lexer/output/optional_ref.out.json | 157 + .../parser/input/optional_ref.in.dbml | 25 + .../parser/output/optional_ref.out.json | 2997 ++++++++++++++ .../validator/input/optional_ref.in.dbml | 26 + .../validator/output/optional_ref.out.json | 3479 +++++++++++++++++ packages/dbml-parse/src/core/types/tokens.ts | 1 + 7 files changed, 6689 insertions(+) create mode 100644 packages/dbml-parse/__tests__/snapshots/lexer/input/optional_ref.in.dbml create mode 100644 packages/dbml-parse/__tests__/snapshots/lexer/output/optional_ref.out.json create mode 100644 packages/dbml-parse/__tests__/snapshots/parser/input/optional_ref.in.dbml create mode 100644 packages/dbml-parse/__tests__/snapshots/parser/output/optional_ref.out.json create mode 100644 packages/dbml-parse/__tests__/snapshots/validator/input/optional_ref.in.dbml create mode 100644 packages/dbml-parse/__tests__/snapshots/validator/output/optional_ref.out.json diff --git a/packages/dbml-parse/__tests__/snapshots/lexer/input/optional_ref.in.dbml b/packages/dbml-parse/__tests__/snapshots/lexer/input/optional_ref.in.dbml new file mode 100644 index 000000000..8be4f7d2d --- /dev/null +++ b/packages/dbml-parse/__tests__/snapshots/lexer/input/optional_ref.in.dbml @@ -0,0 +1,4 @@ +- -? ?- ?-? +> >? ?> ?>? +< <>? ?<> ?<>? diff --git a/packages/dbml-parse/__tests__/snapshots/lexer/output/optional_ref.out.json b/packages/dbml-parse/__tests__/snapshots/lexer/output/optional_ref.out.json new file mode 100644 index 000000000..937ef66bd --- /dev/null +++ b/packages/dbml-parse/__tests__/snapshots/lexer/output/optional_ref.out.json @@ -0,0 +1,157 @@ +{ + "tokens": [ + { + "context": { + "id": "token@@-@[L0:C0, L0:C1]", + "snippet": "-" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "-" + }, + { + "context": { + "id": "token@@-?@[L0:C2, L0:C4]", + "snippet": "-?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "-?" + }, + { + "context": { + "id": "token@@?-@[L0:C5, L0:C7]", + "snippet": "?-" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?-" + }, + { + "context": { + "id": "token@@?-?@[L0:C8, L0:C11]", + "snippet": "?-?" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "?-?" + }, + { + "context": { + "id": "token@@>@[L1:C0, L1:C1]", + "snippet": ">" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ">" + }, + { + "context": { + "id": "token@@>?@[L1:C2, L1:C4]", + "snippet": ">?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ">?" + }, + { + "context": { + "id": "token@@?>@[L1:C5, L1:C7]", + "snippet": "?>" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?>" + }, + { + "context": { + "id": "token@@?>?@[L1:C8, L1:C11]", + "snippet": "?>?" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "?>?" + }, + { + "context": { + "id": "token@@<@[L2:C0, L2:C1]", + "snippet": "<" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "<" + }, + { + "context": { + "id": "token@@@?<@[L2:C5, L2:C7]", + "snippet": "?<" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?<" + }, + { + "context": { + "id": "token@@?@<>@[L3:C0, L3:C2]", + "snippet": "<>" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "<>" + }, + { + "context": { + "id": "token@@<>?@[L3:C3, L3:C6]", + "snippet": "<>?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "<>?" + }, + { + "context": { + "id": "token@@?<>@[L3:C7, L3:C10]", + "snippet": "?<>" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?<>" + }, + { + "context": { + "id": "token@@?<>?@[L3:C11, L3:C15]", + "snippet": "?<>?" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "?<>?" + }, + { + "context": { + "id": "token@@@[L4:C0, L4:C0]", + "snippet": "" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "" + } + ] +} \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/parser/input/optional_ref.in.dbml b/packages/dbml-parse/__tests__/snapshots/parser/input/optional_ref.in.dbml new file mode 100644 index 000000000..0e86213da --- /dev/null +++ b/packages/dbml-parse/__tests__/snapshots/parser/input/optional_ref.in.dbml @@ -0,0 +1,25 @@ +Table users { + id integer +} + +Table posts { + id integer + user_id integer + + Ref: user_id > users.id + Ref: user_id >? users.id + Ref: user_id ?> users.id + Ref: user_id ?>? users.id + Ref: user_id < users.id + Ref: user_id users.id + Ref: user_id <>? users.id + Ref: user_id ?<> users.id + Ref: user_id ?<>? users.id +} diff --git a/packages/dbml-parse/__tests__/snapshots/parser/output/optional_ref.out.json b/packages/dbml-parse/__tests__/snapshots/parser/output/optional_ref.out.json new file mode 100644 index 000000000..91a416f17 --- /dev/null +++ b/packages/dbml-parse/__tests__/snapshots/parser/output/optional_ref.out.json @@ -0,0 +1,2997 @@ +{ + "program": { + "context": { + "id": "node@@@[L0:C0, L25:C0]", + "snippet": "Table user...sers.id\n}\n" + }, + "children": { + "body": [ + { + "context": { + "id": "node@@users@[L0:C0, L2:C1]", + "snippet": "Table user... integer\n}" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L0:C12, L2:C1]", + "snippet": "{\n id integer\n}" + }, + "children": { + "blockCloseBrace": { + "context": { + "id": "token@@}@[L2:C0, L2:C1]", + "snippet": "}" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "}" + }, + "blockOpenBrace": { + "context": { + "id": "token@@{@[L0:C12, L0:C13]", + "snippet": "{" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "{" + }, + "body": [ + { + "context": { + "id": "node@@id@[L1:C4, L1:C14]", + "snippet": "id integer" + }, + "children": { + "args": [ + { + "context": { + "id": "node@@integer@[L1:C7, L1:C14]", + "snippet": "integer" + }, + "children": { + "expression": { + "context": { + "id": "node@@integer@[L1:C7, L1:C14]", + "snippet": "integer" + }, + "children": { + "variable": { + "context": { + "id": "token@@integer@[L1:C7, L1:C14]", + "snippet": "integer" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "integer" + } + }, + "fullEnd": 29, + "fullStart": 21 + } + }, + "fullEnd": 29, + "fullStart": 21 + } + ], + "callee": { + "context": { + "id": "node@@id@[L1:C4, L1:C6]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L1:C4, L1:C6]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L1:C4, L1:C6]", + "snippet": "id" + }, + "leadingTrivia": " ", + "trailingTrivia": " ", + "value": "id" + } + }, + "fullEnd": 21, + "fullStart": 14 + } + }, + "fullEnd": 21, + "fullStart": 14 + } + }, + "fullEnd": 29, + "fullStart": 14 + } + ] + }, + "fullEnd": 31, + "fullStart": 12 + }, + "name": { + "context": { + "id": "node@@users@[L0:C6, L0:C11]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L0:C6, L0:C11]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L0:C6, L0:C11]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "users" + } + }, + "fullEnd": 12, + "fullStart": 6 + } + }, + "fullEnd": 12, + "fullStart": 6 + }, + "type": { + "context": { + "id": "token@@Table@[L0:C0, L0:C5]", + "snippet": "Table" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "Table" + } + }, + "fullEnd": 31, + "fullStart": 0 + }, + { + "context": { + "id": "node@@posts@[L4:C0, L24:C1]", + "snippet": "Table post...users.id\n}" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L4:C12, L24:C1]", + "snippet": "{\n id i...users.id\n}" + }, + "children": { + "blockCloseBrace": { + "context": { + "id": "token@@}@[L24:C0, L24:C1]", + "snippet": "}" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "}" + }, + "blockOpenBrace": { + "context": { + "id": "token@@{@[L4:C12, L4:C13]", + "snippet": "{" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "{" + }, + "body": [ + { + "context": { + "id": "node@@id@[L5:C4, L5:C14]", + "snippet": "id integer" + }, + "children": { + "args": [ + { + "context": { + "id": "node@@integer@[L5:C7, L5:C14]", + "snippet": "integer" + }, + "children": { + "expression": { + "context": { + "id": "node@@integer@[L5:C7, L5:C14]", + "snippet": "integer" + }, + "children": { + "variable": { + "context": { + "id": "token@@integer@[L5:C7, L5:C14]", + "snippet": "integer" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "integer" + } + }, + "fullEnd": 61, + "fullStart": 53 + } + }, + "fullEnd": 61, + "fullStart": 53 + } + ], + "callee": { + "context": { + "id": "node@@id@[L5:C4, L5:C6]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L5:C4, L5:C6]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L5:C4, L5:C6]", + "snippet": "id" + }, + "leadingTrivia": " ", + "trailingTrivia": " ", + "value": "id" + } + }, + "fullEnd": 53, + "fullStart": 46 + } + }, + "fullEnd": 53, + "fullStart": 46 + } + }, + "fullEnd": 61, + "fullStart": 46 + }, + { + "context": { + "id": "node@@user_id@[L6:C4, L6:C19]", + "snippet": "user_id integer" + }, + "children": { + "args": [ + { + "context": { + "id": "node@@integer@[L6:C12, L6:C19]", + "snippet": "integer" + }, + "children": { + "expression": { + "context": { + "id": "node@@integer@[L6:C12, L6:C19]", + "snippet": "integer" + }, + "children": { + "variable": { + "context": { + "id": "token@@integer@[L6:C12, L6:C19]", + "snippet": "integer" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "integer" + } + }, + "fullEnd": 81, + "fullStart": 73 + } + }, + "fullEnd": 81, + "fullStart": 73 + } + ], + "callee": { + "context": { + "id": "node@@user_id@[L6:C4, L6:C11]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L6:C4, L6:C11]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L6:C4, L6:C11]", + "snippet": "user_id" + }, + "leadingTrivia": " ", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 73, + "fullStart": 61 + } + }, + "fullEnd": 73, + "fullStart": 61 + } + }, + "fullEnd": 81, + "fullStart": 61 + }, + { + "context": { + "id": "node@@@[L8:C4, L8:C27]", + "snippet": "Ref: user_...> users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L8:C9, L8:C27]", + "snippet": "user_id > users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L8:C9, L8:C27]", + "snippet": "user_id > users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L8:C9, L8:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L8:C9, L8:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L8:C9, L8:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 99, + "fullStart": 91 + } + }, + "fullEnd": 99, + "fullStart": 91 + }, + "op": { + "context": { + "id": "token@@>@[L8:C17, L8:C18]", + "snippet": ">" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ">" + }, + "rightExpression": { + "context": { + "id": "node@@@[L8:C19, L8:C27]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L8:C19, L8:C24]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L8:C19, L8:C24]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L8:C19, L8:C24]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 106, + "fullStart": 101 + } + }, + "fullEnd": 106, + "fullStart": 101 + }, + "op": { + "context": { + "id": "token@@.@[L8:C24, L8:C25]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L8:C25, L8:C27]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L8:C25, L8:C27]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L8:C25, L8:C27]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 110, + "fullStart": 107 + } + }, + "fullEnd": 110, + "fullStart": 107 + } + }, + "fullEnd": 110, + "fullStart": 101 + } + }, + "fullEnd": 110, + "fullStart": 91 + } + }, + "fullEnd": 110, + "fullStart": 91 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L8:C7, L8:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L8:C4, L8:C7]", + "snippet": "Ref" + }, + "leadingTrivia": "\n ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 110, + "fullStart": 81 + }, + { + "context": { + "id": "node@@@[L9:C4, L9:C28]", + "snippet": "Ref: user_...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L9:C9, L9:C28]", + "snippet": "user_id >? users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L9:C9, L9:C28]", + "snippet": "user_id >? users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L9:C9, L9:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L9:C9, L9:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L9:C9, L9:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 127, + "fullStart": 119 + } + }, + "fullEnd": 127, + "fullStart": 119 + }, + "op": { + "context": { + "id": "token@@>?@[L9:C17, L9:C19]", + "snippet": ">?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ">?" + }, + "rightExpression": { + "context": { + "id": "node@@@[L9:C20, L9:C28]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L9:C20, L9:C25]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L9:C20, L9:C25]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L9:C20, L9:C25]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 135, + "fullStart": 130 + } + }, + "fullEnd": 135, + "fullStart": 130 + }, + "op": { + "context": { + "id": "token@@.@[L9:C25, L9:C26]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L9:C26, L9:C28]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L9:C26, L9:C28]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L9:C26, L9:C28]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 139, + "fullStart": 136 + } + }, + "fullEnd": 139, + "fullStart": 136 + } + }, + "fullEnd": 139, + "fullStart": 130 + } + }, + "fullEnd": 139, + "fullStart": 119 + } + }, + "fullEnd": 139, + "fullStart": 119 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L9:C7, L9:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L9:C4, L9:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 139, + "fullStart": 110 + }, + { + "context": { + "id": "node@@@[L10:C4, L10:C28]", + "snippet": "Ref: user_...> users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L10:C9, L10:C28]", + "snippet": "user_id ?> users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L10:C9, L10:C28]", + "snippet": "user_id ?> users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L10:C9, L10:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L10:C9, L10:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L10:C9, L10:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 156, + "fullStart": 148 + } + }, + "fullEnd": 156, + "fullStart": 148 + }, + "op": { + "context": { + "id": "token@@?>@[L10:C17, L10:C19]", + "snippet": "?>" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?>" + }, + "rightExpression": { + "context": { + "id": "node@@@[L10:C20, L10:C28]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L10:C20, L10:C25]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L10:C20, L10:C25]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L10:C20, L10:C25]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 164, + "fullStart": 159 + } + }, + "fullEnd": 164, + "fullStart": 159 + }, + "op": { + "context": { + "id": "token@@.@[L10:C25, L10:C26]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L10:C26, L10:C28]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L10:C26, L10:C28]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L10:C26, L10:C28]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 168, + "fullStart": 165 + } + }, + "fullEnd": 168, + "fullStart": 165 + } + }, + "fullEnd": 168, + "fullStart": 159 + } + }, + "fullEnd": 168, + "fullStart": 148 + } + }, + "fullEnd": 168, + "fullStart": 148 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L10:C7, L10:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L10:C4, L10:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 168, + "fullStart": 139 + }, + { + "context": { + "id": "node@@@[L11:C4, L11:C29]", + "snippet": "Ref: user_...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L11:C9, L11:C29]", + "snippet": "user_id ?>? users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L11:C9, L11:C29]", + "snippet": "user_id ?>? users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L11:C9, L11:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L11:C9, L11:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L11:C9, L11:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 185, + "fullStart": 177 + } + }, + "fullEnd": 185, + "fullStart": 177 + }, + "op": { + "context": { + "id": "token@@?>?@[L11:C17, L11:C20]", + "snippet": "?>?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?>?" + }, + "rightExpression": { + "context": { + "id": "node@@@[L11:C21, L11:C29]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L11:C21, L11:C26]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L11:C21, L11:C26]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L11:C21, L11:C26]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 194, + "fullStart": 189 + } + }, + "fullEnd": 194, + "fullStart": 189 + }, + "op": { + "context": { + "id": "token@@.@[L11:C26, L11:C27]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L11:C27, L11:C29]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L11:C27, L11:C29]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L11:C27, L11:C29]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 198, + "fullStart": 195 + } + }, + "fullEnd": 198, + "fullStart": 195 + } + }, + "fullEnd": 198, + "fullStart": 189 + } + }, + "fullEnd": 198, + "fullStart": 177 + } + }, + "fullEnd": 198, + "fullStart": 177 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L11:C7, L11:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L11:C4, L11:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 198, + "fullStart": 168 + }, + { + "context": { + "id": "node@@@[L12:C4, L12:C27]", + "snippet": "Ref: user_...< users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L12:C9, L12:C27]", + "snippet": "user_id < users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L12:C9, L12:C27]", + "snippet": "user_id < users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L12:C9, L12:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L12:C9, L12:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L12:C9, L12:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 215, + "fullStart": 207 + } + }, + "fullEnd": 215, + "fullStart": 207 + }, + "op": { + "context": { + "id": "token@@<@[L12:C17, L12:C18]", + "snippet": "<" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "<" + }, + "rightExpression": { + "context": { + "id": "node@@@[L12:C19, L12:C27]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L12:C19, L12:C24]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L12:C19, L12:C24]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L12:C19, L12:C24]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 222, + "fullStart": 217 + } + }, + "fullEnd": 222, + "fullStart": 217 + }, + "op": { + "context": { + "id": "token@@.@[L12:C24, L12:C25]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L12:C25, L12:C27]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L12:C25, L12:C27]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L12:C25, L12:C27]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 226, + "fullStart": 223 + } + }, + "fullEnd": 226, + "fullStart": 223 + } + }, + "fullEnd": 226, + "fullStart": 217 + } + }, + "fullEnd": 226, + "fullStart": 207 + } + }, + "fullEnd": 226, + "fullStart": 207 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L12:C7, L12:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L12:C4, L12:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 226, + "fullStart": 198 + }, + { + "context": { + "id": "node@@@[L13:C4, L13:C28]", + "snippet": "Ref: user_...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L13:C9, L13:C28]", + "snippet": "user_id @@[L13:C9, L13:C28]", + "snippet": "user_id @user_id@[L13:C9, L13:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L13:C9, L13:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L13:C9, L13:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 243, + "fullStart": 235 + } + }, + "fullEnd": 243, + "fullStart": 235 + }, + "op": { + "context": { + "id": "token@@@@[L13:C20, L13:C28]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L13:C20, L13:C25]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L13:C20, L13:C25]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L13:C20, L13:C25]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 251, + "fullStart": 246 + } + }, + "fullEnd": 251, + "fullStart": 246 + }, + "op": { + "context": { + "id": "token@@.@[L13:C25, L13:C26]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L13:C26, L13:C28]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L13:C26, L13:C28]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L13:C26, L13:C28]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 255, + "fullStart": 252 + } + }, + "fullEnd": 255, + "fullStart": 252 + } + }, + "fullEnd": 255, + "fullStart": 246 + } + }, + "fullEnd": 255, + "fullStart": 235 + } + }, + "fullEnd": 255, + "fullStart": 235 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L13:C7, L13:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L13:C4, L13:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 255, + "fullStart": 226 + }, + { + "context": { + "id": "node@@@[L14:C4, L14:C28]", + "snippet": "Ref: user_...< users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L14:C9, L14:C28]", + "snippet": "user_id ?< users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L14:C9, L14:C28]", + "snippet": "user_id ?< users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L14:C9, L14:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L14:C9, L14:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L14:C9, L14:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 272, + "fullStart": 264 + } + }, + "fullEnd": 272, + "fullStart": 264 + }, + "op": { + "context": { + "id": "token@@?<@[L14:C17, L14:C19]", + "snippet": "?<" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?<" + }, + "rightExpression": { + "context": { + "id": "node@@@[L14:C20, L14:C28]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L14:C20, L14:C25]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L14:C20, L14:C25]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L14:C20, L14:C25]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 280, + "fullStart": 275 + } + }, + "fullEnd": 280, + "fullStart": 275 + }, + "op": { + "context": { + "id": "token@@.@[L14:C25, L14:C26]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L14:C26, L14:C28]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L14:C26, L14:C28]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L14:C26, L14:C28]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 284, + "fullStart": 281 + } + }, + "fullEnd": 284, + "fullStart": 281 + } + }, + "fullEnd": 284, + "fullStart": 275 + } + }, + "fullEnd": 284, + "fullStart": 264 + } + }, + "fullEnd": 284, + "fullStart": 264 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L14:C7, L14:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L14:C4, L14:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 284, + "fullStart": 255 + }, + { + "context": { + "id": "node@@@[L15:C4, L15:C29]", + "snippet": "Ref: user_...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L15:C9, L15:C29]", + "snippet": "user_id ?@@[L15:C9, L15:C29]", + "snippet": "user_id ?@user_id@[L15:C9, L15:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L15:C9, L15:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L15:C9, L15:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 301, + "fullStart": 293 + } + }, + "fullEnd": 301, + "fullStart": 293 + }, + "op": { + "context": { + "id": "token@@?@@[L15:C21, L15:C29]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L15:C21, L15:C26]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L15:C21, L15:C26]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L15:C21, L15:C26]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 310, + "fullStart": 305 + } + }, + "fullEnd": 310, + "fullStart": 305 + }, + "op": { + "context": { + "id": "token@@.@[L15:C26, L15:C27]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L15:C27, L15:C29]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L15:C27, L15:C29]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L15:C27, L15:C29]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 314, + "fullStart": 311 + } + }, + "fullEnd": 314, + "fullStart": 311 + } + }, + "fullEnd": 314, + "fullStart": 305 + } + }, + "fullEnd": 314, + "fullStart": 293 + } + }, + "fullEnd": 314, + "fullStart": 293 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L15:C7, L15:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L15:C4, L15:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 314, + "fullStart": 284 + }, + { + "context": { + "id": "node@@@[L16:C4, L16:C27]", + "snippet": "Ref: user_...- users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L16:C9, L16:C27]", + "snippet": "user_id - users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L16:C9, L16:C27]", + "snippet": "user_id - users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L16:C9, L16:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L16:C9, L16:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L16:C9, L16:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 331, + "fullStart": 323 + } + }, + "fullEnd": 331, + "fullStart": 323 + }, + "op": { + "context": { + "id": "token@@-@[L16:C17, L16:C18]", + "snippet": "-" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "-" + }, + "rightExpression": { + "context": { + "id": "node@@@[L16:C19, L16:C27]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L16:C19, L16:C24]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L16:C19, L16:C24]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L16:C19, L16:C24]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 338, + "fullStart": 333 + } + }, + "fullEnd": 338, + "fullStart": 333 + }, + "op": { + "context": { + "id": "token@@.@[L16:C24, L16:C25]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L16:C25, L16:C27]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L16:C25, L16:C27]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L16:C25, L16:C27]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 342, + "fullStart": 339 + } + }, + "fullEnd": 342, + "fullStart": 339 + } + }, + "fullEnd": 342, + "fullStart": 333 + } + }, + "fullEnd": 342, + "fullStart": 323 + } + }, + "fullEnd": 342, + "fullStart": 323 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L16:C7, L16:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L16:C4, L16:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 342, + "fullStart": 314 + }, + { + "context": { + "id": "node@@@[L17:C4, L17:C28]", + "snippet": "Ref: user_...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L17:C9, L17:C28]", + "snippet": "user_id -? users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L17:C9, L17:C28]", + "snippet": "user_id -? users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L17:C9, L17:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L17:C9, L17:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L17:C9, L17:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 359, + "fullStart": 351 + } + }, + "fullEnd": 359, + "fullStart": 351 + }, + "op": { + "context": { + "id": "token@@-?@[L17:C17, L17:C19]", + "snippet": "-?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "-?" + }, + "rightExpression": { + "context": { + "id": "node@@@[L17:C20, L17:C28]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L17:C20, L17:C25]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L17:C20, L17:C25]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L17:C20, L17:C25]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 367, + "fullStart": 362 + } + }, + "fullEnd": 367, + "fullStart": 362 + }, + "op": { + "context": { + "id": "token@@.@[L17:C25, L17:C26]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L17:C26, L17:C28]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L17:C26, L17:C28]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L17:C26, L17:C28]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 371, + "fullStart": 368 + } + }, + "fullEnd": 371, + "fullStart": 368 + } + }, + "fullEnd": 371, + "fullStart": 362 + } + }, + "fullEnd": 371, + "fullStart": 351 + } + }, + "fullEnd": 371, + "fullStart": 351 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L17:C7, L17:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L17:C4, L17:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 371, + "fullStart": 342 + }, + { + "context": { + "id": "node@@@[L18:C4, L18:C28]", + "snippet": "Ref: user_...- users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L18:C9, L18:C28]", + "snippet": "user_id ?- users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L18:C9, L18:C28]", + "snippet": "user_id ?- users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L18:C9, L18:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L18:C9, L18:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L18:C9, L18:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 388, + "fullStart": 380 + } + }, + "fullEnd": 388, + "fullStart": 380 + }, + "op": { + "context": { + "id": "token@@?-@[L18:C17, L18:C19]", + "snippet": "?-" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?-" + }, + "rightExpression": { + "context": { + "id": "node@@@[L18:C20, L18:C28]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L18:C20, L18:C25]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L18:C20, L18:C25]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L18:C20, L18:C25]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 396, + "fullStart": 391 + } + }, + "fullEnd": 396, + "fullStart": 391 + }, + "op": { + "context": { + "id": "token@@.@[L18:C25, L18:C26]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L18:C26, L18:C28]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L18:C26, L18:C28]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L18:C26, L18:C28]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 400, + "fullStart": 397 + } + }, + "fullEnd": 400, + "fullStart": 397 + } + }, + "fullEnd": 400, + "fullStart": 391 + } + }, + "fullEnd": 400, + "fullStart": 380 + } + }, + "fullEnd": 400, + "fullStart": 380 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L18:C7, L18:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L18:C4, L18:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 400, + "fullStart": 371 + }, + { + "context": { + "id": "node@@@[L19:C4, L19:C29]", + "snippet": "Ref: user_...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L19:C9, L19:C29]", + "snippet": "user_id ?-? users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L19:C9, L19:C29]", + "snippet": "user_id ?-? users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L19:C9, L19:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L19:C9, L19:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L19:C9, L19:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 417, + "fullStart": 409 + } + }, + "fullEnd": 417, + "fullStart": 409 + }, + "op": { + "context": { + "id": "token@@?-?@[L19:C17, L19:C20]", + "snippet": "?-?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?-?" + }, + "rightExpression": { + "context": { + "id": "node@@@[L19:C21, L19:C29]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L19:C21, L19:C26]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L19:C21, L19:C26]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L19:C21, L19:C26]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 426, + "fullStart": 421 + } + }, + "fullEnd": 426, + "fullStart": 421 + }, + "op": { + "context": { + "id": "token@@.@[L19:C26, L19:C27]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L19:C27, L19:C29]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L19:C27, L19:C29]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L19:C27, L19:C29]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 430, + "fullStart": 427 + } + }, + "fullEnd": 430, + "fullStart": 427 + } + }, + "fullEnd": 430, + "fullStart": 421 + } + }, + "fullEnd": 430, + "fullStart": 409 + } + }, + "fullEnd": 430, + "fullStart": 409 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L19:C7, L19:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L19:C4, L19:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 430, + "fullStart": 400 + }, + { + "context": { + "id": "node@@@[L20:C4, L20:C28]", + "snippet": "Ref: user_...> users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L20:C9, L20:C28]", + "snippet": "user_id <> users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L20:C9, L20:C28]", + "snippet": "user_id <> users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L20:C9, L20:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L20:C9, L20:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L20:C9, L20:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 447, + "fullStart": 439 + } + }, + "fullEnd": 447, + "fullStart": 439 + }, + "op": { + "context": { + "id": "token@@<>@[L20:C17, L20:C19]", + "snippet": "<>" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "<>" + }, + "rightExpression": { + "context": { + "id": "node@@@[L20:C20, L20:C28]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L20:C20, L20:C25]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L20:C20, L20:C25]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L20:C20, L20:C25]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 455, + "fullStart": 450 + } + }, + "fullEnd": 455, + "fullStart": 450 + }, + "op": { + "context": { + "id": "token@@.@[L20:C25, L20:C26]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L20:C26, L20:C28]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L20:C26, L20:C28]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L20:C26, L20:C28]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 459, + "fullStart": 456 + } + }, + "fullEnd": 459, + "fullStart": 456 + } + }, + "fullEnd": 459, + "fullStart": 450 + } + }, + "fullEnd": 459, + "fullStart": 439 + } + }, + "fullEnd": 459, + "fullStart": 439 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L20:C7, L20:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L20:C4, L20:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 459, + "fullStart": 430 + }, + { + "context": { + "id": "node@@@[L21:C4, L21:C29]", + "snippet": "Ref: user_...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L21:C9, L21:C29]", + "snippet": "user_id <>? users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L21:C9, L21:C29]", + "snippet": "user_id <>? users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L21:C9, L21:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L21:C9, L21:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L21:C9, L21:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 476, + "fullStart": 468 + } + }, + "fullEnd": 476, + "fullStart": 468 + }, + "op": { + "context": { + "id": "token@@<>?@[L21:C17, L21:C20]", + "snippet": "<>?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "<>?" + }, + "rightExpression": { + "context": { + "id": "node@@@[L21:C21, L21:C29]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L21:C21, L21:C26]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L21:C21, L21:C26]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L21:C21, L21:C26]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 485, + "fullStart": 480 + } + }, + "fullEnd": 485, + "fullStart": 480 + }, + "op": { + "context": { + "id": "token@@.@[L21:C26, L21:C27]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L21:C27, L21:C29]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L21:C27, L21:C29]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L21:C27, L21:C29]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 489, + "fullStart": 486 + } + }, + "fullEnd": 489, + "fullStart": 486 + } + }, + "fullEnd": 489, + "fullStart": 480 + } + }, + "fullEnd": 489, + "fullStart": 468 + } + }, + "fullEnd": 489, + "fullStart": 468 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L21:C7, L21:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L21:C4, L21:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 489, + "fullStart": 459 + }, + { + "context": { + "id": "node@@@[L22:C4, L22:C29]", + "snippet": "Ref: user_...> users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L22:C9, L22:C29]", + "snippet": "user_id ?<> users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L22:C9, L22:C29]", + "snippet": "user_id ?<> users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L22:C9, L22:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L22:C9, L22:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L22:C9, L22:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 506, + "fullStart": 498 + } + }, + "fullEnd": 506, + "fullStart": 498 + }, + "op": { + "context": { + "id": "token@@?<>@[L22:C17, L22:C20]", + "snippet": "?<>" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?<>" + }, + "rightExpression": { + "context": { + "id": "node@@@[L22:C21, L22:C29]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L22:C21, L22:C26]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L22:C21, L22:C26]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L22:C21, L22:C26]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 515, + "fullStart": 510 + } + }, + "fullEnd": 515, + "fullStart": 510 + }, + "op": { + "context": { + "id": "token@@.@[L22:C26, L22:C27]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L22:C27, L22:C29]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L22:C27, L22:C29]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L22:C27, L22:C29]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 519, + "fullStart": 516 + } + }, + "fullEnd": 519, + "fullStart": 516 + } + }, + "fullEnd": 519, + "fullStart": 510 + } + }, + "fullEnd": 519, + "fullStart": 498 + } + }, + "fullEnd": 519, + "fullStart": 498 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L22:C7, L22:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L22:C4, L22:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 519, + "fullStart": 489 + }, + { + "context": { + "id": "node@@@[L23:C4, L23:C30]", + "snippet": "Ref: user_...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L23:C9, L23:C30]", + "snippet": "user_id ?<...? users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L23:C9, L23:C30]", + "snippet": "user_id ?<...? users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L23:C9, L23:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L23:C9, L23:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L23:C9, L23:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 536, + "fullStart": 528 + } + }, + "fullEnd": 536, + "fullStart": 528 + }, + "op": { + "context": { + "id": "token@@?<>?@[L23:C17, L23:C21]", + "snippet": "?<>?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?<>?" + }, + "rightExpression": { + "context": { + "id": "node@@@[L23:C22, L23:C30]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L23:C22, L23:C27]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L23:C22, L23:C27]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L23:C22, L23:C27]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 546, + "fullStart": 541 + } + }, + "fullEnd": 546, + "fullStart": 541 + }, + "op": { + "context": { + "id": "token@@.@[L23:C27, L23:C28]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L23:C28, L23:C30]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L23:C28, L23:C30]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L23:C28, L23:C30]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 550, + "fullStart": 547 + } + }, + "fullEnd": 550, + "fullStart": 547 + } + }, + "fullEnd": 550, + "fullStart": 541 + } + }, + "fullEnd": 550, + "fullStart": 528 + } + }, + "fullEnd": 550, + "fullStart": 528 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L23:C7, L23:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L23:C4, L23:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 550, + "fullStart": 519 + } + ] + }, + "fullEnd": 552, + "fullStart": 44 + }, + "name": { + "context": { + "id": "node@@posts@[L4:C6, L4:C11]", + "snippet": "posts" + }, + "children": { + "expression": { + "context": { + "id": "node@@posts@[L4:C6, L4:C11]", + "snippet": "posts" + }, + "children": { + "variable": { + "context": { + "id": "token@@posts@[L4:C6, L4:C11]", + "snippet": "posts" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "posts" + } + }, + "fullEnd": 44, + "fullStart": 38 + } + }, + "fullEnd": 44, + "fullStart": 38 + }, + "type": { + "context": { + "id": "token@@Table@[L4:C0, L4:C5]", + "snippet": "Table" + }, + "leadingTrivia": "\n", + "trailingTrivia": " ", + "value": "Table" + } + }, + "fullEnd": 552, + "fullStart": 31 + } + ], + "eof": { + "context": { + "id": "token@@@[L25:C0, L25:C0]", + "snippet": "" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "" + } + }, + "fullEnd": 552, + "fullStart": 0 + } +} \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/validator/input/optional_ref.in.dbml b/packages/dbml-parse/__tests__/snapshots/validator/input/optional_ref.in.dbml new file mode 100644 index 000000000..9905c5f23 --- /dev/null +++ b/packages/dbml-parse/__tests__/snapshots/validator/input/optional_ref.in.dbml @@ -0,0 +1,26 @@ +Table users { + id integer +} + +Table posts { + id integer + user_id integer + + Ref: user_id > users.id + Ref: user_id >? users.id + Ref: user_id ?> users.id + Ref: user_id ?>? users.id + Ref: user_id - users.id + Ref: user_id -? users.id + Ref: user_id ?- users.id + Ref: user_id ?-? users.id + Ref: user_id <> users.id + Ref: user_id <>? users.id + Ref: user_id ?<> users.id + Ref: user_id ?<>? users.id +} + +Ref: posts.user_id < users.id +Ref: posts.user_id @@[L8:C4, L8:C27]", + "snippet": "Ref: user_...> users.id" + } + } + }, + { + "code": "INVALID_REF_FIELD", + "diagnostic": "Invalid column reference", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@user_id@[L8:C9, L8:C16]", + "snippet": "user_id" + } + } + }, + { + "code": "INVALID_REF_CONTEXT", + "diagnostic": "A Ref must appear top-level", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L9:C4, L9:C28]", + "snippet": "Ref: user_...? users.id" + } + } + }, + { + "code": "INVALID_REF_FIELD", + "diagnostic": "Invalid column reference", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@user_id@[L9:C9, L9:C16]", + "snippet": "user_id" + } + } + }, + { + "code": "INVALID_REF_CONTEXT", + "diagnostic": "A Ref must appear top-level", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L10:C4, L10:C28]", + "snippet": "Ref: user_...> users.id" + } + } + }, + { + "code": "INVALID_REF_FIELD", + "diagnostic": "Invalid column reference", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@user_id@[L10:C9, L10:C16]", + "snippet": "user_id" + } + } + }, + { + "code": "INVALID_REF_CONTEXT", + "diagnostic": "A Ref must appear top-level", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L11:C4, L11:C29]", + "snippet": "Ref: user_...? users.id" + } + } + }, + { + "code": "INVALID_REF_FIELD", + "diagnostic": "Invalid column reference", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@user_id@[L11:C9, L11:C16]", + "snippet": "user_id" + } + } + }, + { + "code": "INVALID_REF_CONTEXT", + "diagnostic": "A Ref must appear top-level", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L12:C4, L12:C27]", + "snippet": "Ref: user_...- users.id" + } + } + }, + { + "code": "INVALID_REF_FIELD", + "diagnostic": "Invalid column reference", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@user_id@[L12:C9, L12:C16]", + "snippet": "user_id" + } + } + }, + { + "code": "INVALID_REF_CONTEXT", + "diagnostic": "A Ref must appear top-level", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L13:C4, L13:C28]", + "snippet": "Ref: user_...? users.id" + } + } + }, + { + "code": "INVALID_REF_FIELD", + "diagnostic": "Invalid column reference", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@user_id@[L13:C9, L13:C16]", + "snippet": "user_id" + } + } + }, + { + "code": "INVALID_REF_CONTEXT", + "diagnostic": "A Ref must appear top-level", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L14:C4, L14:C28]", + "snippet": "Ref: user_...- users.id" + } + } + }, + { + "code": "INVALID_REF_FIELD", + "diagnostic": "Invalid column reference", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@user_id@[L14:C9, L14:C16]", + "snippet": "user_id" + } + } + }, + { + "code": "INVALID_REF_CONTEXT", + "diagnostic": "A Ref must appear top-level", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L15:C4, L15:C29]", + "snippet": "Ref: user_...? users.id" + } + } + }, + { + "code": "INVALID_REF_FIELD", + "diagnostic": "Invalid column reference", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@user_id@[L15:C9, L15:C16]", + "snippet": "user_id" + } + } + }, + { + "code": "INVALID_REF_CONTEXT", + "diagnostic": "A Ref must appear top-level", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L16:C4, L16:C28]", + "snippet": "Ref: user_...> users.id" + } + } + }, + { + "code": "INVALID_REF_FIELD", + "diagnostic": "Invalid column reference", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@user_id@[L16:C9, L16:C16]", + "snippet": "user_id" + } + } + }, + { + "code": "INVALID_REF_CONTEXT", + "diagnostic": "A Ref must appear top-level", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L17:C4, L17:C29]", + "snippet": "Ref: user_...? users.id" + } + } + }, + { + "code": "INVALID_REF_FIELD", + "diagnostic": "Invalid column reference", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@user_id@[L17:C9, L17:C16]", + "snippet": "user_id" + } + } + }, + { + "code": "INVALID_REF_CONTEXT", + "diagnostic": "A Ref must appear top-level", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L18:C4, L18:C29]", + "snippet": "Ref: user_...> users.id" + } + } + }, + { + "code": "INVALID_REF_FIELD", + "diagnostic": "Invalid column reference", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@user_id@[L18:C9, L18:C16]", + "snippet": "user_id" + } + } + }, + { + "code": "INVALID_REF_CONTEXT", + "diagnostic": "A Ref must appear top-level", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L19:C4, L19:C30]", + "snippet": "Ref: user_...? users.id" + } + } + }, + { + "code": "INVALID_REF_FIELD", + "diagnostic": "Invalid column reference", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@user_id@[L19:C9, L19:C16]", + "snippet": "user_id" + } + } + } + ], + "program": { + "context": { + "id": "node@@@[L0:C0, L26:C0]", + "snippet": "Table user... users.id\n" + }, + "children": { + "body": [ + { + "context": { + "id": "node@@users@[L0:C0, L2:C1]", + "snippet": "Table user... integer\n}" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L0:C12, L2:C1]", + "snippet": "{\n id integer\n}" + }, + "children": { + "blockCloseBrace": { + "context": { + "id": "token@@}@[L2:C0, L2:C1]", + "snippet": "}" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "}" + }, + "blockOpenBrace": { + "context": { + "id": "token@@{@[L0:C12, L0:C13]", + "snippet": "{" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "{" + }, + "body": [ + { + "context": { + "id": "node@@id@[L1:C4, L1:C14]", + "snippet": "id integer" + }, + "children": { + "args": [ + { + "context": { + "id": "node@@integer@[L1:C7, L1:C14]", + "snippet": "integer" + }, + "children": { + "expression": { + "context": { + "id": "node@@integer@[L1:C7, L1:C14]", + "snippet": "integer" + }, + "children": { + "variable": { + "context": { + "id": "token@@integer@[L1:C7, L1:C14]", + "snippet": "integer" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "integer" + } + }, + "fullEnd": 29, + "fullStart": 21 + } + }, + "fullEnd": 29, + "fullStart": 21 + } + ], + "callee": { + "context": { + "id": "node@@id@[L1:C4, L1:C6]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L1:C4, L1:C6]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L1:C4, L1:C6]", + "snippet": "id" + }, + "leadingTrivia": " ", + "trailingTrivia": " ", + "value": "id" + } + }, + "fullEnd": 21, + "fullStart": 14 + } + }, + "fullEnd": 21, + "fullStart": 14 + } + }, + "fullEnd": 29, + "fullStart": 14 + } + ] + }, + "fullEnd": 31, + "fullStart": 12 + }, + "name": { + "context": { + "id": "node@@users@[L0:C6, L0:C11]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L0:C6, L0:C11]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L0:C6, L0:C11]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "users" + } + }, + "fullEnd": 12, + "fullStart": 6 + } + }, + "fullEnd": 12, + "fullStart": 6 + }, + "type": { + "context": { + "id": "token@@Table@[L0:C0, L0:C5]", + "snippet": "Table" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "Table" + } + }, + "fullEnd": 31, + "fullStart": 0 + }, + { + "context": { + "id": "node@@posts@[L4:C0, L20:C1]", + "snippet": "Table post...users.id\n}" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L4:C12, L20:C1]", + "snippet": "{\n id i...users.id\n}" + }, + "children": { + "blockCloseBrace": { + "context": { + "id": "token@@}@[L20:C0, L20:C1]", + "snippet": "}" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "}" + }, + "blockOpenBrace": { + "context": { + "id": "token@@{@[L4:C12, L4:C13]", + "snippet": "{" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "{" + }, + "body": [ + { + "context": { + "id": "node@@id@[L5:C4, L5:C14]", + "snippet": "id integer" + }, + "children": { + "args": [ + { + "context": { + "id": "node@@integer@[L5:C7, L5:C14]", + "snippet": "integer" + }, + "children": { + "expression": { + "context": { + "id": "node@@integer@[L5:C7, L5:C14]", + "snippet": "integer" + }, + "children": { + "variable": { + "context": { + "id": "token@@integer@[L5:C7, L5:C14]", + "snippet": "integer" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "integer" + } + }, + "fullEnd": 61, + "fullStart": 53 + } + }, + "fullEnd": 61, + "fullStart": 53 + } + ], + "callee": { + "context": { + "id": "node@@id@[L5:C4, L5:C6]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L5:C4, L5:C6]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L5:C4, L5:C6]", + "snippet": "id" + }, + "leadingTrivia": " ", + "trailingTrivia": " ", + "value": "id" + } + }, + "fullEnd": 53, + "fullStart": 46 + } + }, + "fullEnd": 53, + "fullStart": 46 + } + }, + "fullEnd": 61, + "fullStart": 46 + }, + { + "context": { + "id": "node@@user_id@[L6:C4, L6:C19]", + "snippet": "user_id integer" + }, + "children": { + "args": [ + { + "context": { + "id": "node@@integer@[L6:C12, L6:C19]", + "snippet": "integer" + }, + "children": { + "expression": { + "context": { + "id": "node@@integer@[L6:C12, L6:C19]", + "snippet": "integer" + }, + "children": { + "variable": { + "context": { + "id": "token@@integer@[L6:C12, L6:C19]", + "snippet": "integer" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "integer" + } + }, + "fullEnd": 81, + "fullStart": 73 + } + }, + "fullEnd": 81, + "fullStart": 73 + } + ], + "callee": { + "context": { + "id": "node@@user_id@[L6:C4, L6:C11]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L6:C4, L6:C11]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L6:C4, L6:C11]", + "snippet": "user_id" + }, + "leadingTrivia": " ", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 73, + "fullStart": 61 + } + }, + "fullEnd": 73, + "fullStart": 61 + } + }, + "fullEnd": 81, + "fullStart": 61 + }, + { + "context": { + "id": "node@@@[L8:C4, L8:C27]", + "snippet": "Ref: user_...> users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L8:C9, L8:C27]", + "snippet": "user_id > users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L8:C9, L8:C27]", + "snippet": "user_id > users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L8:C9, L8:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L8:C9, L8:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L8:C9, L8:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 99, + "fullStart": 91 + } + }, + "fullEnd": 99, + "fullStart": 91 + }, + "op": { + "context": { + "id": "token@@>@[L8:C17, L8:C18]", + "snippet": ">" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ">" + }, + "rightExpression": { + "context": { + "id": "node@@@[L8:C19, L8:C27]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L8:C19, L8:C24]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L8:C19, L8:C24]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L8:C19, L8:C24]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 106, + "fullStart": 101 + } + }, + "fullEnd": 106, + "fullStart": 101 + }, + "op": { + "context": { + "id": "token@@.@[L8:C24, L8:C25]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L8:C25, L8:C27]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L8:C25, L8:C27]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L8:C25, L8:C27]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 110, + "fullStart": 107 + } + }, + "fullEnd": 110, + "fullStart": 107 + } + }, + "fullEnd": 110, + "fullStart": 101 + } + }, + "fullEnd": 110, + "fullStart": 91 + } + }, + "fullEnd": 110, + "fullStart": 91 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L8:C7, L8:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L8:C4, L8:C7]", + "snippet": "Ref" + }, + "leadingTrivia": "\n ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 110, + "fullStart": 81 + }, + { + "context": { + "id": "node@@@[L9:C4, L9:C28]", + "snippet": "Ref: user_...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L9:C9, L9:C28]", + "snippet": "user_id >? users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L9:C9, L9:C28]", + "snippet": "user_id >? users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L9:C9, L9:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L9:C9, L9:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L9:C9, L9:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 127, + "fullStart": 119 + } + }, + "fullEnd": 127, + "fullStart": 119 + }, + "op": { + "context": { + "id": "token@@>?@[L9:C17, L9:C19]", + "snippet": ">?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ">?" + }, + "rightExpression": { + "context": { + "id": "node@@@[L9:C20, L9:C28]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L9:C20, L9:C25]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L9:C20, L9:C25]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L9:C20, L9:C25]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 135, + "fullStart": 130 + } + }, + "fullEnd": 135, + "fullStart": 130 + }, + "op": { + "context": { + "id": "token@@.@[L9:C25, L9:C26]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L9:C26, L9:C28]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L9:C26, L9:C28]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L9:C26, L9:C28]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 139, + "fullStart": 136 + } + }, + "fullEnd": 139, + "fullStart": 136 + } + }, + "fullEnd": 139, + "fullStart": 130 + } + }, + "fullEnd": 139, + "fullStart": 119 + } + }, + "fullEnd": 139, + "fullStart": 119 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L9:C7, L9:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L9:C4, L9:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 139, + "fullStart": 110 + }, + { + "context": { + "id": "node@@@[L10:C4, L10:C28]", + "snippet": "Ref: user_...> users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L10:C9, L10:C28]", + "snippet": "user_id ?> users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L10:C9, L10:C28]", + "snippet": "user_id ?> users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L10:C9, L10:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L10:C9, L10:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L10:C9, L10:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 156, + "fullStart": 148 + } + }, + "fullEnd": 156, + "fullStart": 148 + }, + "op": { + "context": { + "id": "token@@?>@[L10:C17, L10:C19]", + "snippet": "?>" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?>" + }, + "rightExpression": { + "context": { + "id": "node@@@[L10:C20, L10:C28]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L10:C20, L10:C25]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L10:C20, L10:C25]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L10:C20, L10:C25]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 164, + "fullStart": 159 + } + }, + "fullEnd": 164, + "fullStart": 159 + }, + "op": { + "context": { + "id": "token@@.@[L10:C25, L10:C26]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L10:C26, L10:C28]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L10:C26, L10:C28]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L10:C26, L10:C28]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 168, + "fullStart": 165 + } + }, + "fullEnd": 168, + "fullStart": 165 + } + }, + "fullEnd": 168, + "fullStart": 159 + } + }, + "fullEnd": 168, + "fullStart": 148 + } + }, + "fullEnd": 168, + "fullStart": 148 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L10:C7, L10:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L10:C4, L10:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 168, + "fullStart": 139 + }, + { + "context": { + "id": "node@@@[L11:C4, L11:C29]", + "snippet": "Ref: user_...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L11:C9, L11:C29]", + "snippet": "user_id ?>? users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L11:C9, L11:C29]", + "snippet": "user_id ?>? users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L11:C9, L11:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L11:C9, L11:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L11:C9, L11:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 185, + "fullStart": 177 + } + }, + "fullEnd": 185, + "fullStart": 177 + }, + "op": { + "context": { + "id": "token@@?>?@[L11:C17, L11:C20]", + "snippet": "?>?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?>?" + }, + "rightExpression": { + "context": { + "id": "node@@@[L11:C21, L11:C29]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L11:C21, L11:C26]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L11:C21, L11:C26]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L11:C21, L11:C26]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 194, + "fullStart": 189 + } + }, + "fullEnd": 194, + "fullStart": 189 + }, + "op": { + "context": { + "id": "token@@.@[L11:C26, L11:C27]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L11:C27, L11:C29]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L11:C27, L11:C29]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L11:C27, L11:C29]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 198, + "fullStart": 195 + } + }, + "fullEnd": 198, + "fullStart": 195 + } + }, + "fullEnd": 198, + "fullStart": 189 + } + }, + "fullEnd": 198, + "fullStart": 177 + } + }, + "fullEnd": 198, + "fullStart": 177 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L11:C7, L11:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L11:C4, L11:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 198, + "fullStart": 168 + }, + { + "context": { + "id": "node@@@[L12:C4, L12:C27]", + "snippet": "Ref: user_...- users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L12:C9, L12:C27]", + "snippet": "user_id - users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L12:C9, L12:C27]", + "snippet": "user_id - users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L12:C9, L12:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L12:C9, L12:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L12:C9, L12:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 215, + "fullStart": 207 + } + }, + "fullEnd": 215, + "fullStart": 207 + }, + "op": { + "context": { + "id": "token@@-@[L12:C17, L12:C18]", + "snippet": "-" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "-" + }, + "rightExpression": { + "context": { + "id": "node@@@[L12:C19, L12:C27]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L12:C19, L12:C24]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L12:C19, L12:C24]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L12:C19, L12:C24]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 222, + "fullStart": 217 + } + }, + "fullEnd": 222, + "fullStart": 217 + }, + "op": { + "context": { + "id": "token@@.@[L12:C24, L12:C25]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L12:C25, L12:C27]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L12:C25, L12:C27]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L12:C25, L12:C27]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 226, + "fullStart": 223 + } + }, + "fullEnd": 226, + "fullStart": 223 + } + }, + "fullEnd": 226, + "fullStart": 217 + } + }, + "fullEnd": 226, + "fullStart": 207 + } + }, + "fullEnd": 226, + "fullStart": 207 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L12:C7, L12:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L12:C4, L12:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 226, + "fullStart": 198 + }, + { + "context": { + "id": "node@@@[L13:C4, L13:C28]", + "snippet": "Ref: user_...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L13:C9, L13:C28]", + "snippet": "user_id -? users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L13:C9, L13:C28]", + "snippet": "user_id -? users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L13:C9, L13:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L13:C9, L13:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L13:C9, L13:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 243, + "fullStart": 235 + } + }, + "fullEnd": 243, + "fullStart": 235 + }, + "op": { + "context": { + "id": "token@@-?@[L13:C17, L13:C19]", + "snippet": "-?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "-?" + }, + "rightExpression": { + "context": { + "id": "node@@@[L13:C20, L13:C28]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L13:C20, L13:C25]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L13:C20, L13:C25]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L13:C20, L13:C25]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 251, + "fullStart": 246 + } + }, + "fullEnd": 251, + "fullStart": 246 + }, + "op": { + "context": { + "id": "token@@.@[L13:C25, L13:C26]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L13:C26, L13:C28]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L13:C26, L13:C28]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L13:C26, L13:C28]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 255, + "fullStart": 252 + } + }, + "fullEnd": 255, + "fullStart": 252 + } + }, + "fullEnd": 255, + "fullStart": 246 + } + }, + "fullEnd": 255, + "fullStart": 235 + } + }, + "fullEnd": 255, + "fullStart": 235 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L13:C7, L13:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L13:C4, L13:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 255, + "fullStart": 226 + }, + { + "context": { + "id": "node@@@[L14:C4, L14:C28]", + "snippet": "Ref: user_...- users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L14:C9, L14:C28]", + "snippet": "user_id ?- users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L14:C9, L14:C28]", + "snippet": "user_id ?- users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L14:C9, L14:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L14:C9, L14:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L14:C9, L14:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 272, + "fullStart": 264 + } + }, + "fullEnd": 272, + "fullStart": 264 + }, + "op": { + "context": { + "id": "token@@?-@[L14:C17, L14:C19]", + "snippet": "?-" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?-" + }, + "rightExpression": { + "context": { + "id": "node@@@[L14:C20, L14:C28]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L14:C20, L14:C25]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L14:C20, L14:C25]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L14:C20, L14:C25]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 280, + "fullStart": 275 + } + }, + "fullEnd": 280, + "fullStart": 275 + }, + "op": { + "context": { + "id": "token@@.@[L14:C25, L14:C26]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L14:C26, L14:C28]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L14:C26, L14:C28]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L14:C26, L14:C28]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 284, + "fullStart": 281 + } + }, + "fullEnd": 284, + "fullStart": 281 + } + }, + "fullEnd": 284, + "fullStart": 275 + } + }, + "fullEnd": 284, + "fullStart": 264 + } + }, + "fullEnd": 284, + "fullStart": 264 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L14:C7, L14:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L14:C4, L14:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 284, + "fullStart": 255 + }, + { + "context": { + "id": "node@@@[L15:C4, L15:C29]", + "snippet": "Ref: user_...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L15:C9, L15:C29]", + "snippet": "user_id ?-? users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L15:C9, L15:C29]", + "snippet": "user_id ?-? users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L15:C9, L15:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L15:C9, L15:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L15:C9, L15:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 301, + "fullStart": 293 + } + }, + "fullEnd": 301, + "fullStart": 293 + }, + "op": { + "context": { + "id": "token@@?-?@[L15:C17, L15:C20]", + "snippet": "?-?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?-?" + }, + "rightExpression": { + "context": { + "id": "node@@@[L15:C21, L15:C29]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L15:C21, L15:C26]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L15:C21, L15:C26]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L15:C21, L15:C26]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 310, + "fullStart": 305 + } + }, + "fullEnd": 310, + "fullStart": 305 + }, + "op": { + "context": { + "id": "token@@.@[L15:C26, L15:C27]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L15:C27, L15:C29]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L15:C27, L15:C29]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L15:C27, L15:C29]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 314, + "fullStart": 311 + } + }, + "fullEnd": 314, + "fullStart": 311 + } + }, + "fullEnd": 314, + "fullStart": 305 + } + }, + "fullEnd": 314, + "fullStart": 293 + } + }, + "fullEnd": 314, + "fullStart": 293 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L15:C7, L15:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L15:C4, L15:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 314, + "fullStart": 284 + }, + { + "context": { + "id": "node@@@[L16:C4, L16:C28]", + "snippet": "Ref: user_...> users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L16:C9, L16:C28]", + "snippet": "user_id <> users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L16:C9, L16:C28]", + "snippet": "user_id <> users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L16:C9, L16:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L16:C9, L16:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L16:C9, L16:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 331, + "fullStart": 323 + } + }, + "fullEnd": 331, + "fullStart": 323 + }, + "op": { + "context": { + "id": "token@@<>@[L16:C17, L16:C19]", + "snippet": "<>" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "<>" + }, + "rightExpression": { + "context": { + "id": "node@@@[L16:C20, L16:C28]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L16:C20, L16:C25]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L16:C20, L16:C25]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L16:C20, L16:C25]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 339, + "fullStart": 334 + } + }, + "fullEnd": 339, + "fullStart": 334 + }, + "op": { + "context": { + "id": "token@@.@[L16:C25, L16:C26]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L16:C26, L16:C28]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L16:C26, L16:C28]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L16:C26, L16:C28]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 343, + "fullStart": 340 + } + }, + "fullEnd": 343, + "fullStart": 340 + } + }, + "fullEnd": 343, + "fullStart": 334 + } + }, + "fullEnd": 343, + "fullStart": 323 + } + }, + "fullEnd": 343, + "fullStart": 323 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L16:C7, L16:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L16:C4, L16:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 343, + "fullStart": 314 + }, + { + "context": { + "id": "node@@@[L17:C4, L17:C29]", + "snippet": "Ref: user_...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L17:C9, L17:C29]", + "snippet": "user_id <>? users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L17:C9, L17:C29]", + "snippet": "user_id <>? users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L17:C9, L17:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L17:C9, L17:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L17:C9, L17:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 360, + "fullStart": 352 + } + }, + "fullEnd": 360, + "fullStart": 352 + }, + "op": { + "context": { + "id": "token@@<>?@[L17:C17, L17:C20]", + "snippet": "<>?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "<>?" + }, + "rightExpression": { + "context": { + "id": "node@@@[L17:C21, L17:C29]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L17:C21, L17:C26]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L17:C21, L17:C26]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L17:C21, L17:C26]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 369, + "fullStart": 364 + } + }, + "fullEnd": 369, + "fullStart": 364 + }, + "op": { + "context": { + "id": "token@@.@[L17:C26, L17:C27]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L17:C27, L17:C29]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L17:C27, L17:C29]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L17:C27, L17:C29]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 373, + "fullStart": 370 + } + }, + "fullEnd": 373, + "fullStart": 370 + } + }, + "fullEnd": 373, + "fullStart": 364 + } + }, + "fullEnd": 373, + "fullStart": 352 + } + }, + "fullEnd": 373, + "fullStart": 352 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L17:C7, L17:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L17:C4, L17:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 373, + "fullStart": 343 + }, + { + "context": { + "id": "node@@@[L18:C4, L18:C29]", + "snippet": "Ref: user_...> users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L18:C9, L18:C29]", + "snippet": "user_id ?<> users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L18:C9, L18:C29]", + "snippet": "user_id ?<> users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L18:C9, L18:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L18:C9, L18:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L18:C9, L18:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 390, + "fullStart": 382 + } + }, + "fullEnd": 390, + "fullStart": 382 + }, + "op": { + "context": { + "id": "token@@?<>@[L18:C17, L18:C20]", + "snippet": "?<>" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?<>" + }, + "rightExpression": { + "context": { + "id": "node@@@[L18:C21, L18:C29]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L18:C21, L18:C26]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L18:C21, L18:C26]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L18:C21, L18:C26]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 399, + "fullStart": 394 + } + }, + "fullEnd": 399, + "fullStart": 394 + }, + "op": { + "context": { + "id": "token@@.@[L18:C26, L18:C27]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L18:C27, L18:C29]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L18:C27, L18:C29]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L18:C27, L18:C29]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 403, + "fullStart": 400 + } + }, + "fullEnd": 403, + "fullStart": 400 + } + }, + "fullEnd": 403, + "fullStart": 394 + } + }, + "fullEnd": 403, + "fullStart": 382 + } + }, + "fullEnd": 403, + "fullStart": 382 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L18:C7, L18:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L18:C4, L18:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 403, + "fullStart": 373 + }, + { + "context": { + "id": "node@@@[L19:C4, L19:C30]", + "snippet": "Ref: user_...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L19:C9, L19:C30]", + "snippet": "user_id ?<...? users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L19:C9, L19:C30]", + "snippet": "user_id ?<...? users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@user_id@[L19:C9, L19:C16]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L19:C9, L19:C16]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L19:C9, L19:C16]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 420, + "fullStart": 412 + } + }, + "fullEnd": 420, + "fullStart": 412 + }, + "op": { + "context": { + "id": "token@@?<>?@[L19:C17, L19:C21]", + "snippet": "?<>?" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?<>?" + }, + "rightExpression": { + "context": { + "id": "node@@@[L19:C22, L19:C30]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L19:C22, L19:C27]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L19:C22, L19:C27]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L19:C22, L19:C27]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 430, + "fullStart": 425 + } + }, + "fullEnd": 430, + "fullStart": 425 + }, + "op": { + "context": { + "id": "token@@.@[L19:C27, L19:C28]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L19:C28, L19:C30]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L19:C28, L19:C30]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L19:C28, L19:C30]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 434, + "fullStart": 431 + } + }, + "fullEnd": 434, + "fullStart": 431 + } + }, + "fullEnd": 434, + "fullStart": 425 + } + }, + "fullEnd": 434, + "fullStart": 412 + } + }, + "fullEnd": 434, + "fullStart": 412 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L19:C7, L19:C8]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L19:C4, L19:C7]", + "snippet": "Ref" + }, + "leadingTrivia": " ", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 434, + "fullStart": 403 + } + ] + }, + "fullEnd": 436, + "fullStart": 44 + }, + "name": { + "context": { + "id": "node@@posts@[L4:C6, L4:C11]", + "snippet": "posts" + }, + "children": { + "expression": { + "context": { + "id": "node@@posts@[L4:C6, L4:C11]", + "snippet": "posts" + }, + "children": { + "variable": { + "context": { + "id": "token@@posts@[L4:C6, L4:C11]", + "snippet": "posts" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "posts" + } + }, + "fullEnd": 44, + "fullStart": 38 + } + }, + "fullEnd": 44, + "fullStart": 38 + }, + "type": { + "context": { + "id": "token@@Table@[L4:C0, L4:C5]", + "snippet": "Table" + }, + "leadingTrivia": "\n", + "trailingTrivia": " ", + "value": "Table" + } + }, + "fullEnd": 436, + "fullStart": 31 + }, + { + "context": { + "id": "node@@@[L22:C0, L22:C29]", + "snippet": "Ref: posts...< users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L22:C5, L22:C29]", + "snippet": "posts.user...< users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L22:C5, L22:C29]", + "snippet": "posts.user...< users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@@[L22:C5, L22:C18]", + "snippet": "posts.user_id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@posts@[L22:C5, L22:C10]", + "snippet": "posts" + }, + "children": { + "expression": { + "context": { + "id": "node@@posts@[L22:C5, L22:C10]", + "snippet": "posts" + }, + "children": { + "variable": { + "context": { + "id": "token@@posts@[L22:C5, L22:C10]", + "snippet": "posts" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "posts" + } + }, + "fullEnd": 447, + "fullStart": 442 + } + }, + "fullEnd": 447, + "fullStart": 442 + }, + "op": { + "context": { + "id": "token@@.@[L22:C10, L22:C11]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@user_id@[L22:C11, L22:C18]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L22:C11, L22:C18]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L22:C11, L22:C18]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 456, + "fullStart": 448 + } + }, + "fullEnd": 456, + "fullStart": 448 + } + }, + "fullEnd": 456, + "fullStart": 442 + }, + "op": { + "context": { + "id": "token@@<@[L22:C19, L22:C20]", + "snippet": "<" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "<" + }, + "rightExpression": { + "context": { + "id": "node@@@[L22:C21, L22:C29]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L22:C21, L22:C26]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L22:C21, L22:C26]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L22:C21, L22:C26]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 463, + "fullStart": 458 + } + }, + "fullEnd": 463, + "fullStart": 458 + }, + "op": { + "context": { + "id": "token@@.@[L22:C26, L22:C27]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L22:C27, L22:C29]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L22:C27, L22:C29]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L22:C27, L22:C29]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 467, + "fullStart": 464 + } + }, + "fullEnd": 467, + "fullStart": 464 + } + }, + "fullEnd": 467, + "fullStart": 458 + } + }, + "fullEnd": 467, + "fullStart": 442 + } + }, + "fullEnd": 467, + "fullStart": 442 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L22:C3, L22:C4]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L22:C0, L22:C3]", + "snippet": "Ref" + }, + "leadingTrivia": "\n", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 467, + "fullStart": 436 + }, + { + "context": { + "id": "node@@@[L23:C0, L23:C30]", + "snippet": "Ref: posts...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L23:C5, L23:C30]", + "snippet": "posts.user...? users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L23:C5, L23:C30]", + "snippet": "posts.user...? users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@@[L23:C5, L23:C18]", + "snippet": "posts.user_id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@posts@[L23:C5, L23:C10]", + "snippet": "posts" + }, + "children": { + "expression": { + "context": { + "id": "node@@posts@[L23:C5, L23:C10]", + "snippet": "posts" + }, + "children": { + "variable": { + "context": { + "id": "token@@posts@[L23:C5, L23:C10]", + "snippet": "posts" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "posts" + } + }, + "fullEnd": 477, + "fullStart": 472 + } + }, + "fullEnd": 477, + "fullStart": 472 + }, + "op": { + "context": { + "id": "token@@.@[L23:C10, L23:C11]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@user_id@[L23:C11, L23:C18]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L23:C11, L23:C18]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L23:C11, L23:C18]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 486, + "fullStart": 478 + } + }, + "fullEnd": 486, + "fullStart": 478 + } + }, + "fullEnd": 486, + "fullStart": 472 + }, + "op": { + "context": { + "id": "token@@@@[L23:C22, L23:C30]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L23:C22, L23:C27]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L23:C22, L23:C27]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L23:C22, L23:C27]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 494, + "fullStart": 489 + } + }, + "fullEnd": 494, + "fullStart": 489 + }, + "op": { + "context": { + "id": "token@@.@[L23:C27, L23:C28]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L23:C28, L23:C30]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L23:C28, L23:C30]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L23:C28, L23:C30]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 498, + "fullStart": 495 + } + }, + "fullEnd": 498, + "fullStart": 495 + } + }, + "fullEnd": 498, + "fullStart": 489 + } + }, + "fullEnd": 498, + "fullStart": 472 + } + }, + "fullEnd": 498, + "fullStart": 472 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L23:C3, L23:C4]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L23:C0, L23:C3]", + "snippet": "Ref" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 498, + "fullStart": 467 + }, + { + "context": { + "id": "node@@@[L24:C0, L24:C30]", + "snippet": "Ref: posts...< users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L24:C5, L24:C30]", + "snippet": "posts.user...< users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L24:C5, L24:C30]", + "snippet": "posts.user...< users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@@[L24:C5, L24:C18]", + "snippet": "posts.user_id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@posts@[L24:C5, L24:C10]", + "snippet": "posts" + }, + "children": { + "expression": { + "context": { + "id": "node@@posts@[L24:C5, L24:C10]", + "snippet": "posts" + }, + "children": { + "variable": { + "context": { + "id": "token@@posts@[L24:C5, L24:C10]", + "snippet": "posts" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "posts" + } + }, + "fullEnd": 508, + "fullStart": 503 + } + }, + "fullEnd": 508, + "fullStart": 503 + }, + "op": { + "context": { + "id": "token@@.@[L24:C10, L24:C11]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@user_id@[L24:C11, L24:C18]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L24:C11, L24:C18]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L24:C11, L24:C18]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 517, + "fullStart": 509 + } + }, + "fullEnd": 517, + "fullStart": 509 + } + }, + "fullEnd": 517, + "fullStart": 503 + }, + "op": { + "context": { + "id": "token@@?<@[L24:C19, L24:C21]", + "snippet": "?<" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "?<" + }, + "rightExpression": { + "context": { + "id": "node@@@[L24:C22, L24:C30]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L24:C22, L24:C27]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L24:C22, L24:C27]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L24:C22, L24:C27]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 525, + "fullStart": 520 + } + }, + "fullEnd": 525, + "fullStart": 520 + }, + "op": { + "context": { + "id": "token@@.@[L24:C27, L24:C28]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L24:C28, L24:C30]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L24:C28, L24:C30]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L24:C28, L24:C30]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 529, + "fullStart": 526 + } + }, + "fullEnd": 529, + "fullStart": 526 + } + }, + "fullEnd": 529, + "fullStart": 520 + } + }, + "fullEnd": 529, + "fullStart": 503 + } + }, + "fullEnd": 529, + "fullStart": 503 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L24:C3, L24:C4]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L24:C0, L24:C3]", + "snippet": "Ref" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 529, + "fullStart": 498 + }, + { + "context": { + "id": "node@@@[L25:C0, L25:C31]", + "snippet": "Ref: posts...? users.id" + }, + "children": { + "body": { + "context": { + "id": "node@@@[L25:C5, L25:C31]", + "snippet": "posts.user...? users.id" + }, + "children": { + "callee": { + "context": { + "id": "node@@@[L25:C5, L25:C31]", + "snippet": "posts.user...? users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@@[L25:C5, L25:C18]", + "snippet": "posts.user_id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@posts@[L25:C5, L25:C10]", + "snippet": "posts" + }, + "children": { + "expression": { + "context": { + "id": "node@@posts@[L25:C5, L25:C10]", + "snippet": "posts" + }, + "children": { + "variable": { + "context": { + "id": "token@@posts@[L25:C5, L25:C10]", + "snippet": "posts" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "posts" + } + }, + "fullEnd": 539, + "fullStart": 534 + } + }, + "fullEnd": 539, + "fullStart": 534 + }, + "op": { + "context": { + "id": "token@@.@[L25:C10, L25:C11]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@user_id@[L25:C11, L25:C18]", + "snippet": "user_id" + }, + "children": { + "expression": { + "context": { + "id": "node@@user_id@[L25:C11, L25:C18]", + "snippet": "user_id" + }, + "children": { + "variable": { + "context": { + "id": "token@@user_id@[L25:C11, L25:C18]", + "snippet": "user_id" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": "user_id" + } + }, + "fullEnd": 548, + "fullStart": 540 + } + }, + "fullEnd": 548, + "fullStart": 540 + } + }, + "fullEnd": 548, + "fullStart": 534 + }, + "op": { + "context": { + "id": "token@@?@@[L25:C23, L25:C31]", + "snippet": "users.id" + }, + "children": { + "leftExpression": { + "context": { + "id": "node@@users@[L25:C23, L25:C28]", + "snippet": "users" + }, + "children": { + "expression": { + "context": { + "id": "node@@users@[L25:C23, L25:C28]", + "snippet": "users" + }, + "children": { + "variable": { + "context": { + "id": "token@@users@[L25:C23, L25:C28]", + "snippet": "users" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "users" + } + }, + "fullEnd": 557, + "fullStart": 552 + } + }, + "fullEnd": 557, + "fullStart": 552 + }, + "op": { + "context": { + "id": "token@@.@[L25:C28, L25:C29]", + "snippet": "." + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "." + }, + "rightExpression": { + "context": { + "id": "node@@id@[L25:C29, L25:C31]", + "snippet": "id" + }, + "children": { + "expression": { + "context": { + "id": "node@@id@[L25:C29, L25:C31]", + "snippet": "id" + }, + "children": { + "variable": { + "context": { + "id": "token@@id@[L25:C29, L25:C31]", + "snippet": "id" + }, + "leadingTrivia": "", + "trailingTrivia": "\n", + "value": "id" + } + }, + "fullEnd": 561, + "fullStart": 558 + } + }, + "fullEnd": 561, + "fullStart": 558 + } + }, + "fullEnd": 561, + "fullStart": 552 + } + }, + "fullEnd": 561, + "fullStart": 534 + } + }, + "fullEnd": 561, + "fullStart": 534 + }, + "bodyColon": { + "context": { + "id": "token@@:@[L25:C3, L25:C4]", + "snippet": ":" + }, + "leadingTrivia": "", + "trailingTrivia": " ", + "value": ":" + }, + "type": { + "context": { + "id": "token@@Ref@[L25:C0, L25:C3]", + "snippet": "Ref" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "Ref" + } + }, + "fullEnd": 561, + "fullStart": 529 + } + ], + "eof": { + "context": { + "id": "token@@@[L26:C0, L26:C0]", + "snippet": "" + }, + "leadingTrivia": "", + "trailingTrivia": "", + "value": "" + } + }, + "fullEnd": 561, + "fullStart": 0 + } +} \ No newline at end of file diff --git a/packages/dbml-parse/src/core/types/tokens.ts b/packages/dbml-parse/src/core/types/tokens.ts index febd34487..1fc50d927 100644 --- a/packages/dbml-parse/src/core/types/tokens.ts +++ b/packages/dbml-parse/src/core/types/tokens.ts @@ -61,6 +61,7 @@ export function isOp (c?: string): boolean { case '>': case '=': case '!': + case '?': case '.': case '&': case '|': From ac22a33c5684799b5cbace70d095da230dd68007 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 10:14:16 +0700 Subject: [PATCH 05/35] fix: lint issues --- packages/dbml-parse/eslint.config.ts | 1 + packages/dbml-parse/src/core/global_modules/utils.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dbml-parse/eslint.config.ts b/packages/dbml-parse/eslint.config.ts index fd13835b0..8562511f6 100644 --- a/packages/dbml-parse/eslint.config.ts +++ b/packages/dbml-parse/eslint.config.ts @@ -18,6 +18,7 @@ export default defineConfig( ignores: [ 'node_modules/*', 'dist/*', + 'dist-profile/*', 'vite.config.ts', 'vite.profile.config.ts', 'eslint.config.ts', diff --git a/packages/dbml-parse/src/core/global_modules/utils.ts b/packages/dbml-parse/src/core/global_modules/utils.ts index 930926a22..973f16da1 100644 --- a/packages/dbml-parse/src/core/global_modules/utils.ts +++ b/packages/dbml-parse/src/core/global_modules/utils.ts @@ -109,4 +109,3 @@ export function nodeRefereeOfLeftExpression (compiler: Compiler, node: SyntaxNod } return compiler.nodeReferee(leftExpr).getFiltered(UNHANDLED) ?? undefined; } - From 939a6fd508a50eef3e968e832274d324ab9f6743 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 10:15:03 +0700 Subject: [PATCH 06/35] feat: make metadata optional ref aware --- packages/dbml-parse/src/core/types/symbol/metadata.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/dbml-parse/src/core/types/symbol/metadata.ts b/packages/dbml-parse/src/core/types/symbol/metadata.ts index 5b209af48..2c8b8f8d8 100644 --- a/packages/dbml-parse/src/core/types/symbol/metadata.ts +++ b/packages/dbml-parse/src/core/types/symbol/metadata.ts @@ -16,6 +16,7 @@ import type { TableSymbol, } from '../symbol'; import type { Internable } from '../internable'; +import type { RelationshipOp } from '../relation'; import { UNHANDLED } from '../module'; import { ElementKind, SettingName } from '../keywords'; import { @@ -145,18 +146,18 @@ export class RefMetadata extends NodeMetadata { return undefined; } - op (_compiler: Compiler): '>' | '<' | '-' | '<>' | undefined { + op (_compiler: Compiler): RelationshipOp | undefined { if (this.declaration instanceof ElementDeclarationNode) { const field = getBody(this.declaration)[0]; if (!(field instanceof FunctionApplicationNode)) return undefined; const infix = field.callee; if (!(infix instanceof InfixExpressionNode)) return undefined; - return infix.op?.value as '>' | '<' | '-' | '<>' | undefined; + return infix.op?.value as RelationshipOp | undefined; } if (this.declaration instanceof AttributeNode) { const prefix = this.declaration.value; if (!(prefix instanceof PrefixExpressionNode)) return undefined; - return prefix.op?.value as '>' | '<' | '-' | '<>' | undefined; + return prefix.op?.value as RelationshipOp | undefined; } return undefined; } @@ -271,11 +272,11 @@ export class PartialRefMetadata extends NodeMetadata { return extractTableFromEndpoint(compiler, prefix.expression) ?? this.container(compiler); } - op (_compiler: Compiler): '>' | '<' | '-' | '<>' | undefined { + op (_compiler: Compiler): RelationshipOp | undefined { if (!(this.declaration instanceof AttributeNode)) return undefined; const prefix = this.declaration.value; if (!(prefix instanceof PrefixExpressionNode)) return undefined; - return prefix.op?.value as '>' | '<' | '-' | '<>' | undefined; + return prefix.op?.value as RelationshipOp | undefined; } leftToken (): SyntaxNode { From 7c59ef595a90169c85040c3f08535ec6be514bc5 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 10:17:08 +0700 Subject: [PATCH 07/35] feat: add methods to metadata to return cardinalities --- .../dbml-parse/src/core/types/symbol/metadata.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/dbml-parse/src/core/types/symbol/metadata.ts b/packages/dbml-parse/src/core/types/symbol/metadata.ts index 2c8b8f8d8..42740b807 100644 --- a/packages/dbml-parse/src/core/types/symbol/metadata.ts +++ b/packages/dbml-parse/src/core/types/symbol/metadata.ts @@ -16,7 +16,8 @@ import type { TableSymbol, } from '../symbol'; import type { Internable } from '../internable'; -import type { RelationshipOp } from '../relation'; +import type { RelationshipOp, RelationCardinality } from '../relation'; +import { getMultiplicities } from '../relation'; import { UNHANDLED } from '../module'; import { ElementKind, SettingName } from '../keywords'; import { @@ -162,6 +163,11 @@ export class RefMetadata extends NodeMetadata { return undefined; } + cardinalities (compiler: Compiler): [RelationCardinality, RelationCardinality] | undefined { + const op = this.op(compiler); + return op ? getMultiplicities(op) : undefined; + } + active (compiler: Compiler): boolean { if (!(this.declaration instanceof ElementDeclarationNode)) return true; const field = getBody(this.declaration)[0]; @@ -279,6 +285,11 @@ export class PartialRefMetadata extends NodeMetadata { return prefix.op?.value as RelationshipOp | undefined; } + cardinalities (compiler: Compiler): [RelationCardinality, RelationCardinality] | undefined { + const op = this.op(compiler); + return op ? getMultiplicities(op) : undefined; + } + leftToken (): SyntaxNode { if (this.declaration instanceof AttributeNode) { const columnField = this.declaration.parentOfKind(FunctionApplicationNode); From 5f68c4d889b3841f02180f6a8594cacb897bc3fd Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 10:59:07 +0700 Subject: [PATCH 08/35] feat: refactor the flow of constraints checking to be more methodical --- .../examples/interpreter/record/fk.test.ts | 34 +++-- .../records/utils/constraints/fk.ts | 143 ++++++++---------- .../core/global_modules/table/interpret.ts | 2 +- 3 files changed, 91 insertions(+), 88 deletions(-) diff --git a/packages/dbml-parse/__tests__/examples/interpreter/record/fk.test.ts b/packages/dbml-parse/__tests__/examples/interpreter/record/fk.test.ts index f33d2eb8e..89f0941d2 100644 --- a/packages/dbml-parse/__tests__/examples/interpreter/record/fk.test.ts +++ b/packages/dbml-parse/__tests__/examples/interpreter/record/fk.test.ts @@ -110,9 +110,13 @@ describe('[example - record] composite foreign key constraints', () => { const result = interpret(source); const warnings = result.getWarnings(); - expect(warnings.length).toBe(2); + expect(warnings.length).toBe(4); + // orders → merchants: (1, "UK") doesn't exist in merchants expect(warnings[0].diagnostic).toBe('FK violation: (orders.merchant_id, orders.country) = (1, "UK") does not exist in (merchants.id, merchants.country_code)'); expect(warnings[1].diagnostic).toBe('FK violation: (orders.merchant_id, orders.country) = (1, "UK") does not exist in (merchants.id, merchants.country_code)'); + // merchants → orders: (2, "UK") doesn't exist in orders + expect(warnings[2].diagnostic).toBe('FK violation: (merchants.id, merchants.country_code) = (2, "UK") does not exist in (orders.merchant_id, orders.country)'); + expect(warnings[3].diagnostic).toBe('FK violation: (merchants.id, merchants.country_code) = (2, "UK") does not exist in (orders.merchant_id, orders.country)'); }); test('should allow NULL in composite FK columns', () => { @@ -131,7 +135,7 @@ describe('[example - record] composite foreign key constraints', () => { country varchar status varchar } - Ref: orders.(merchant_id, country) > merchants.(id, country_code) + Ref: orders.(merchant_id, country) >? merchants.(id, country_code) records merchants(id, country_code) { 1, "US" @@ -244,9 +248,11 @@ describe('[example - record] composite foreign key constraints', () => { const result = interpret(source); const warnings = result.getWarnings(); - expect(warnings.length).toBe(2); + expect(warnings.length).toBe(4); expect(warnings[0].diagnostic).toBe('FK violation: (posts.user_id, posts.tenant_id) = (999, 100) does not exist in (auth.users.id, auth.users.tenant_id)'); expect(warnings[1].diagnostic).toBe('FK violation: (posts.user_id, posts.tenant_id) = (999, 100) does not exist in (auth.users.id, auth.users.tenant_id)'); + expect(warnings[2].diagnostic).toBe('FK violation: (auth.users.id, auth.users.tenant_id) = (2, 100) does not exist in (posts.user_id, posts.tenant_id)'); + expect(warnings[3].diagnostic).toBe('FK violation: (auth.users.id, auth.users.tenant_id) = (2, 100) does not exist in (posts.user_id, posts.tenant_id)'); }); }); @@ -442,7 +448,7 @@ describe('[example - record] simple foreign key constraints', () => { category_id int name varchar } - Ref: products.category_id > categories.id + Ref: products.category_id >? categories.id records categories(id, name) { 1, "Electronics" @@ -556,7 +562,7 @@ describe('[example - record] simple foreign key constraints', () => { manager_id int name varchar } - Ref: employees.manager_id > employees.id + Ref: employees.manager_id >? employees.id records users(id, name) { 1, "Alice" @@ -574,9 +580,12 @@ describe('[example - record] simple foreign key constraints', () => { const result = interpret(source); const warnings = result.getWarnings(); - expect(warnings.length).toBe(2); + expect(warnings.length).toBe(4); expect(warnings[0].diagnostic).toBe('FK violation: posts.user_id = 999 does not exist in users.id'); expect(warnings[1].diagnostic).toBe('FK violation: employees.manager_id = 999 does not exist in employees.id'); + // Reverse: employees.id must exist in employees.manager_id + expect(warnings[2].diagnostic).toBe('FK violation: employees.id = 2 does not exist in employees.manager_id'); + expect(warnings[3].diagnostic).toBe('FK violation: employees.id = 3 does not exist in employees.manager_id'); }); test('should detect FK violation when target table is empty', () => { @@ -743,15 +752,17 @@ describe('[example - record] FK in table partials', () => { const result = interpret(source); const warnings = result.getWarnings(); - expect(warnings.length).toBe(1); + expect(warnings.length).toBe(2); expect(warnings[0].code).toBe(CompileErrorCode.INVALID_RECORDS_FIELD); expect(warnings[0].diagnostic).toBe('FK violation: comments.created_by = 999 does not exist in users.id'); + // Reverse: users.id=2 not in comments.created_by + expect(warnings[1].diagnostic).toBe('FK violation: users.id = 2 does not exist in comments.created_by'); }); test('should allow NULL FK values from injected table partial', () => { const source = ` TablePartial optional_user { - user_id int [ref: > users.id] + user_id int [ref: >? users.id] } Table users { @@ -835,7 +846,7 @@ describe('[example - record] FK in table partials', () => { test('should validate self-referencing FK from injected table partial', () => { const source = ` TablePartial hierarchical { - parent_id int [ref: > nodes.id] + parent_id int [ref: >? nodes.id] } Table nodes { @@ -853,8 +864,11 @@ describe('[example - record] FK in table partials', () => { const result = interpret(source); const warnings = result.getWarnings(); - expect(warnings.length).toBe(1); + expect(warnings.length).toBe(3); expect(warnings[0].code).toBe(CompileErrorCode.INVALID_RECORDS_FIELD); expect(warnings[0].diagnostic).toBe('FK violation: nodes.parent_id = 999 does not exist in nodes.id'); + // Reverse: nodes.id must exist in nodes.parent_id + expect(warnings[1].diagnostic).toBe('FK violation: nodes.id = 2 does not exist in nodes.parent_id'); + expect(warnings[2].diagnostic).toBe('FK violation: nodes.id = 3 does not exist in nodes.parent_id'); }); }); diff --git a/packages/dbml-parse/src/core/global_modules/records/utils/constraints/fk.ts b/packages/dbml-parse/src/core/global_modules/records/utils/constraints/fk.ts index b83179c33..7eea6487c 100644 --- a/packages/dbml-parse/src/core/global_modules/records/utils/constraints/fk.ts +++ b/packages/dbml-parse/src/core/global_modules/records/utils/constraints/fk.ts @@ -3,6 +3,8 @@ import type Compiler from '@/compiler/index'; import type { CompileWarning } from '@/core/types/errors'; import type { Filepath } from '@/core/types/filepath'; import type { SyntaxNode } from '@/core/types/nodes'; +import { parseCardinality } from '@/core/types/relation'; +import type { RelationCardinality } from '@/core/types/relation'; import type { Ref, RefEndpoint, TableRecord } from '@/core/types/schemaJson'; import type { TableSymbol } from '@/core/types/symbol'; import type { InternedNodeSymbol } from '@/core/types/symbol/symbols'; @@ -63,63 +65,16 @@ export function validateForeignKeys ( return ref.endpoints.some((ep) => tablesWithRecords.has(makeTableKey(ep.schemaName, ep.tableName))); }); - return flatMap(relevantRefs, (ref) => validateRef(compiler, ref, tableInfoLookup, filepath)); + return flatMap(relevantRefs, (ref) => validateForeignKey(compiler, ref, tableInfoLookup, filepath)); } -// Validate that source's FK values exist in target's values -function validateFkSourceToTarget ( +// Validate 1 foreign key constraint only +function validateForeignKey ( compiler: Compiler, - sourceTable: TableInfo, - targetTable: TableInfo, - sourceEndpoint: RefEndpoint, - targetEndpoint: RefEndpoint, + ref: Ref, // The constraint to validate + tableInfoLookup: Map, // Fast info lookup filepath: Filepath, ): CompileWarning[] { - if (!sourceTable.record || isEmpty(sourceTable.record.values)) return []; - - const sourceRows = toKeyedRows(sourceTable.record); - const targetRows = targetTable.record ? toKeyedRows(targetTable.record) : []; - - // Build set of valid target values for FK reference check - const validFkValues = new Set( - targetRows.map((row) => extractKeyValueWithDefault(row, targetEndpoint.fieldNames)), - ); - - // Filter rows with NULL values (optional relationships) - const rowsWithValues = sourceRows - .filter((row) => !hasNullWithoutDefaultInKey(row, sourceEndpoint.fieldNames)); - - // Find rows with FK values that don't exist in target - const invalidRows = rowsWithValues.filter((row) => { - const fkValue = extractKeyValueWithDefault(row, sourceEndpoint.fieldNames); - return !validFkValues.has(fkValue); - }); - - const sourceName = sourceTable.tableSymbol.interpretedName(compiler, filepath); - const targetName = targetTable.tableSymbol.interpretedName(compiler, filepath); - - // Transform invalid rows to warnings - return flatMap(invalidRows, (row) => { - const sourceColumnRef = formatFullColumnNames( - sourceName.schema, - sourceName.name, - sourceEndpoint.fieldNames, - ); - const targetColumnRef = formatFullColumnNames( - targetName.schema, - targetName.name, - targetEndpoint.fieldNames, - ); - const valueStr = formatValues(row, sourceEndpoint.fieldNames); - const message = `FK violation: ${sourceColumnRef} = ${valueStr} does not exist in ${targetColumnRef}`; - - return sourceEndpoint.fieldNames.map((col) => - createConstraintWarning(compiler, row[col], message), - ); - }); -} - -function validateRef (compiler: Compiler, ref: Ref, tableInfoLookup: Map, filepath: Filepath): CompileWarning[] { if (!ref.endpoints) return []; const [ @@ -131,37 +86,71 @@ function validateRef (compiler: Compiler, ref: Ref, tableInfoLookup: Map left allows NULL +// - right min >= 1 -> left must not be NULL +// - right max = 1 -> left must map to exactly 1 right row (FK existence) +// - right max = * -> no FK constraint +function validateEndpoint ( compiler: Compiler, - table1: TableInfo, - table2: TableInfo, - endpoint1: RefEndpoint, - endpoint2: RefEndpoint, + leftTable: TableInfo, + leftEndpoint: RefEndpoint, + rightTable: TableInfo, + rightEndpoint: RefEndpoint, + rightCard: RelationCardinality, // This will constrains the left table's records filepath: Filepath, ): CompileWarning[] { - const rel1 = endpoint1.relation; - const rel2 = endpoint2.relation; - - // Bidirectional relationships: both 1-1 and many-to-many - const isBidirectional = (rel1 === '1' && rel2 === '1') || (rel1 === '*' && rel2 === '*'); - if (isBidirectional) { - return [ - ...validateFkSourceToTarget(compiler, table1, table2, endpoint1, endpoint2, filepath), - ...validateFkSourceToTarget(compiler, table2, table1, endpoint2, endpoint1, filepath), - ]; - } + if (!leftTable.record || isEmpty(leftTable.record.values)) return []; - // Many-to-one: validate FK from "many" side to "one" side - if (rel1 === '*' && rel2 === '1') { - return validateFkSourceToTarget(compiler, table1, table2, endpoint1, endpoint2, filepath); - } + const { min: rightMin } = parseCardinality(rightCard); - if (rel1 === '1' && rel2 === '*') { - return validateFkSourceToTarget(compiler, table2, table1, endpoint2, endpoint1, filepath); - } + // right min = 0 -> left FK values may be NULL (optional relationship) + // right min >= 1 -> left FK values must not be NULL + const allowNull = rightMin === 0; + + const leftRows = toKeyedRows(leftTable.record); + const rightRows = rightTable.record ? toKeyedRows(rightTable.record) : []; + + const validFkValues = new Set( + rightRows.map((row) => extractKeyValueWithDefault(row, rightEndpoint.fieldNames)), + ); - return []; + const leftName = leftTable.tableSymbol.interpretedName(compiler, filepath); + const rightName = rightTable.tableSymbol.interpretedName(compiler, filepath); + + return flatMap(leftRows, (row) => { + const isNull = hasNullWithoutDefaultInKey(row, leftEndpoint.fieldNames); + + if (isNull) { + if (allowNull) return []; + const leftColumnRef = formatFullColumnNames(leftName.schema, leftName.name, leftEndpoint.fieldNames); + const valueStr = formatValues(row, leftEndpoint.fieldNames); + const message = `FK violation: ${leftColumnRef} = ${valueStr} must not be null`; + return leftEndpoint.fieldNames.map((col) => + createConstraintWarning(compiler, row[col], message), + ); + } + + // right max = 1 -> non-null left value must map to exactly 1 right row + // right max = * -> non-null left value must exist in right + // Both cases: left value must exist in right values + const fkValue = extractKeyValueWithDefault(row, leftEndpoint.fieldNames); + if (validFkValues.has(fkValue)) return []; + + const leftColumnRef = formatFullColumnNames(leftName.schema, leftName.name, leftEndpoint.fieldNames); + const rightColumnRef = formatFullColumnNames(rightName.schema, rightName.name, rightEndpoint.fieldNames); + const valueStr = formatValues(row, leftEndpoint.fieldNames); + const message = `FK violation: ${leftColumnRef} = ${valueStr} does not exist in ${rightColumnRef}`; + return leftEndpoint.fieldNames.map((col) => + createConstraintWarning(compiler, row[col], message), + ); + }); } diff --git a/packages/dbml-parse/src/core/global_modules/table/interpret.ts b/packages/dbml-parse/src/core/global_modules/table/interpret.ts index c3b90391e..2274b3cd8 100644 --- a/packages/dbml-parse/src/core/global_modules/table/interpret.ts +++ b/packages/dbml-parse/src/core/global_modules/table/interpret.ts @@ -1,4 +1,4 @@ -import { last, partition } from 'lodash-es'; +import { partition } from 'lodash-es'; import Compiler from '@/compiler'; import { CompileError, CompileErrorCode } from '@/core/types/errors'; import { ElementKind, SettingName } from '@/core/types/keywords'; From c76d4d4df838241b6fbf232a7a66c406f15439a5 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 11:03:08 +0700 Subject: [PATCH 09/35] feat: integrate relationship op into @dbml/core --- packages/dbml-core/types/model_structure/endpoint.d.ts | 5 +++-- packages/dbml-core/types/model_structure/field.d.ts | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/dbml-core/types/model_structure/endpoint.d.ts b/packages/dbml-core/types/model_structure/endpoint.d.ts index d29855d37..1d9f9af53 100644 --- a/packages/dbml-core/types/model_structure/endpoint.d.ts +++ b/packages/dbml-core/types/model_structure/endpoint.d.ts @@ -4,12 +4,13 @@ import Ref from './ref'; import DbState from './dbState'; import { NormalizedModel } from './database'; import { Token } from './element'; +import type { RelationCardinality } from '@dbml/parse'; export interface RawEndpoint { schemaName: string | null; tableName: string; fieldNames: string[]; - relation: '1' | '*'; + relation: RelationCardinality; token: Token; } @@ -57,7 +58,7 @@ export interface NormalizedEndpoint { tableName: string; fieldNames: string[]; fieldIds: number[]; - relation: string; + relation: RelationCardinality; refId: number; } diff --git a/packages/dbml-core/types/model_structure/field.d.ts b/packages/dbml-core/types/model_structure/field.d.ts index 8e322abaa..a29be4da9 100644 --- a/packages/dbml-core/types/model_structure/field.d.ts +++ b/packages/dbml-core/types/model_structure/field.d.ts @@ -6,12 +6,13 @@ import Enum from './enum'; import Table from './table'; import TablePartial from './tablePartial'; import Check from './check'; +import type { RelationshipOp } from '@dbml/parse'; export interface InlineRef { schemaName: string | null; tableName: string; fieldNames: string[]; - relation: '>' | '<' | '-' | '<>'; + relation: RelationshipOp; token: Token; } From 698cacbcfa96b479406bb2e47dbf778866e0f72a Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 11:11:59 +0700 Subject: [PATCH 10/35] feat: support exporting optional ref in DbmlExporter --- packages/dbml-core/src/export/DbmlExporter.ts | 78 +++++++++---------- packages/dbml-parse/src/index.ts | 1 + 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/packages/dbml-core/src/export/DbmlExporter.ts b/packages/dbml-core/src/export/DbmlExporter.ts index 02b40e1a9..f5ca143b2 100644 --- a/packages/dbml-core/src/export/DbmlExporter.ts +++ b/packages/dbml-core/src/export/DbmlExporter.ts @@ -1,5 +1,5 @@ import { groupBy, isEmpty, reduce } from 'lodash-es'; -import { addDoubleQuoteIfNeeded, formatRecordValue } from '@dbml/parse'; +import { addDoubleQuoteIfNeeded, formatRecordValue, getRelationshipOp } from '@dbml/parse'; import { shouldPrintSchema } from './utils'; import { DEFAULT_SCHEMA_NAME } from '../model_structure/config'; import type { NormalizedModel, RecordValue } from '../../types/model_structure/database'; @@ -45,9 +45,9 @@ class DbmlExporter { const schema = model.schemas[_enum.schemaId]; return `Enum ${shouldPrintSchema(schema, model) - ? `"${schema.name}".` - : ''}"${_enum.name}" {\n${ - _enum.valueIds.map((valueId) => ` "${model.enumValues[valueId].name}"${model.enumValues[valueId].note + ? `${addDoubleQuoteIfNeeded(schema.name)}.` + : ''}${addDoubleQuoteIfNeeded(_enum.name)} {\n${ + _enum.valueIds.map((valueId) => ` ${addDoubleQuoteIfNeeded(model.enumValues[valueId].name)}${model.enumValues[valueId].note ? ` [note: ${DbmlExporter.escapeNote(model.enumValues[valueId].note)}]` : ''}`).join('\n')}\n}\n`; }); @@ -63,12 +63,10 @@ class DbmlExporter { let schemaName = ''; if (field.type.schemaName && field.type.schemaName !== DEFAULT_SCHEMA_NAME) { - schemaName = DbmlExporter.hasWhiteSpace(field.type.schemaName) ? `"${field.type.schemaName}".` : `${field.type.schemaName}.`; + schemaName = `${addDoubleQuoteIfNeeded(field.type.schemaName)}.`; } - let line = `"${field.name}" ${schemaName}${DbmlExporter.hasWhiteSpace(field.type.type_name) || DbmlExporter.hasSquareBracket(field.type.type_name) - ? `"${field.type.type_name}"` - : field.type.type_name}`; + let line = `${addDoubleQuoteIfNeeded(field.name)} ${schemaName}${addDoubleQuoteIfNeeded(field.type.type_name)}`; const constraints: string[] = []; if (field.unique) { @@ -237,8 +235,8 @@ class DbmlExporter { const schema = model.schemas[table.schemaId]; const tableSettingStr = DbmlExporter.getTableSettings(table); // Include schema name if needed - let tableName = `"${table.name}"`; - if (shouldPrintSchema(schema, model)) tableName = `"${schema.name}"."${table.name}"`; + let tableName = addDoubleQuoteIfNeeded(table.name); + if (shouldPrintSchema(schema, model)) tableName = `${addDoubleQuoteIfNeeded(schema.name)}.${addDoubleQuoteIfNeeded(table.name)}`; // Include alias if present const aliasStr = table.alias ? ` as ${addDoubleQuoteIfNeeded(table.alias)}` : ''; @@ -266,49 +264,43 @@ class DbmlExporter { } static buildFieldName (fieldIds: number[], model: NormalizedModel): string { - const fieldNames = fieldIds.map((fieldId) => `"${model.fields[fieldId].name}"`).join(', '); + const fieldNames = fieldIds.map((fieldId) => addDoubleQuoteIfNeeded(model.fields[fieldId].name)).join(', '); return fieldIds.length === 1 ? fieldNames : `(${fieldNames})`; } static exportRefs (refIds: number[], model: NormalizedModel): string { const strArr = refIds.map((refId) => { const ref = model.refs[refId]; - const oneRelationEndpointIndex = ref.endpointIds.findIndex((endpointId) => model.endpoints[endpointId].relation === '1'); - const isManyToMany = oneRelationEndpointIndex === -1; - const refEndpointIndex = isManyToMany ? 0 : oneRelationEndpointIndex; - const foreignEndpointId = ref.endpointIds[1 - refEndpointIndex]; - const refEndpointId = ref.endpointIds[refEndpointIndex]; - const foreignEndpoint = model.endpoints[foreignEndpointId]; - const refEndpoint = model.endpoints[refEndpointId]; + const endpoint1 = model.endpoints[ref.endpointIds[0]]; + const endpoint2 = model.endpoints[ref.endpointIds[1]]; + const op = getRelationshipOp(endpoint1.relation, endpoint2.relation); + + const ep1Field = model.fields[endpoint1.fieldIds[0]]; + const ep1Table = model.tables[ep1Field.tableId]; + const ep1Schema = model.schemas[ep1Table.schemaId]; + const ep1FieldName = DbmlExporter.buildFieldName(endpoint1.fieldIds, model); let line = 'Ref'; - const refEndpointField = model.fields[refEndpoint.fieldIds[0]]; - const refEndpointTable = model.tables[refEndpointField.tableId]; - const refEndpointSchema = model.schemas[refEndpointTable.schemaId]; - const refEndpointFieldName = DbmlExporter.buildFieldName(refEndpoint.fieldIds, model); if (ref.name) { line += ` ${shouldPrintSchema(model.schemas[ref.schemaId], model) - ? `"${model.schemas[ref.schemaId].name}".` - : ''}"${ref.name}"`; + ? `${addDoubleQuoteIfNeeded(model.schemas[ref.schemaId].name)}.` + : ''}${addDoubleQuoteIfNeeded(ref.name)}`; } line += ':'; - line += `${shouldPrintSchema(refEndpointSchema, model) - ? `"${refEndpointSchema.name}".` - : ''}"${refEndpointTable.name}".${refEndpointFieldName} `; - - const foreignEndpointField = model.fields[foreignEndpoint.fieldIds[0]]; - const foreignEndpointTable = model.tables[foreignEndpointField.tableId]; - const foreignEndpointSchema = model.schemas[foreignEndpointTable.schemaId]; - const foreignEndpointFieldName = DbmlExporter.buildFieldName(foreignEndpoint.fieldIds, model); - - if (isManyToMany) line += '<> '; - else - if (foreignEndpoint.relation === '1') line += '- '; - else line += '< '; - line += `${shouldPrintSchema(foreignEndpointSchema, model) - ? `"${foreignEndpointSchema.name}".` - : ''}"${foreignEndpointTable.name}".${foreignEndpointFieldName}`; + line += `${shouldPrintSchema(ep1Schema, model) + ? `${addDoubleQuoteIfNeeded(ep1Schema.name)}.` + : ''}${addDoubleQuoteIfNeeded(ep1Table.name)}.${ep1FieldName} `; + + const ep2Field = model.fields[endpoint2.fieldIds[0]]; + const ep2Table = model.tables[ep2Field.tableId]; + const ep2Schema = model.schemas[ep2Table.schemaId]; + const ep2FieldName = DbmlExporter.buildFieldName(endpoint2.fieldIds, model); + + line += `${op} `; + line += `${shouldPrintSchema(ep2Schema, model) + ? `${addDoubleQuoteIfNeeded(ep2Schema.name)}.` + : ''}${addDoubleQuoteIfNeeded(ep2Table.name)}.${ep2FieldName}`; const refActions: string[] = []; if (ref.onUpdate) { @@ -346,14 +338,14 @@ class DbmlExporter { const groupSettingStr = DbmlExporter.getTableGroupSettings(group); const groupNote = group.note ? ` Note: ${DbmlExporter.escapeNote(group.note)}\n` : ''; - const groupSchemaName = shouldPrintSchema(groupSchema, model) ? `"${groupSchema.name}".` : ''; - const groupName = `${groupSchemaName}"${group.name}"`; + const groupSchemaName = shouldPrintSchema(groupSchema, model) ? `${addDoubleQuoteIfNeeded(groupSchema.name)}.` : ''; + const groupName = `${groupSchemaName}${addDoubleQuoteIfNeeded(group.name)}`; const tableNames = group.tableIds .reduce((result, tableId) => { const table = model.tables[tableId]; const tableSchema = model.schemas[table.schemaId]; - const tableName = ` ${shouldPrintSchema(tableSchema, model) ? `"${tableSchema.name}".` : ''}"${table.name}"`; + const tableName = ` ${shouldPrintSchema(tableSchema, model) ? `${addDoubleQuoteIfNeeded(tableSchema.name)}.` : ''}${addDoubleQuoteIfNeeded(table.name)}`; return `${result}${tableName}\n`; }, ''); diff --git a/packages/dbml-parse/src/index.ts b/packages/dbml-parse/src/index.ts index cef913a04..a8608bcde 100644 --- a/packages/dbml-parse/src/index.ts +++ b/packages/dbml-parse/src/index.ts @@ -89,6 +89,7 @@ export { export { type RelationCardinality, type RelationshipOp, + getRelationshipOp, } from '@/core/types/relation'; // DiagramView types From c945551c5fa695840995a901ba390dae492f0840 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 11:19:26 +0700 Subject: [PATCH 11/35] feat: export getMultiplicites --- packages/dbml-parse/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/dbml-parse/src/index.ts b/packages/dbml-parse/src/index.ts index a8608bcde..44a394070 100644 --- a/packages/dbml-parse/src/index.ts +++ b/packages/dbml-parse/src/index.ts @@ -90,6 +90,7 @@ export { type RelationCardinality, type RelationshipOp, getRelationshipOp, + getMultiplicities, } from '@/core/types/relation'; // DiagramView types From c5a701688b5f5d752820b628372875e6cb5234b4 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 11:22:43 +0700 Subject: [PATCH 12/35] feat: support relationship op in sql exporter --- packages/dbml-core/src/export/MysqlExporter.js | 3 ++- packages/dbml-core/src/export/OracleExporter.js | 3 ++- packages/dbml-core/src/export/PostgresExporter.js | 3 ++- packages/dbml-core/src/export/SqlServerExporter.js | 3 ++- packages/dbml-core/types/model_structure/endpoint.d.ts | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/dbml-core/src/export/MysqlExporter.js b/packages/dbml-core/src/export/MysqlExporter.js index 2c8ab5135..7d3f59b8b 100644 --- a/packages/dbml-core/src/export/MysqlExporter.js +++ b/packages/dbml-core/src/export/MysqlExporter.js @@ -1,4 +1,5 @@ import { + parseCardinality, isBinaryType, isBooleanType, isDateTimeType, @@ -248,7 +249,7 @@ class MySQLExporter { const strArr = refIds.map((refId) => { let line = ''; const ref = model.refs[refId]; - const refOneIndex = ref.endpointIds.findIndex((endpointId) => model.endpoints[endpointId].relation === '1'); + const refOneIndex = ref.endpointIds.findIndex((endpointId) => parseCardinality(model.endpoints[endpointId].relation).max === 1); const refEndpointIndex = refOneIndex === -1 ? 0 : refOneIndex; const foreignEndpointId = ref.endpointIds[1 - refEndpointIndex]; const refEndpointId = ref.endpointIds[refEndpointIndex]; diff --git a/packages/dbml-core/src/export/OracleExporter.js b/packages/dbml-core/src/export/OracleExporter.js index b50e108e0..9b2dd789c 100644 --- a/packages/dbml-core/src/export/OracleExporter.js +++ b/packages/dbml-core/src/export/OracleExporter.js @@ -1,4 +1,5 @@ import { + parseCardinality, isBinaryType, isBooleanType, isDateTimeType, @@ -323,7 +324,7 @@ class OracleExporter { const ref = model.refs[refId]; // find the one relation in one-to-xxx or xxx-to-one relationship - const refOneIndex = ref.endpointIds.findIndex((endpointId) => model.endpoints[endpointId].relation === '1'); + const refOneIndex = ref.endpointIds.findIndex((endpointId) => parseCardinality(model.endpoints[endpointId].relation).max === 1); const refEndpointIndex = refOneIndex === -1 ? 0 : refOneIndex; diff --git a/packages/dbml-core/src/export/PostgresExporter.js b/packages/dbml-core/src/export/PostgresExporter.js index b65f1a850..4b675c337 100644 --- a/packages/dbml-core/src/export/PostgresExporter.js +++ b/packages/dbml-core/src/export/PostgresExporter.js @@ -1,4 +1,5 @@ import { + parseCardinality, isBinaryType, isBooleanType, isDateTimeType, @@ -415,7 +416,7 @@ class PostgresExporter { const strArr = refIds.map((refId) => { let line = ''; const ref = model.refs[refId]; - const refOneIndex = ref.endpointIds.findIndex((endpointId) => model.endpoints[endpointId].relation === '1'); + const refOneIndex = ref.endpointIds.findIndex((endpointId) => parseCardinality(model.endpoints[endpointId].relation).max === 1); const refEndpointIndex = refOneIndex === -1 ? 0 : refOneIndex; const foreignEndpointId = ref.endpointIds[1 - refEndpointIndex]; const refEndpointId = ref.endpointIds[refEndpointIndex]; diff --git a/packages/dbml-core/src/export/SqlServerExporter.js b/packages/dbml-core/src/export/SqlServerExporter.js index 3e74e1fd0..22c82b61f 100644 --- a/packages/dbml-core/src/export/SqlServerExporter.js +++ b/packages/dbml-core/src/export/SqlServerExporter.js @@ -1,4 +1,5 @@ import { + parseCardinality, isBinaryType, isBooleanType, isDateTimeType, @@ -248,7 +249,7 @@ class SqlServerExporter { const strArr = refIds.map((refId) => { let line = ''; const ref = model.refs[refId]; - const refOneIndex = ref.endpointIds.findIndex((endpointId) => model.endpoints[endpointId].relation === '1'); + const refOneIndex = ref.endpointIds.findIndex((endpointId) => parseCardinality(model.endpoints[endpointId].relation).max === 1); const refEndpointIndex = refOneIndex === -1 ? 0 : refOneIndex; const foreignEndpointId = ref.endpointIds[1 - refEndpointIndex]; const refEndpointId = ref.endpointIds[refEndpointIndex]; diff --git a/packages/dbml-core/types/model_structure/endpoint.d.ts b/packages/dbml-core/types/model_structure/endpoint.d.ts index 1d9f9af53..3b3a3a562 100644 --- a/packages/dbml-core/types/model_structure/endpoint.d.ts +++ b/packages/dbml-core/types/model_structure/endpoint.d.ts @@ -15,7 +15,7 @@ export interface RawEndpoint { } declare class Endpoint extends Element { - relation: any; + relation: RelationCardinality; schemaName: string; tableName: string; fieldNames: string[]; From 26d9cde2fb3efcf74acb3395d622376d1b6802c3 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 11:40:56 +0700 Subject: [PATCH 13/35] fix: respect original export ref order --- packages/dbml-core/src/export/DbmlExporter.ts | 43 ++++++++++--------- packages/dbml-parse/src/index.ts | 1 + 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/packages/dbml-core/src/export/DbmlExporter.ts b/packages/dbml-core/src/export/DbmlExporter.ts index f5ca143b2..865b88190 100644 --- a/packages/dbml-core/src/export/DbmlExporter.ts +++ b/packages/dbml-core/src/export/DbmlExporter.ts @@ -1,5 +1,5 @@ import { groupBy, isEmpty, reduce } from 'lodash-es'; -import { addDoubleQuoteIfNeeded, formatRecordValue, getRelationshipOp } from '@dbml/parse'; +import { addDoubleQuoteIfNeeded, formatRecordValue, getRelationshipOp, parseCardinality } from '@dbml/parse'; import { shouldPrintSchema } from './utils'; import { DEFAULT_SCHEMA_NAME } from '../model_structure/config'; import type { NormalizedModel, RecordValue } from '../../types/model_structure/database'; @@ -271,14 +271,17 @@ class DbmlExporter { static exportRefs (refIds: number[], model: NormalizedModel): string { const strArr = refIds.map((refId) => { const ref = model.refs[refId]; - const endpoint1 = model.endpoints[ref.endpointIds[0]]; - const endpoint2 = model.endpoints[ref.endpointIds[1]]; - const op = getRelationshipOp(endpoint1.relation, endpoint2.relation); - - const ep1Field = model.fields[endpoint1.fieldIds[0]]; - const ep1Table = model.tables[ep1Field.tableId]; - const ep1Schema = model.schemas[ep1Table.schemaId]; - const ep1FieldName = DbmlExporter.buildFieldName(endpoint1.fieldIds, model); + // Put the "one" side on the left to preserve canonical order (one < many) + const oneIndex = ref.endpointIds.findIndex((id) => parseCardinality(model.endpoints[id].relation).max === 1); + const leftIndex = oneIndex === -1 ? 0 : oneIndex; + const leftEndpoint = model.endpoints[ref.endpointIds[leftIndex]]; + const rightEndpoint = model.endpoints[ref.endpointIds[1 - leftIndex]]; + const op = getRelationshipOp(leftEndpoint.relation, rightEndpoint.relation); + + const leftField = model.fields[leftEndpoint.fieldIds[0]]; + const leftTable = model.tables[leftField.tableId]; + const leftSchema = model.schemas[leftTable.schemaId]; + const leftFieldName = DbmlExporter.buildFieldName(leftEndpoint.fieldIds, model); let line = 'Ref'; @@ -287,20 +290,20 @@ class DbmlExporter { ? `${addDoubleQuoteIfNeeded(model.schemas[ref.schemaId].name)}.` : ''}${addDoubleQuoteIfNeeded(ref.name)}`; } - line += ':'; - line += `${shouldPrintSchema(ep1Schema, model) - ? `${addDoubleQuoteIfNeeded(ep1Schema.name)}.` - : ''}${addDoubleQuoteIfNeeded(ep1Table.name)}.${ep1FieldName} `; + line += ': '; + line += `${shouldPrintSchema(leftSchema, model) + ? `${addDoubleQuoteIfNeeded(leftSchema.name)}.` + : ''}${addDoubleQuoteIfNeeded(leftTable.name)}.${leftFieldName} `; - const ep2Field = model.fields[endpoint2.fieldIds[0]]; - const ep2Table = model.tables[ep2Field.tableId]; - const ep2Schema = model.schemas[ep2Table.schemaId]; - const ep2FieldName = DbmlExporter.buildFieldName(endpoint2.fieldIds, model); + const rightField = model.fields[rightEndpoint.fieldIds[0]]; + const rightTable = model.tables[rightField.tableId]; + const rightSchema = model.schemas[rightTable.schemaId]; + const rightFieldName = DbmlExporter.buildFieldName(rightEndpoint.fieldIds, model); line += `${op} `; - line += `${shouldPrintSchema(ep2Schema, model) - ? `${addDoubleQuoteIfNeeded(ep2Schema.name)}.` - : ''}${addDoubleQuoteIfNeeded(ep2Table.name)}.${ep2FieldName}`; + line += `${shouldPrintSchema(rightSchema, model) + ? `${addDoubleQuoteIfNeeded(rightSchema.name)}.` + : ''}${addDoubleQuoteIfNeeded(rightTable.name)}.${rightFieldName}`; const refActions: string[] = []; if (ref.onUpdate) { diff --git a/packages/dbml-parse/src/index.ts b/packages/dbml-parse/src/index.ts index 44a394070..1f1bbc18b 100644 --- a/packages/dbml-parse/src/index.ts +++ b/packages/dbml-parse/src/index.ts @@ -91,6 +91,7 @@ export { type RelationshipOp, getRelationshipOp, getMultiplicities, + parseCardinality, } from '@/core/types/relation'; // DiagramView types From 2af4ad88145773e7ff5e618b8538264234cfa858 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 11:42:53 +0700 Subject: [PATCH 14/35] fix: revert big breaking change regarding addDoubleQuoteIfNeeded --- packages/dbml-core/src/export/DbmlExporter.ts | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/packages/dbml-core/src/export/DbmlExporter.ts b/packages/dbml-core/src/export/DbmlExporter.ts index 865b88190..8631c67f0 100644 --- a/packages/dbml-core/src/export/DbmlExporter.ts +++ b/packages/dbml-core/src/export/DbmlExporter.ts @@ -1,5 +1,5 @@ import { groupBy, isEmpty, reduce } from 'lodash-es'; -import { addDoubleQuoteIfNeeded, formatRecordValue, getRelationshipOp, parseCardinality } from '@dbml/parse'; +import { formatRecordValue, getRelationshipOp, parseCardinality } from '@dbml/parse'; import { shouldPrintSchema } from './utils'; import { DEFAULT_SCHEMA_NAME } from '../model_structure/config'; import type { NormalizedModel, RecordValue } from '../../types/model_structure/database'; @@ -45,9 +45,9 @@ class DbmlExporter { const schema = model.schemas[_enum.schemaId]; return `Enum ${shouldPrintSchema(schema, model) - ? `${addDoubleQuoteIfNeeded(schema.name)}.` - : ''}${addDoubleQuoteIfNeeded(_enum.name)} {\n${ - _enum.valueIds.map((valueId) => ` ${addDoubleQuoteIfNeeded(model.enumValues[valueId].name)}${model.enumValues[valueId].note + ? `"${schema.name}".` + : ''}"${_enum.name}" {\n${ + _enum.valueIds.map((valueId) => ` "${model.enumValues[valueId].name}"${model.enumValues[valueId].note ? ` [note: ${DbmlExporter.escapeNote(model.enumValues[valueId].note)}]` : ''}`).join('\n')}\n}\n`; }); @@ -63,10 +63,12 @@ class DbmlExporter { let schemaName = ''; if (field.type.schemaName && field.type.schemaName !== DEFAULT_SCHEMA_NAME) { - schemaName = `${addDoubleQuoteIfNeeded(field.type.schemaName)}.`; + schemaName = DbmlExporter.hasWhiteSpace(field.type.schemaName) ? `"${field.type.schemaName}".` : `${field.type.schemaName}.`; } - let line = `${addDoubleQuoteIfNeeded(field.name)} ${schemaName}${addDoubleQuoteIfNeeded(field.type.type_name)}`; + let line = `"${field.name}" ${schemaName}${DbmlExporter.hasWhiteSpace(field.type.type_name) || DbmlExporter.hasSquareBracket(field.type.type_name) + ? `"${field.type.type_name}"` + : field.type.type_name}`; const constraints: string[] = []; if (field.unique) { @@ -235,8 +237,8 @@ class DbmlExporter { const schema = model.schemas[table.schemaId]; const tableSettingStr = DbmlExporter.getTableSettings(table); // Include schema name if needed - let tableName = addDoubleQuoteIfNeeded(table.name); - if (shouldPrintSchema(schema, model)) tableName = `${addDoubleQuoteIfNeeded(schema.name)}.${addDoubleQuoteIfNeeded(table.name)}`; + let tableName = `"${table.name}"`; + if (shouldPrintSchema(schema, model)) tableName = `"${schema.name}"."${table.name}"`; // Include alias if present const aliasStr = table.alias ? ` as ${addDoubleQuoteIfNeeded(table.alias)}` : ''; @@ -264,7 +266,7 @@ class DbmlExporter { } static buildFieldName (fieldIds: number[], model: NormalizedModel): string { - const fieldNames = fieldIds.map((fieldId) => addDoubleQuoteIfNeeded(model.fields[fieldId].name)).join(', '); + const fieldNames = fieldIds.map((fieldId) => `"${model.fields[fieldId].name}"`).join(', '); return fieldIds.length === 1 ? fieldNames : `(${fieldNames})`; } @@ -287,13 +289,13 @@ class DbmlExporter { if (ref.name) { line += ` ${shouldPrintSchema(model.schemas[ref.schemaId], model) - ? `${addDoubleQuoteIfNeeded(model.schemas[ref.schemaId].name)}.` - : ''}${addDoubleQuoteIfNeeded(ref.name)}`; + ? `"${model.schemas[ref.schemaId].name}".` + : ''}"${ref.name}"`; } line += ': '; line += `${shouldPrintSchema(leftSchema, model) - ? `${addDoubleQuoteIfNeeded(leftSchema.name)}.` - : ''}${addDoubleQuoteIfNeeded(leftTable.name)}.${leftFieldName} `; + ? `"${leftSchema.name}".` + : ''}"${leftTable.name}".${leftFieldName} `; const rightField = model.fields[rightEndpoint.fieldIds[0]]; const rightTable = model.tables[rightField.tableId]; @@ -302,8 +304,8 @@ class DbmlExporter { line += `${op} `; line += `${shouldPrintSchema(rightSchema, model) - ? `${addDoubleQuoteIfNeeded(rightSchema.name)}.` - : ''}${addDoubleQuoteIfNeeded(rightTable.name)}.${rightFieldName}`; + ? `"${rightSchema.name}".` + : ''}"${rightTable.name}".${rightFieldName}`; const refActions: string[] = []; if (ref.onUpdate) { @@ -341,14 +343,14 @@ class DbmlExporter { const groupSettingStr = DbmlExporter.getTableGroupSettings(group); const groupNote = group.note ? ` Note: ${DbmlExporter.escapeNote(group.note)}\n` : ''; - const groupSchemaName = shouldPrintSchema(groupSchema, model) ? `${addDoubleQuoteIfNeeded(groupSchema.name)}.` : ''; - const groupName = `${groupSchemaName}${addDoubleQuoteIfNeeded(group.name)}`; + const groupSchemaName = shouldPrintSchema(groupSchema, model) ? `"${groupSchema.name}".` : ''; + const groupName = `${groupSchemaName}"${group.name}"`; const tableNames = group.tableIds .reduce((result, tableId) => { const table = model.tables[tableId]; const tableSchema = model.schemas[table.schemaId]; - const tableName = ` ${shouldPrintSchema(tableSchema, model) ? `${addDoubleQuoteIfNeeded(tableSchema.name)}.` : ''}${addDoubleQuoteIfNeeded(table.name)}`; + const tableName = ` ${shouldPrintSchema(tableSchema, model) ? `"${tableSchema.name}".` : ''}"${table.name}"`; return `${result}${tableName}\n`; }, ''); From 823fa63f5bb9a474630fbf48c56ff101d4f457d3 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 11:45:30 +0700 Subject: [PATCH 15/35] test: add space after : --- .../output/general_schema.out.dbml | 12 +- .../output/general_schema.out.dbml | 12 +- .../output/multiple_schema.out.dbml | 12 +- .../output/shorthand_syntax.out.dbml | 4 +- .../mysql_importer/output/ddl_create.out.dbml | 2 +- .../output/general_schema.out.dbml | 18 +- .../output/legacy_issues.out.dbml | 4 +- .../mysql_importer/output/lower_case.out.dbml | 18 +- .../output/multiple_schema.out.dbml | 16 +- .../output/alter_table_add_fk.out.dbml | 24 +-- .../output/alter_table_add_pk.out.dbml | 2 +- .../output/alter_table_add_unique.out.dbml | 2 +- .../alter_table_check_constraints.out.dbml | 2 +- .../output/alter_table_set_nullable.out.dbml | 2 +- .../output/column_def_fk.out.dbml | 82 ++++---- .../output/create_table.out.dbml | 2 +- .../output/in_table_fk.out.dbml | 26 +-- .../output/quoted_names.out.dbml | 2 +- .../output/cmd_create_table.out.dbml | 4 +- .../postgres_importer/output/db_dump.out.dbml | 180 +++++++++--------- .../output/general_schema.out.dbml | 16 +- .../output/multiple_schema.out.dbml | 10 +- .../output/shorthand_syntax.out.dbml | 4 +- .../snowflake_importer/output/alter.out.dbml | 2 +- .../output/create_table.out.dbml | 26 +-- .../output/column_settings.out.dbml | 16 +- .../output/general_schema.out.dbml | 12 +- .../output/referential_actions.out.dbml | 8 +- .../output/sticky_notes.out.dbml | 6 +- .../output/table_group_settings.out.dbml | 16 +- 30 files changed, 271 insertions(+), 271 deletions(-) diff --git a/packages/dbml-core/__tests__/examples/importer/json_importer/output/general_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/json_importer/output/general_schema.out.dbml index 4ba4e7f91..1bbc1b2ce 100644 --- a/packages/dbml-core/__tests__/examples/importer/json_importer/output/general_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/json_importer/output/general_schema.out.dbml @@ -61,14 +61,14 @@ Table "countries" { "continent_name" varchar } -Ref:"orders"."id" < "order_items"."order_id" +Ref: "orders"."id" < "order_items"."order_id" -Ref:"products"."id" < "order_items"."product_id" +Ref: "products"."id" < "order_items"."product_id" -Ref:"countries"."code" < "users"."country_code" +Ref: "countries"."code" < "users"."country_code" -Ref:"countries"."code" < "merchants"."country_code" +Ref: "countries"."code" < "merchants"."country_code" -Ref:"merchants"."id" < "products"."merchant_id" +Ref: "merchants"."id" < "products"."merchant_id" -Ref:"users"."id" < "merchants"."admin_id" +Ref: "users"."id" < "merchants"."admin_id" diff --git a/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/general_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/general_schema.out.dbml index 4458f5269..d0e8f22e4 100644 --- a/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/general_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/general_schema.out.dbml @@ -50,14 +50,14 @@ Table "countries" { "continent_name" varchar(255) } -Ref:"orders"."id" < "order_items"."order_id" +Ref: "orders"."id" < "order_items"."order_id" -Ref:"products"."id" < "order_items"."product_id" +Ref: "products"."id" < "order_items"."product_id" -Ref:"countries"."code" < "users"."country_code" +Ref: "countries"."code" < "users"."country_code" -Ref:"countries"."code" < "merchants"."country_code" +Ref: "countries"."code" < "merchants"."country_code" -Ref:"merchants"."id" < "products"."merchant_id" +Ref: "merchants"."id" < "products"."merchant_id" -Ref:"users"."id" < "merchants"."admin_id" +Ref: "users"."id" < "merchants"."admin_id" diff --git a/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/multiple_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/multiple_schema.out.dbml index f5531b77e..5cbc6d1bb 100644 --- a/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/multiple_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/multiple_schema.out.dbml @@ -12,17 +12,17 @@ Table "products" { "name" nvarchar(255) [note: 'Product name'] } -Ref:"schemaA"."locations"."id" < "schemaA"."products"."lid" +Ref: "schemaA"."locations"."id" < "schemaA"."products"."lid" -Ref "FK_1":"schemaA"."locations"."id" < "schemaA"."products"."lid2" +Ref "FK_1": "schemaA"."locations"."id" < "schemaA"."products"."lid2" -Ref:"users"."id" < "ecommerce"."users"."id" +Ref: "users"."id" < "ecommerce"."users"."id" -Ref "name_optional":"users"."name" < "ecommerce"."users"."id" +Ref "name_optional": "users"."name" < "ecommerce"."users"."id" -Ref:"ecommerce"."users"."id" < "schemaA"."products"."name" +Ref: "ecommerce"."users"."id" < "schemaA"."products"."name" -Ref:"users"."id" < "schemaA"."locations"."name" +Ref: "users"."id" < "schemaA"."locations"."name" Table "ecommerce"."users" { "id" int [pk] diff --git a/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/shorthand_syntax.out.dbml b/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/shorthand_syntax.out.dbml index 5b5bfff33..cc0781dcc 100644 --- a/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/shorthand_syntax.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/shorthand_syntax.out.dbml @@ -35,6 +35,6 @@ Table "users" { "country_code" int } -Ref "fk_country_code":"countries"."code" < "users"."country_code" +Ref "fk_country_code": "countries"."code" < "users"."country_code" -Ref "fk_composite":"booking_reference".("reference_id", "cust_id") < "br_flight".("reference_id", "cust_id") +Ref "fk_composite": "booking_reference".("reference_id", "cust_id") < "br_flight".("reference_id", "cust_id") diff --git a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/ddl_create.out.dbml b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/ddl_create.out.dbml index c001ef8f7..4ceb44970 100644 --- a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/ddl_create.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/ddl_create.out.dbml @@ -531,4 +531,4 @@ Table "tab1" { "mi" MIDDLEINT } -Ref:"parent_table"."id" < "child_table"."id_parent" [update: cascade, delete: set null] +Ref: "parent_table"."id" < "child_table"."id_parent" [update: cascade, delete: set null] diff --git a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/general_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/general_schema.out.dbml index 32a519a96..1b1563b0e 100644 --- a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/general_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/general_schema.out.dbml @@ -111,20 +111,20 @@ Table "Cities" { } } -Ref "FK_States_Countries_CountryId":"Countries"."Id" < "States"."CountryId" [delete: cascade] +Ref "FK_States_Countries_CountryId": "Countries"."Id" < "States"."CountryId" [delete: cascade] -Ref "FK_Cities_Countries_CountryId":"Countries"."Id" < "Cities"."CountryId" [delete: cascade] +Ref "FK_Cities_Countries_CountryId": "Countries"."Id" < "Cities"."CountryId" [delete: cascade] -Ref "FK_Cities_States_StateId_CountryId":"States".("Id", "CountryId") < "Cities".("StateId", "CountryId") [delete: cascade] +Ref "FK_Cities_States_StateId_CountryId": "States".("Id", "CountryId") < "Cities".("StateId", "CountryId") [delete: cascade] -Ref:"orders"."id" < "order_items"."order_id" +Ref: "orders"."id" < "order_items"."order_id" -Ref:"products"."id" < "order_items"."product_id" +Ref: "products"."id" < "order_items"."product_id" -Ref:"countries"."code" < "users"."country_code" +Ref: "countries"."code" < "users"."country_code" -Ref:"countries"."code" < "merchants"."country_code" +Ref: "countries"."code" < "merchants"."country_code" -Ref:"merchants"."id" < "products"."merchant_id" +Ref: "merchants"."id" < "products"."merchant_id" -Ref:"users"."id" < "merchants"."admin_id" +Ref: "users"."id" < "merchants"."admin_id" diff --git a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/legacy_issues.out.dbml b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/legacy_issues.out.dbml index 2be98b1b2..97c69f161 100644 --- a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/legacy_issues.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/legacy_issues.out.dbml @@ -61,6 +61,6 @@ Table "example" { "id" varchar(255) } -Ref "user_role_links_fk_1":"users"."id" < "user_role_links"."user_id" +Ref "user_role_links_fk_1": "users"."id" < "user_role_links"."user_id" -Ref "user_role_links_fk_2":"user_roles"."id" < "user_role_links"."role_id" +Ref "user_role_links_fk_2": "user_roles"."id" < "user_role_links"."role_id" diff --git a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/lower_case.out.dbml b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/lower_case.out.dbml index 32a519a96..1b1563b0e 100644 --- a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/lower_case.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/lower_case.out.dbml @@ -111,20 +111,20 @@ Table "Cities" { } } -Ref "FK_States_Countries_CountryId":"Countries"."Id" < "States"."CountryId" [delete: cascade] +Ref "FK_States_Countries_CountryId": "Countries"."Id" < "States"."CountryId" [delete: cascade] -Ref "FK_Cities_Countries_CountryId":"Countries"."Id" < "Cities"."CountryId" [delete: cascade] +Ref "FK_Cities_Countries_CountryId": "Countries"."Id" < "Cities"."CountryId" [delete: cascade] -Ref "FK_Cities_States_StateId_CountryId":"States".("Id", "CountryId") < "Cities".("StateId", "CountryId") [delete: cascade] +Ref "FK_Cities_States_StateId_CountryId": "States".("Id", "CountryId") < "Cities".("StateId", "CountryId") [delete: cascade] -Ref:"orders"."id" < "order_items"."order_id" +Ref: "orders"."id" < "order_items"."order_id" -Ref:"products"."id" < "order_items"."product_id" +Ref: "products"."id" < "order_items"."product_id" -Ref:"countries"."code" < "users"."country_code" +Ref: "countries"."code" < "users"."country_code" -Ref:"countries"."code" < "merchants"."country_code" +Ref: "countries"."code" < "merchants"."country_code" -Ref:"merchants"."id" < "products"."merchant_id" +Ref: "merchants"."id" < "products"."merchant_id" -Ref:"users"."id" < "merchants"."admin_id" +Ref: "users"."id" < "merchants"."admin_id" diff --git a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/multiple_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/multiple_schema.out.dbml index 530a0f1ce..a95181637 100644 --- a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/multiple_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/multiple_schema.out.dbml @@ -70,21 +70,21 @@ Table "orders4" { } } -Ref "FK_1":"schemaA"."locations"."id" < "schemaA"."products"."lid" [delete: cascade] +Ref "FK_1": "schemaA"."locations"."id" < "schemaA"."products"."lid" [delete: cascade] -Ref:"schemaA"."products"."id" < "orders"."pid" +Ref: "schemaA"."products"."id" < "orders"."pid" -Ref:"schemaA"."products"."id" < "orders2"."pid" +Ref: "schemaA"."products"."id" < "orders2"."pid" -Ref "CFK_1":"orders".("id1", "id2") < "orders2".("id1", "id2") [update: set null, delete: cascade] +Ref "CFK_1": "orders".("id1", "id2") < "orders2".("id1", "id2") [update: set null, delete: cascade] -Ref:"users"."id" < "ecommerce"."users"."id" +Ref: "users"."id" < "ecommerce"."users"."id" -Ref "name_optional":"users"."name" < "ecommerce"."users"."id" +Ref "name_optional": "users"."name" < "ecommerce"."users"."id" -Ref:"ecommerce"."users"."id" < "schemaA"."products"."name" +Ref: "ecommerce"."users"."id" < "schemaA"."products"."name" -Ref:"users"."id" < "schemaA"."locations"."name" +Ref: "users"."id" < "schemaA"."locations"."name" Enum "ecommerce"."users_ejs_enum" { "created2" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_fk.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_fk.out.dbml index 1a17848dd..9974c65d9 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_fk.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_fk.out.dbml @@ -58,26 +58,26 @@ Table "task_hours" { "hours_worked" NUMBER(5,2) } -Ref "fk_emp_dept":"departments"."dept_id" < "employees"."department_id" +Ref "fk_emp_dept": "departments"."dept_id" < "employees"."department_id" -Ref "fk_dept_location":"locations"."location_id" < "departments"."location_id" +Ref "fk_dept_location": "locations"."location_id" < "departments"."location_id" -Ref "fk_loc_country":"countries"."country_code" < "locations"."country_code" +Ref "fk_loc_country": "countries"."country_code" < "locations"."country_code" -Ref "fk_emp_manager":"employees"."emp_id" < "employees"."manager_id" +Ref "fk_emp_manager": "employees"."emp_id" < "employees"."manager_id" -Ref "fk_dept_manager":"employees"."emp_id" < "departments"."manager_id" +Ref "fk_dept_manager": "employees"."emp_id" < "departments"."manager_id" -Ref:"departments"."dept_id" < "projects"."dept_id" +Ref: "departments"."dept_id" < "projects"."dept_id" -Ref:"employees"."emp_id" < "assignments"."emp_id" +Ref: "employees"."emp_id" < "assignments"."emp_id" -Ref:"projects"."project_id" < "assignments"."project_id" +Ref: "projects"."project_id" < "assignments"."project_id" -Ref "fk_task_hours_project_task":"project_tasks".("project_id", "task_id") < "task_hours".("project_id", "task_id") +Ref "fk_task_hours_project_task": "project_tasks".("project_id", "task_id") < "task_hours".("project_id", "task_id") -Ref "fk_proj_lead_emp":"employees"."emp_id" < "projects"."lead_emp_id" +Ref "fk_proj_lead_emp": "employees"."emp_id" < "projects"."lead_emp_id" -Ref "fk_proj_backup_emp":"employees"."emp_id" < "projects"."backup_emp_id" +Ref "fk_proj_backup_emp": "employees"."emp_id" < "projects"."backup_emp_id" -Ref "fk_task_assigned_emp":"employees"."emp_id" < "project_tasks"."assigned_emp_id" +Ref "fk_task_assigned_emp": "employees"."emp_id" < "project_tasks"."assigned_emp_id" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_pk.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_pk.out.dbml index f228e3d50..82c66b0da 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_pk.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_pk.out.dbml @@ -61,4 +61,4 @@ Table "combo_test" { "col2" VARCHAR2(50) } -Ref "fk_complex_parent":"complex_test"."id" < "complex_test"."parent_id" +Ref "fk_complex_parent": "complex_test"."id" < "complex_test"."parent_id" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_unique.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_unique.out.dbml index ea56339ff..dda02e64f 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_unique.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_unique.out.dbml @@ -61,4 +61,4 @@ Table "combo_test" { "col2" VARCHAR2(50) } -Ref "fk_complex_parent":"complex_test"."id" < "complex_test"."parent_id" +Ref "fk_complex_parent": "complex_test"."id" < "complex_test"."parent_id" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_check_constraints.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_check_constraints.out.dbml index bfe70496c..ee254aa1c 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_check_constraints.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_check_constraints.out.dbml @@ -67,4 +67,4 @@ Table "combo_test" { "col2" VARCHAR2(50) } -Ref "fk_complex_parent":"complex_test"."id" < "complex_test"."parent_id" +Ref "fk_complex_parent": "complex_test"."id" < "complex_test"."parent_id" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_set_nullable.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_set_nullable.out.dbml index 61b640b1f..4478d2698 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_set_nullable.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_set_nullable.out.dbml @@ -63,4 +63,4 @@ Table "combo_test" { "col2" VARCHAR2(50) } -Ref "fk_complex_parent":"complex_test"."id" < "complex_test"."parent_id" +Ref "fk_complex_parent": "complex_test"."id" < "complex_test"."parent_id" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/column_def_fk.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/column_def_fk.out.dbml index fb5eb5838..0a3d2fc62 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/column_def_fk.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/column_def_fk.out.dbml @@ -188,84 +188,84 @@ Table "test_set_null" { "cust_id" NUMBER(10) } -Ref "fk_emp_dept":"departments"."dept_id" < "employees"."dept_id" +Ref "fk_emp_dept": "departments"."dept_id" < "employees"."dept_id" -Ref "fk_emp_manager":"employees"."emp_id" < "employees"."manager_id" +Ref "fk_emp_manager": "employees"."emp_id" < "employees"."manager_id" -Ref "fk_order_cust":"customers"."cust_id" < "orders"."customer_id" +Ref "fk_order_cust": "customers"."cust_id" < "orders"."customer_id" -Ref "fk_order_track":"shipments"."tracking_number" < "orders"."tracking_number" +Ref "fk_order_track": "shipments"."tracking_number" < "orders"."tracking_number" -Ref "fk_ship_cust":"customers"."cust_id" < "shipments"."customer_id" +Ref "fk_ship_cust": "customers"."cust_id" < "shipments"."customer_id" -Ref "fk_child_complex":"complex_test"."id" < "complex_child"."parent_id" +Ref "fk_child_complex": "complex_test"."id" < "complex_child"."parent_id" -Ref "fk_child_code":"complex_test"."code" < "complex_child"."parent_code" +Ref "fk_child_code": "complex_test"."code" < "complex_child"."parent_code" -Ref "fk_enable_dept":"departments"."dept_id" < "test_enable"."dept_id" +Ref "fk_enable_dept": "departments"."dept_id" < "test_enable"."dept_id" -Ref "fk_enable_supp":"suppliers"."supp_id" < "test_enable"."supp_id" +Ref "fk_enable_supp": "suppliers"."supp_id" < "test_enable"."supp_id" -Ref "fk_disable_dept":"departments"."dept_id" < "test_disable"."dept_id" +Ref "fk_disable_dept": "departments"."dept_id" < "test_disable"."dept_id" -Ref "fk_disable_cust":"customers"."cust_id" < "test_disable"."cust_id" +Ref "fk_disable_cust": "customers"."cust_id" < "test_disable"."cust_id" -Ref "fk_validate_item":"inventory"."item_id" < "test_validate"."item_id" +Ref "fk_validate_item": "inventory"."item_id" < "test_validate"."item_id" -Ref "fk_validate_supp":"suppliers"."supp_id" < "test_validate"."supp_id" +Ref "fk_validate_supp": "suppliers"."supp_id" < "test_validate"."supp_id" -Ref "fk_noval_dept":"departments"."dept_id" < "test_novalidate"."dept_id" +Ref "fk_noval_dept": "departments"."dept_id" < "test_novalidate"."dept_id" -Ref "fk_noval_cust":"customers"."cust_id" < "test_novalidate"."cust_id" +Ref "fk_noval_cust": "customers"."cust_id" < "test_novalidate"."cust_id" -Ref "fk_dis_noval_item":"inventory"."item_id" < "test_disable_novalidate"."item_id" +Ref "fk_dis_noval_item": "inventory"."item_id" < "test_disable_novalidate"."item_id" -Ref "fk_dis_noval_supp":"suppliers"."supp_id" < "test_disable_novalidate"."supp_id" +Ref "fk_dis_noval_supp": "suppliers"."supp_id" < "test_disable_novalidate"."supp_id" -Ref "fk_rely_dept":"departments"."dept_id" < "test_rely"."dept_id" +Ref "fk_rely_dept": "departments"."dept_id" < "test_rely"."dept_id" -Ref "fk_rely_cust":"customers"."cust_id" < "test_rely"."cust_id" +Ref "fk_rely_cust": "customers"."cust_id" < "test_rely"."cust_id" -Ref "fk_norely_item":"inventory"."item_id" < "test_norely"."item_id" +Ref "fk_norely_item": "inventory"."item_id" < "test_norely"."item_id" -Ref "fk_norely_supp":"suppliers"."supp_id" < "test_norely"."supp_id" +Ref "fk_norely_supp": "suppliers"."supp_id" < "test_norely"."supp_id" -Ref "fk_en_rely_dept":"departments"."dept_id" < "test_enable_rely"."dept_id" +Ref "fk_en_rely_dept": "departments"."dept_id" < "test_enable_rely"."dept_id" -Ref "fk_en_rely_cust":"customers"."cust_id" < "test_enable_rely"."cust_id" +Ref "fk_en_rely_cust": "customers"."cust_id" < "test_enable_rely"."cust_id" -Ref "fk_dis_norely_item":"inventory"."item_id" < "test_disable_norely"."item_id" +Ref "fk_dis_norely_item": "inventory"."item_id" < "test_disable_norely"."item_id" -Ref "fk_dis_norely_supp":"suppliers"."supp_id" < "test_disable_norely"."supp_id" +Ref "fk_dis_norely_supp": "suppliers"."supp_id" < "test_disable_norely"."supp_id" -Ref "fk_def_deferred_dept":"departments"."dept_id" < "test_deferrable_deferred"."dept_id" +Ref "fk_def_deferred_dept": "departments"."dept_id" < "test_deferrable_deferred"."dept_id" -Ref "fk_def_deferred_cust":"customers"."cust_id" < "test_deferrable_deferred"."cust_id" +Ref "fk_def_deferred_cust": "customers"."cust_id" < "test_deferrable_deferred"."cust_id" -Ref "fk_def_immed_item":"inventory"."item_id" < "test_deferrable_immediate"."item_id" +Ref "fk_def_immed_item": "inventory"."item_id" < "test_deferrable_immediate"."item_id" -Ref "fk_def_immed_supp":"suppliers"."supp_id" < "test_deferrable_immediate"."supp_id" +Ref "fk_def_immed_supp": "suppliers"."supp_id" < "test_deferrable_immediate"."supp_id" -Ref "fk_not_def_dept":"departments"."dept_id" < "test_not_deferrable"."dept_id" +Ref "fk_not_def_dept": "departments"."dept_id" < "test_not_deferrable"."dept_id" -Ref "fk_not_def_cust":"customers"."cust_id" < "test_not_deferrable"."cust_id" +Ref "fk_not_def_cust": "customers"."cust_id" < "test_not_deferrable"."cust_id" -Ref "fk_dt_number":"test_datatypes"."col_number" < "test_datatypes_fk"."col_number" +Ref "fk_dt_number": "test_datatypes"."col_number" < "test_datatypes_fk"."col_number" -Ref "fk_dt_varchar2":"test_datatypes"."col_varchar2" < "test_datatypes_fk"."col_varchar2" +Ref "fk_dt_varchar2": "test_datatypes"."col_varchar2" < "test_datatypes_fk"."col_varchar2" -Ref "fk_self_emp":"employees_self"."emp_id" < "employees_self"."manager_id" +Ref "fk_self_emp": "employees_self"."emp_id" < "employees_self"."manager_id" -Ref "fk_self_dept":"departments"."dept_id" < "employees_self"."dept_id" +Ref "fk_self_dept": "departments"."dept_id" < "employees_self"."dept_id" -Ref "fk_detail_item":"inventory"."item_id" < "order_details"."item_id" +Ref "fk_detail_item": "inventory"."item_id" < "order_details"."item_id" -Ref "fk_detail_cust":"customers"."cust_id" < "order_details"."cust_id" +Ref "fk_detail_cust": "customers"."cust_id" < "order_details"."cust_id" -Ref "fk_cascade_dept":"departments"."dept_id" < "test_cascade"."dept_id" +Ref "fk_cascade_dept": "departments"."dept_id" < "test_cascade"."dept_id" -Ref "fk_cascade_supp":"suppliers"."supp_id" < "test_cascade"."supp_id" +Ref "fk_cascade_supp": "suppliers"."supp_id" < "test_cascade"."supp_id" -Ref "fk_set_null_dept":"departments"."dept_id" < "test_set_null"."dept_id" +Ref "fk_set_null_dept": "departments"."dept_id" < "test_set_null"."dept_id" -Ref "fk_set_null_cust":"customers"."cust_id" < "test_set_null"."cust_id" +Ref "fk_set_null_cust": "customers"."cust_id" < "test_set_null"."cust_id" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/create_table.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/create_table.out.dbml index 4a44240d5..496b9ba5b 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/create_table.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/create_table.out.dbml @@ -159,7 +159,7 @@ Table "Order Details#2023" { } } -Ref "fk_order_cust":"customers"."cust_id" < "order details"."cust_id" +Ref "fk_order_cust": "customers"."cust_id" < "order details"."cust_id" Table "hr"."employees" { "emp_id" NUMBER(10) diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/in_table_fk.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/in_table_fk.out.dbml index ef5b090fe..39d22d8d6 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/in_table_fk.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/in_table_fk.out.dbml @@ -98,28 +98,28 @@ Table "project_assignments" { "project_id" NUMBER(10) } -Ref:"departments"."dept_id" < "employees"."dept_id" +Ref: "departments"."dept_id" < "employees"."dept_id" -Ref "fk_order_cust":"customers"."cust_id" < "orders"."customer_id" +Ref "fk_order_cust": "customers"."cust_id" < "orders"."customer_id" -Ref "fk_ship_cust":"customers"."cust_id" < "shipments"."customer_id" +Ref "fk_ship_cust": "customers"."cust_id" < "shipments"."customer_id" -Ref "fk_child_complex":"complex_parent".("id", "code") < "complex_child".("parent_id", "parent_code") +Ref "fk_child_complex": "complex_parent".("id", "code") < "complex_child".("parent_id", "parent_code") -Ref "fk_detail_item":"inventory"."item_id" < "order_details"."item_id" +Ref "fk_detail_item": "inventory"."item_id" < "order_details"."item_id" -Ref "fk_assign_dept":"departments"."dept_id" < "assignments"."dept_id" +Ref "fk_assign_dept": "departments"."dept_id" < "assignments"."dept_id" -Ref "fk_member_dept":"departments"."dept_id" < "project_members"."dept_id" +Ref "fk_member_dept": "departments"."dept_id" < "project_members"."dept_id" -Ref "fk_dt_number":"test_datatypes"."col_number" < "test_datatypes_fk"."col_number" +Ref "fk_dt_number": "test_datatypes"."col_number" < "test_datatypes_fk"."col_number" -Ref "fk_dt_varchar2":"test_datatypes"."col_varchar2" < "test_datatypes_fk"."col_varchar2" +Ref "fk_dt_varchar2": "test_datatypes"."col_varchar2" < "test_datatypes_fk"."col_varchar2" -Ref "fk_self_manager":"employees_self"."emp_id" < "employees_self"."manager_id" +Ref "fk_self_manager": "employees_self"."emp_id" < "employees_self"."manager_id" -Ref "fk_very_long_constraint_name_for_testing_parser_limits":"orders"."order_id" < "order_history"."order_id" +Ref "fk_very_long_constraint_name_for_testing_parser_limits": "orders"."order_id" < "order_history"."order_id" -Ref "fk_ship_detail":"shipments"."ship_id" < "shipment_details"."ship_id" +Ref "fk_ship_detail": "shipments"."ship_id" < "shipment_details"."ship_id" -Ref "fk_assign_emp":"employees"."emp_id" < "project_assignments"."emp_id" +Ref "fk_assign_emp": "employees"."emp_id" < "project_assignments"."emp_id" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/quoted_names.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/quoted_names.out.dbml index 4cf4f079d..0dc90eae3 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/quoted_names.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/quoted_names.out.dbml @@ -20,4 +20,4 @@ Table "silver users" { } } -Ref " foreign key bd ":"silver users"."date of birth" < " aba "."date of birth" +Ref " foreign key bd ": "silver users"."date of birth" < " aba "."date of birth" diff --git a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/cmd_create_table.out.dbml b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/cmd_create_table.out.dbml index c2bcfbdf9..f6588d53b 100644 --- a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/cmd_create_table.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/cmd_create_table.out.dbml @@ -42,6 +42,6 @@ Table "foo" { "bar15" "character varying[76]" } -Ref:"a"."id" < "b"."id" [delete: no action] +Ref: "a"."id" < "b"."id" [delete: no action] -Ref "c_time_constraint":"a"."name" < "c"."time" +Ref "c_time_constraint": "a"."name" < "c"."time" diff --git a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/db_dump.out.dbml b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/db_dump.out.dbml index 62e8e53ec..cd6d62528 100644 --- a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/db_dump.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/db_dump.out.dbml @@ -998,182 +998,182 @@ Table "sales"."salestaxrate" { Note: 'Tax rate lookup table.' } -Ref "FK_EmployeeDepartmentHistory_Department_DepartmentID":"humanresources"."department"."departmentid" < "humanresources"."employeedepartmenthistory"."departmentid" +Ref "FK_EmployeeDepartmentHistory_Department_DepartmentID": "humanresources"."department"."departmentid" < "humanresources"."employeedepartmenthistory"."departmentid" -Ref "FK_EmployeeDepartmentHistory_Employee_BusinessEntityID":"humanresources"."employee"."businessentityid" < "humanresources"."employeedepartmenthistory"."businessentityid" +Ref "FK_EmployeeDepartmentHistory_Employee_BusinessEntityID": "humanresources"."employee"."businessentityid" < "humanresources"."employeedepartmenthistory"."businessentityid" -Ref "FK_EmployeeDepartmentHistory_Shift_ShiftID":"humanresources"."shift"."shiftid" < "humanresources"."employeedepartmenthistory"."shiftid" +Ref "FK_EmployeeDepartmentHistory_Shift_ShiftID": "humanresources"."shift"."shiftid" < "humanresources"."employeedepartmenthistory"."shiftid" -Ref "FK_EmployeePayHistory_Employee_BusinessEntityID":"humanresources"."employee"."businessentityid" < "humanresources"."employeepayhistory"."businessentityid" +Ref "FK_EmployeePayHistory_Employee_BusinessEntityID": "humanresources"."employee"."businessentityid" < "humanresources"."employeepayhistory"."businessentityid" -Ref "FK_Employee_Person_BusinessEntityID":"person"."person"."businessentityid" < "humanresources"."employee"."businessentityid" +Ref "FK_Employee_Person_BusinessEntityID": "person"."person"."businessentityid" < "humanresources"."employee"."businessentityid" -Ref "FK_JobCandidate_Employee_BusinessEntityID":"humanresources"."employee"."businessentityid" < "humanresources"."jobcandidate"."businessentityid" +Ref "FK_JobCandidate_Employee_BusinessEntityID": "humanresources"."employee"."businessentityid" < "humanresources"."jobcandidate"."businessentityid" -Ref "FK_Address_StateProvince_StateProvinceID":"person"."stateprovince"."stateprovinceid" < "person"."address"."stateprovinceid" +Ref "FK_Address_StateProvince_StateProvinceID": "person"."stateprovince"."stateprovinceid" < "person"."address"."stateprovinceid" -Ref "FK_BusinessEntityAddress_AddressType_AddressTypeID":"person"."addresstype"."addresstypeid" < "person"."businessentityaddress"."addresstypeid" +Ref "FK_BusinessEntityAddress_AddressType_AddressTypeID": "person"."addresstype"."addresstypeid" < "person"."businessentityaddress"."addresstypeid" -Ref "FK_BusinessEntityAddress_Address_AddressID":"person"."address"."addressid" < "person"."businessentityaddress"."addressid" +Ref "FK_BusinessEntityAddress_Address_AddressID": "person"."address"."addressid" < "person"."businessentityaddress"."addressid" -Ref "FK_BusinessEntityAddress_BusinessEntity_BusinessEntityID":"person"."businessentity"."businessentityid" < "person"."businessentityaddress"."businessentityid" +Ref "FK_BusinessEntityAddress_BusinessEntity_BusinessEntityID": "person"."businessentity"."businessentityid" < "person"."businessentityaddress"."businessentityid" -Ref "FK_BusinessEntityContact_BusinessEntity_BusinessEntityID":"person"."businessentity"."businessentityid" < "person"."businessentitycontact"."businessentityid" +Ref "FK_BusinessEntityContact_BusinessEntity_BusinessEntityID": "person"."businessentity"."businessentityid" < "person"."businessentitycontact"."businessentityid" -Ref "FK_BusinessEntityContact_ContactType_ContactTypeID":"person"."contacttype"."contacttypeid" < "person"."businessentitycontact"."contacttypeid" +Ref "FK_BusinessEntityContact_ContactType_ContactTypeID": "person"."contacttype"."contacttypeid" < "person"."businessentitycontact"."contacttypeid" -Ref "FK_BusinessEntityContact_Person_PersonID":"person"."person"."businessentityid" < "person"."businessentitycontact"."personid" +Ref "FK_BusinessEntityContact_Person_PersonID": "person"."person"."businessentityid" < "person"."businessentitycontact"."personid" -Ref "FK_EmailAddress_Person_BusinessEntityID":"person"."person"."businessentityid" < "person"."emailaddress"."businessentityid" +Ref "FK_EmailAddress_Person_BusinessEntityID": "person"."person"."businessentityid" < "person"."emailaddress"."businessentityid" -Ref "FK_Password_Person_BusinessEntityID":"person"."person"."businessentityid" < "person"."password"."businessentityid" +Ref "FK_Password_Person_BusinessEntityID": "person"."person"."businessentityid" < "person"."password"."businessentityid" -Ref "FK_PersonPhone_Person_BusinessEntityID":"person"."person"."businessentityid" < "person"."personphone"."businessentityid" +Ref "FK_PersonPhone_Person_BusinessEntityID": "person"."person"."businessentityid" < "person"."personphone"."businessentityid" -Ref "FK_PersonPhone_PhoneNumberType_PhoneNumberTypeID":"person"."phonenumbertype"."phonenumbertypeid" < "person"."personphone"."phonenumbertypeid" +Ref "FK_PersonPhone_PhoneNumberType_PhoneNumberTypeID": "person"."phonenumbertype"."phonenumbertypeid" < "person"."personphone"."phonenumbertypeid" -Ref "FK_Person_BusinessEntity_BusinessEntityID":"person"."businessentity"."businessentityid" < "person"."person"."businessentityid" +Ref "FK_Person_BusinessEntity_BusinessEntityID": "person"."businessentity"."businessentityid" < "person"."person"."businessentityid" -Ref "FK_StateProvince_CountryRegion_CountryRegionCode":"person"."countryregion"."countryregioncode" < "person"."stateprovince"."countryregioncode" +Ref "FK_StateProvince_CountryRegion_CountryRegionCode": "person"."countryregion"."countryregioncode" < "person"."stateprovince"."countryregioncode" -Ref "FK_StateProvince_SalesTerritory_TerritoryID":"sales"."salesterritory"."territoryid" < "person"."stateprovince"."territoryid" +Ref "FK_StateProvince_SalesTerritory_TerritoryID": "sales"."salesterritory"."territoryid" < "person"."stateprovince"."territoryid" -Ref "FK_BillOfMaterials_Product_ComponentID":"production"."product"."productid" < "production"."billofmaterials"."componentid" +Ref "FK_BillOfMaterials_Product_ComponentID": "production"."product"."productid" < "production"."billofmaterials"."componentid" -Ref "FK_BillOfMaterials_Product_ProductAssemblyID":"production"."product"."productid" < "production"."billofmaterials"."productassemblyid" +Ref "FK_BillOfMaterials_Product_ProductAssemblyID": "production"."product"."productid" < "production"."billofmaterials"."productassemblyid" -Ref "FK_BillOfMaterials_UnitMeasure_UnitMeasureCode":"production"."unitmeasure"."unitmeasurecode" < "production"."billofmaterials"."unitmeasurecode" +Ref "FK_BillOfMaterials_UnitMeasure_UnitMeasureCode": "production"."unitmeasure"."unitmeasurecode" < "production"."billofmaterials"."unitmeasurecode" -Ref "FK_Document_Employee_Owner":"humanresources"."employee"."businessentityid" < "production"."document"."owner" +Ref "FK_Document_Employee_Owner": "humanresources"."employee"."businessentityid" < "production"."document"."owner" -Ref "FK_ProductCostHistory_Product_ProductID":"production"."product"."productid" < "production"."productcosthistory"."productid" +Ref "FK_ProductCostHistory_Product_ProductID": "production"."product"."productid" < "production"."productcosthistory"."productid" -Ref "FK_ProductDocument_Document_DocumentNode":"production"."document"."documentnode" < "production"."productdocument"."documentnode" +Ref "FK_ProductDocument_Document_DocumentNode": "production"."document"."documentnode" < "production"."productdocument"."documentnode" -Ref "FK_ProductDocument_Product_ProductID":"production"."product"."productid" < "production"."productdocument"."productid" +Ref "FK_ProductDocument_Product_ProductID": "production"."product"."productid" < "production"."productdocument"."productid" -Ref "FK_ProductInventory_Location_LocationID":"production"."location"."locationid" < "production"."productinventory"."locationid" +Ref "FK_ProductInventory_Location_LocationID": "production"."location"."locationid" < "production"."productinventory"."locationid" -Ref "FK_ProductInventory_Product_ProductID":"production"."product"."productid" < "production"."productinventory"."productid" +Ref "FK_ProductInventory_Product_ProductID": "production"."product"."productid" < "production"."productinventory"."productid" -Ref "FK_ProductListPriceHistory_Product_ProductID":"production"."product"."productid" < "production"."productlistpricehistory"."productid" +Ref "FK_ProductListPriceHistory_Product_ProductID": "production"."product"."productid" < "production"."productlistpricehistory"."productid" -Ref "FK_ProductModelIllustration_Illustration_IllustrationID":"production"."illustration"."illustrationid" < "production"."productmodelillustration"."illustrationid" +Ref "FK_ProductModelIllustration_Illustration_IllustrationID": "production"."illustration"."illustrationid" < "production"."productmodelillustration"."illustrationid" -Ref "FK_ProductModelIllustration_ProductModel_ProductModelID":"production"."productmodel"."productmodelid" < "production"."productmodelillustration"."productmodelid" +Ref "FK_ProductModelIllustration_ProductModel_ProductModelID": "production"."productmodel"."productmodelid" < "production"."productmodelillustration"."productmodelid" -Ref "FK_ProductModelProductDescriptionCulture_Culture_CultureID":"production"."culture"."cultureid" < "production"."productmodelproductdescriptionculture"."cultureid" +Ref "FK_ProductModelProductDescriptionCulture_Culture_CultureID": "production"."culture"."cultureid" < "production"."productmodelproductdescriptionculture"."cultureid" -Ref "FK_ProductModelProductDescriptionCulture_ProductDescription_Pro":"production"."productdescription"."productdescriptionid" < "production"."productmodelproductdescriptionculture"."productdescriptionid" +Ref "FK_ProductModelProductDescriptionCulture_ProductDescription_Pro": "production"."productdescription"."productdescriptionid" < "production"."productmodelproductdescriptionculture"."productdescriptionid" -Ref "FK_ProductModelProductDescriptionCulture_ProductModel_ProductMo":"production"."productmodel"."productmodelid" < "production"."productmodelproductdescriptionculture"."productmodelid" +Ref "FK_ProductModelProductDescriptionCulture_ProductModel_ProductMo": "production"."productmodel"."productmodelid" < "production"."productmodelproductdescriptionculture"."productmodelid" -Ref "FK_ProductProductPhoto_ProductPhoto_ProductPhotoID":"production"."productphoto"."productphotoid" < "production"."productproductphoto"."productphotoid" +Ref "FK_ProductProductPhoto_ProductPhoto_ProductPhotoID": "production"."productphoto"."productphotoid" < "production"."productproductphoto"."productphotoid" -Ref "FK_ProductProductPhoto_Product_ProductID":"production"."product"."productid" < "production"."productproductphoto"."productid" +Ref "FK_ProductProductPhoto_Product_ProductID": "production"."product"."productid" < "production"."productproductphoto"."productid" -Ref "FK_ProductReview_Product_ProductID":"production"."product"."productid" < "production"."productreview"."productid" +Ref "FK_ProductReview_Product_ProductID": "production"."product"."productid" < "production"."productreview"."productid" -Ref "FK_ProductSubcategory_ProductCategory_ProductCategoryID":"production"."productcategory"."productcategoryid" < "production"."productsubcategory"."productcategoryid" +Ref "FK_ProductSubcategory_ProductCategory_ProductCategoryID": "production"."productcategory"."productcategoryid" < "production"."productsubcategory"."productcategoryid" -Ref "FK_Product_ProductModel_ProductModelID":"production"."productmodel"."productmodelid" < "production"."product"."productmodelid" +Ref "FK_Product_ProductModel_ProductModelID": "production"."productmodel"."productmodelid" < "production"."product"."productmodelid" -Ref "FK_Product_ProductSubcategory_ProductSubcategoryID":"production"."productsubcategory"."productsubcategoryid" < "production"."product"."productsubcategoryid" +Ref "FK_Product_ProductSubcategory_ProductSubcategoryID": "production"."productsubcategory"."productsubcategoryid" < "production"."product"."productsubcategoryid" -Ref "FK_Product_UnitMeasure_SizeUnitMeasureCode":"production"."unitmeasure"."unitmeasurecode" < "production"."product"."sizeunitmeasurecode" +Ref "FK_Product_UnitMeasure_SizeUnitMeasureCode": "production"."unitmeasure"."unitmeasurecode" < "production"."product"."sizeunitmeasurecode" -Ref "FK_Product_UnitMeasure_WeightUnitMeasureCode":"production"."unitmeasure"."unitmeasurecode" < "production"."product"."weightunitmeasurecode" +Ref "FK_Product_UnitMeasure_WeightUnitMeasureCode": "production"."unitmeasure"."unitmeasurecode" < "production"."product"."weightunitmeasurecode" -Ref "FK_TransactionHistory_Product_ProductID":"production"."product"."productid" < "production"."transactionhistory"."productid" +Ref "FK_TransactionHistory_Product_ProductID": "production"."product"."productid" < "production"."transactionhistory"."productid" -Ref "FK_WorkOrderRouting_Location_LocationID":"production"."location"."locationid" < "production"."workorderrouting"."locationid" +Ref "FK_WorkOrderRouting_Location_LocationID": "production"."location"."locationid" < "production"."workorderrouting"."locationid" -Ref "FK_WorkOrderRouting_WorkOrder_WorkOrderID":"production"."workorder"."workorderid" < "production"."workorderrouting"."workorderid" +Ref "FK_WorkOrderRouting_WorkOrder_WorkOrderID": "production"."workorder"."workorderid" < "production"."workorderrouting"."workorderid" -Ref "FK_WorkOrder_Product_ProductID":"production"."product"."productid" < "production"."workorder"."productid" +Ref "FK_WorkOrder_Product_ProductID": "production"."product"."productid" < "production"."workorder"."productid" -Ref "FK_WorkOrder_ScrapReason_ScrapReasonID":"production"."scrapreason"."scrapreasonid" < "production"."workorder"."scrapreasonid" +Ref "FK_WorkOrder_ScrapReason_ScrapReasonID": "production"."scrapreason"."scrapreasonid" < "production"."workorder"."scrapreasonid" -Ref "FK_ProductVendor_Product_ProductID":"production"."product"."productid" < "purchasing"."productvendor"."productid" +Ref "FK_ProductVendor_Product_ProductID": "production"."product"."productid" < "purchasing"."productvendor"."productid" -Ref "FK_ProductVendor_UnitMeasure_UnitMeasureCode":"production"."unitmeasure"."unitmeasurecode" < "purchasing"."productvendor"."unitmeasurecode" +Ref "FK_ProductVendor_UnitMeasure_UnitMeasureCode": "production"."unitmeasure"."unitmeasurecode" < "purchasing"."productvendor"."unitmeasurecode" -Ref "FK_ProductVendor_Vendor_BusinessEntityID":"purchasing"."vendor"."businessentityid" < "purchasing"."productvendor"."businessentityid" +Ref "FK_ProductVendor_Vendor_BusinessEntityID": "purchasing"."vendor"."businessentityid" < "purchasing"."productvendor"."businessentityid" -Ref "FK_PurchaseOrderDetail_Product_ProductID":"production"."product"."productid" < "purchasing"."purchaseorderdetail"."productid" +Ref "FK_PurchaseOrderDetail_Product_ProductID": "production"."product"."productid" < "purchasing"."purchaseorderdetail"."productid" -Ref "FK_PurchaseOrderDetail_PurchaseOrderHeader_PurchaseOrderID":"purchasing"."purchaseorderheader"."purchaseorderid" < "purchasing"."purchaseorderdetail"."purchaseorderid" +Ref "FK_PurchaseOrderDetail_PurchaseOrderHeader_PurchaseOrderID": "purchasing"."purchaseorderheader"."purchaseorderid" < "purchasing"."purchaseorderdetail"."purchaseorderid" -Ref "FK_PurchaseOrderHeader_Employee_EmployeeID":"humanresources"."employee"."businessentityid" < "purchasing"."purchaseorderheader"."employeeid" +Ref "FK_PurchaseOrderHeader_Employee_EmployeeID": "humanresources"."employee"."businessentityid" < "purchasing"."purchaseorderheader"."employeeid" -Ref "FK_PurchaseOrderHeader_ShipMethod_ShipMethodID":"purchasing"."shipmethod"."shipmethodid" < "purchasing"."purchaseorderheader"."shipmethodid" +Ref "FK_PurchaseOrderHeader_ShipMethod_ShipMethodID": "purchasing"."shipmethod"."shipmethodid" < "purchasing"."purchaseorderheader"."shipmethodid" -Ref "FK_PurchaseOrderHeader_Vendor_VendorID":"purchasing"."vendor"."businessentityid" < "purchasing"."purchaseorderheader"."vendorid" +Ref "FK_PurchaseOrderHeader_Vendor_VendorID": "purchasing"."vendor"."businessentityid" < "purchasing"."purchaseorderheader"."vendorid" -Ref "FK_Vendor_BusinessEntity_BusinessEntityID":"person"."businessentity"."businessentityid" < "purchasing"."vendor"."businessentityid" +Ref "FK_Vendor_BusinessEntity_BusinessEntityID": "person"."businessentity"."businessentityid" < "purchasing"."vendor"."businessentityid" -Ref "FK_CountryRegionCurrency_CountryRegion_CountryRegionCode":"person"."countryregion"."countryregioncode" < "sales"."countryregioncurrency"."countryregioncode" +Ref "FK_CountryRegionCurrency_CountryRegion_CountryRegionCode": "person"."countryregion"."countryregioncode" < "sales"."countryregioncurrency"."countryregioncode" -Ref "FK_CountryRegionCurrency_Currency_CurrencyCode":"sales"."currency"."currencycode" < "sales"."countryregioncurrency"."currencycode" +Ref "FK_CountryRegionCurrency_Currency_CurrencyCode": "sales"."currency"."currencycode" < "sales"."countryregioncurrency"."currencycode" -Ref "FK_CurrencyRate_Currency_FromCurrencyCode":"sales"."currency"."currencycode" < "sales"."currencyrate"."fromcurrencycode" +Ref "FK_CurrencyRate_Currency_FromCurrencyCode": "sales"."currency"."currencycode" < "sales"."currencyrate"."fromcurrencycode" -Ref "FK_CurrencyRate_Currency_ToCurrencyCode":"sales"."currency"."currencycode" < "sales"."currencyrate"."tocurrencycode" +Ref "FK_CurrencyRate_Currency_ToCurrencyCode": "sales"."currency"."currencycode" < "sales"."currencyrate"."tocurrencycode" -Ref "FK_Customer_Person_PersonID":"person"."person"."businessentityid" < "sales"."customer"."personid" +Ref "FK_Customer_Person_PersonID": "person"."person"."businessentityid" < "sales"."customer"."personid" -Ref "FK_Customer_SalesTerritory_TerritoryID":"sales"."salesterritory"."territoryid" < "sales"."customer"."territoryid" +Ref "FK_Customer_SalesTerritory_TerritoryID": "sales"."salesterritory"."territoryid" < "sales"."customer"."territoryid" -Ref "FK_Customer_Store_StoreID":"sales"."store"."businessentityid" < "sales"."customer"."storeid" +Ref "FK_Customer_Store_StoreID": "sales"."store"."businessentityid" < "sales"."customer"."storeid" -Ref "FK_PersonCreditCard_CreditCard_CreditCardID":"sales"."creditcard"."creditcardid" < "sales"."personcreditcard"."creditcardid" +Ref "FK_PersonCreditCard_CreditCard_CreditCardID": "sales"."creditcard"."creditcardid" < "sales"."personcreditcard"."creditcardid" -Ref "FK_PersonCreditCard_Person_BusinessEntityID":"person"."person"."businessentityid" < "sales"."personcreditcard"."businessentityid" +Ref "FK_PersonCreditCard_Person_BusinessEntityID": "person"."person"."businessentityid" < "sales"."personcreditcard"."businessentityid" -Ref "FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID":"sales"."salesorderheader"."salesorderid" < "sales"."salesorderdetail"."salesorderid" [delete: cascade] +Ref "FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID": "sales"."salesorderheader"."salesorderid" < "sales"."salesorderdetail"."salesorderid" [delete: cascade] -Ref "FK_SalesOrderDetail_SpecialOfferProduct_SpecialOfferIDProductID":"sales"."specialofferproduct".("specialofferid", "productid") < "sales"."salesorderdetail".("specialofferid", "productid") +Ref "FK_SalesOrderDetail_SpecialOfferProduct_SpecialOfferIDProductID": "sales"."specialofferproduct".("specialofferid", "productid") < "sales"."salesorderdetail".("specialofferid", "productid") -Ref "FK_SalesOrderHeaderSalesReason_SalesOrderHeader_SalesOrderID":"sales"."salesorderheader"."salesorderid" < "sales"."salesorderheadersalesreason"."salesorderid" [delete: cascade] +Ref "FK_SalesOrderHeaderSalesReason_SalesOrderHeader_SalesOrderID": "sales"."salesorderheader"."salesorderid" < "sales"."salesorderheadersalesreason"."salesorderid" [delete: cascade] -Ref "FK_SalesOrderHeaderSalesReason_SalesReason_SalesReasonID":"sales"."salesreason"."salesreasonid" < "sales"."salesorderheadersalesreason"."salesreasonid" +Ref "FK_SalesOrderHeaderSalesReason_SalesReason_SalesReasonID": "sales"."salesreason"."salesreasonid" < "sales"."salesorderheadersalesreason"."salesreasonid" -Ref "FK_SalesOrderHeader_Address_BillToAddressID":"person"."address"."addressid" < "sales"."salesorderheader"."billtoaddressid" +Ref "FK_SalesOrderHeader_Address_BillToAddressID": "person"."address"."addressid" < "sales"."salesorderheader"."billtoaddressid" -Ref "FK_SalesOrderHeader_Address_ShipToAddressID":"person"."address"."addressid" < "sales"."salesorderheader"."shiptoaddressid" +Ref "FK_SalesOrderHeader_Address_ShipToAddressID": "person"."address"."addressid" < "sales"."salesorderheader"."shiptoaddressid" -Ref "FK_SalesOrderHeader_CreditCard_CreditCardID":"sales"."creditcard"."creditcardid" < "sales"."salesorderheader"."creditcardid" +Ref "FK_SalesOrderHeader_CreditCard_CreditCardID": "sales"."creditcard"."creditcardid" < "sales"."salesorderheader"."creditcardid" -Ref "FK_SalesOrderHeader_CurrencyRate_CurrencyRateID":"sales"."currencyrate"."currencyrateid" < "sales"."salesorderheader"."currencyrateid" +Ref "FK_SalesOrderHeader_CurrencyRate_CurrencyRateID": "sales"."currencyrate"."currencyrateid" < "sales"."salesorderheader"."currencyrateid" -Ref "FK_SalesOrderHeader_Customer_CustomerID":"sales"."customer"."customerid" < "sales"."salesorderheader"."customerid" +Ref "FK_SalesOrderHeader_Customer_CustomerID": "sales"."customer"."customerid" < "sales"."salesorderheader"."customerid" -Ref "FK_SalesOrderHeader_SalesPerson_SalesPersonID":"sales"."salesperson"."businessentityid" < "sales"."salesorderheader"."salespersonid" +Ref "FK_SalesOrderHeader_SalesPerson_SalesPersonID": "sales"."salesperson"."businessentityid" < "sales"."salesorderheader"."salespersonid" -Ref "FK_SalesOrderHeader_SalesTerritory_TerritoryID":"sales"."salesterritory"."territoryid" < "sales"."salesorderheader"."territoryid" +Ref "FK_SalesOrderHeader_SalesTerritory_TerritoryID": "sales"."salesterritory"."territoryid" < "sales"."salesorderheader"."territoryid" -Ref "FK_SalesOrderHeader_ShipMethod_ShipMethodID":"purchasing"."shipmethod"."shipmethodid" < "sales"."salesorderheader"."shipmethodid" +Ref "FK_SalesOrderHeader_ShipMethod_ShipMethodID": "purchasing"."shipmethod"."shipmethodid" < "sales"."salesorderheader"."shipmethodid" -Ref "FK_SalesPersonQuotaHistory_SalesPerson_BusinessEntityID":"sales"."salesperson"."businessentityid" < "sales"."salespersonquotahistory"."businessentityid" +Ref "FK_SalesPersonQuotaHistory_SalesPerson_BusinessEntityID": "sales"."salesperson"."businessentityid" < "sales"."salespersonquotahistory"."businessentityid" -Ref "FK_SalesPerson_Employee_BusinessEntityID":"humanresources"."employee"."businessentityid" < "sales"."salesperson"."businessentityid" +Ref "FK_SalesPerson_Employee_BusinessEntityID": "humanresources"."employee"."businessentityid" < "sales"."salesperson"."businessentityid" -Ref "FK_SalesPerson_SalesTerritory_TerritoryID":"sales"."salesterritory"."territoryid" < "sales"."salesperson"."territoryid" +Ref "FK_SalesPerson_SalesTerritory_TerritoryID": "sales"."salesterritory"."territoryid" < "sales"."salesperson"."territoryid" -Ref "FK_SalesTaxRate_StateProvince_StateProvinceID":"person"."stateprovince"."stateprovinceid" < "sales"."salestaxrate"."stateprovinceid" +Ref "FK_SalesTaxRate_StateProvince_StateProvinceID": "person"."stateprovince"."stateprovinceid" < "sales"."salestaxrate"."stateprovinceid" -Ref "FK_SalesTerritoryHistory_SalesPerson_BusinessEntityID":"sales"."salesperson"."businessentityid" < "sales"."salesterritoryhistory"."businessentityid" +Ref "FK_SalesTerritoryHistory_SalesPerson_BusinessEntityID": "sales"."salesperson"."businessentityid" < "sales"."salesterritoryhistory"."businessentityid" -Ref "FK_SalesTerritoryHistory_SalesTerritory_TerritoryID":"sales"."salesterritory"."territoryid" < "sales"."salesterritoryhistory"."territoryid" +Ref "FK_SalesTerritoryHistory_SalesTerritory_TerritoryID": "sales"."salesterritory"."territoryid" < "sales"."salesterritoryhistory"."territoryid" -Ref "FK_SalesTerritory_CountryRegion_CountryRegionCode":"person"."countryregion"."countryregioncode" < "sales"."salesterritory"."countryregioncode" +Ref "FK_SalesTerritory_CountryRegion_CountryRegionCode": "person"."countryregion"."countryregioncode" < "sales"."salesterritory"."countryregioncode" -Ref "FK_ShoppingCartItem_Product_ProductID":"production"."product"."productid" < "sales"."shoppingcartitem"."productid" +Ref "FK_ShoppingCartItem_Product_ProductID": "production"."product"."productid" < "sales"."shoppingcartitem"."productid" -Ref "FK_SpecialOfferProduct_Product_ProductID":"production"."product"."productid" < "sales"."specialofferproduct"."productid" +Ref "FK_SpecialOfferProduct_Product_ProductID": "production"."product"."productid" < "sales"."specialofferproduct"."productid" -Ref "FK_SpecialOfferProduct_SpecialOffer_SpecialOfferID":"sales"."specialoffer"."specialofferid" < "sales"."specialofferproduct"."specialofferid" +Ref "FK_SpecialOfferProduct_SpecialOffer_SpecialOfferID": "sales"."specialoffer"."specialofferid" < "sales"."specialofferproduct"."specialofferid" -Ref "FK_Store_BusinessEntity_BusinessEntityID":"person"."businessentity"."businessentityid" < "sales"."store"."businessentityid" +Ref "FK_Store_BusinessEntity_BusinessEntityID": "person"."businessentity"."businessentityid" < "sales"."store"."businessentityid" -Ref "FK_Store_SalesPerson_SalesPersonID":"sales"."salesperson"."businessentityid" < "sales"."store"."salespersonid" +Ref "FK_Store_SalesPerson_SalesPersonID": "sales"."salesperson"."businessentityid" < "sales"."store"."salespersonid" diff --git a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/general_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/general_schema.out.dbml index efb3b0813..5adfa4fad 100644 --- a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/general_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/general_schema.out.dbml @@ -138,18 +138,18 @@ Table "test_table" { } } -Ref:"orders"."id" < "order_items"."order_id" +Ref: "orders"."id" < "order_items"."order_id" -Ref:"products"."id" < "order_items"."product_id" +Ref: "products"."id" < "order_items"."product_id" -Ref:"countries"."code" < "users"."country_code" +Ref: "countries"."code" < "users"."country_code" -Ref:"countries"."code" < "merchants"."country_code" +Ref: "countries"."code" < "merchants"."country_code" -Ref:"merchants"."id" < "products"."merchant_id" +Ref: "merchants"."id" < "products"."merchant_id" -Ref:"users"."id" < "merchants"."admin_id" +Ref: "users"."id" < "merchants"."admin_id" -Ref:"table1".("field", "field2") < "table2".("field", "field2") +Ref: "table1".("field", "field2") < "table2".("field", "field2") -Ref "test_table_fk":"test_table".("name", "id") < "test_table".("linked_name", "id") +Ref "test_table_fk": "test_table".("name", "id") < "test_table".("linked_name", "id") diff --git a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/multiple_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/multiple_schema.out.dbml index d76041a88..dc97f7757 100644 --- a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/multiple_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/multiple_schema.out.dbml @@ -28,15 +28,15 @@ Table "products" { } } -Ref "fk_1":"schemaA"."locations"."id" < "schemaA"."products"."lid" +Ref "fk_1": "schemaA"."locations"."id" < "schemaA"."products"."lid" -Ref:"users"."id" < "ecommerce"."users"."id" +Ref: "users"."id" < "ecommerce"."users"."id" -Ref "name_optional":"users"."name" < "ecommerce"."users"."id" +Ref "name_optional": "users"."name" < "ecommerce"."users"."id" -Ref:"ecommerce"."users"."id" < "schemaA"."products"."name" +Ref: "ecommerce"."users"."id" < "schemaA"."products"."name" -Ref:"users"."id" < "schemaA"."locations"."name" +Ref: "users"."id" < "schemaA"."locations"."name" Enum "schemaB"."gender" { "male" diff --git a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/shorthand_syntax.out.dbml b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/shorthand_syntax.out.dbml index 8bc5dc5c6..fdf16c4e7 100644 --- a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/shorthand_syntax.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/shorthand_syntax.out.dbml @@ -35,6 +35,6 @@ Table "countries" { "continent_name" varchar } -Ref "fk_country_code":"countries"."code" < "users"."country_code" +Ref "fk_country_code": "countries"."code" < "users"."country_code" -Ref "fk_composite":"booking_reference".("reference_id", "cust_id") < "br_flight".("reference_id", "cust_id") +Ref "fk_composite": "booking_reference".("reference_id", "cust_id") < "br_flight".("reference_id", "cust_id") diff --git a/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/alter.out.dbml b/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/alter.out.dbml index c31496b19..907ed8700 100644 --- a/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/alter.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/alter.out.dbml @@ -25,4 +25,4 @@ Table "employees" { "department" VARCHAR(50) } -Ref:"customers"."customer_id" < "orders"."customer_id" +Ref: "customers"."customer_id" < "orders"."customer_id" diff --git a/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/create_table.out.dbml b/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/create_table.out.dbml index 054442bca..dabb8fae5 100644 --- a/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/create_table.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/create_table.out.dbml @@ -446,28 +446,28 @@ Table "floor" { "getdate" int } -Ref:"HP_TEST"."CUSTOMERS"."CUSTOMER_ID" < "HP_TEST"."ORDERS"."CUSTOMER_ID" +Ref: "HP_TEST"."CUSTOMERS"."CUSTOMER_ID" < "HP_TEST"."ORDERS"."CUSTOMER_ID" -Ref:"HP_TEST"."ORDERS"."ORDER_ID" < "HP_TEST"."ORDER_ITEMS"."ORDER_ID" +Ref: "HP_TEST"."ORDERS"."ORDER_ID" < "HP_TEST"."ORDER_ITEMS"."ORDER_ID" -Ref:"HP_TEST"."PRODUCTS"."PRODUCT_ID" < "HP_TEST"."ORDER_ITEMS"."PRODUCT_ID" +Ref: "HP_TEST"."PRODUCTS"."PRODUCT_ID" < "HP_TEST"."ORDER_ITEMS"."PRODUCT_ID" -Ref:"TEST_2024_06_28_10_01_ZQMIMY"."ONE".("BAR", "FOO") < "TEST_2024_06_28_10_01_ZQMIMY"."TWO".("XYZ", "ABC") +Ref: "TEST_2024_06_28_10_01_ZQMIMY"."ONE".("BAR", "FOO") < "TEST_2024_06_28_10_01_ZQMIMY"."TWO".("XYZ", "ABC") -Ref:"TEST_2024_06_28_10_01_ZQMIMY"."ONE"."FOO" < "TEST_2024_06_28_10_01_ZQMIMY"."TWO"."QWE" +Ref: "TEST_2024_06_28_10_01_ZQMIMY"."ONE"."FOO" < "TEST_2024_06_28_10_01_ZQMIMY"."TWO"."QWE" -Ref:"T_OUT_2"."C2" < "T_OUT_3"."C3" +Ref: "T_OUT_2"."C2" < "T_OUT_3"."C3" -Ref:"T_OUT_5"."C2" < "T_OUT_6"."C3" +Ref: "T_OUT_5"."C2" < "T_OUT_6"."C3" -Ref:"T_OUT_5"."C2" < "T_CONSTRAINT_3"."C3" +Ref: "T_OUT_5"."C2" < "T_CONSTRAINT_3"."C3" -Ref:"T_OUT_5"."C2" < "T_CONSTRAINT_4"."C3" +Ref: "T_OUT_5"."C2" < "T_CONSTRAINT_4"."C3" -Ref:"T_OUT_5"."C2" < "T_CONSTRAINT_5"."C3" +Ref: "T_OUT_5"."C2" < "T_CONSTRAINT_5"."C3" -Ref:"T_OUT_5"."C2" < "T_CONSTRAINT_6"."C3" +Ref: "T_OUT_5"."C2" < "T_CONSTRAINT_6"."C3" -Ref:"T_OUT_5"."C2" < "T_CONSTRAINT_8"."C3" +Ref: "T_OUT_5"."C2" < "T_CONSTRAINT_8"."C3" -Ref:"T_OUT_5"."C2" < "T_CONSTRAINT_9"."C3" +Ref: "T_OUT_5"."C2" < "T_CONSTRAINT_9"."C3" diff --git a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/column_settings.out.dbml b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/column_settings.out.dbml index 98ce82870..934f53849 100644 --- a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/column_settings.out.dbml +++ b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/column_settings.out.dbml @@ -84,18 +84,18 @@ TableGroup "e_commerce_orders" [color: #24BAB1] { "ecommerce"."order_items" } -Ref:"users"."id" < "ecommerce"."merchants"."admin_id" +Ref: "users"."id" < "ecommerce"."merchants"."admin_id" -Ref:"countries"."code" < "users"."country_code" +Ref: "countries"."code" < "users"."country_code" -Ref:"countries"."code" < "ecommerce"."merchants"."country_code" +Ref: "countries"."code" < "ecommerce"."merchants"."country_code" -Ref:"ecommerce"."orders"."id" < "ecommerce"."order_items"."order_id" +Ref: "ecommerce"."orders"."id" < "ecommerce"."order_items"."order_id" -Ref:"ecommerce"."products"."id" < "ecommerce"."order_items"."product_id" +Ref: "ecommerce"."products"."id" < "ecommerce"."order_items"."product_id" -Ref:"ecommerce"."merchants"."id" < "ecommerce"."products"."merchant_id" +Ref: "ecommerce"."merchants"."id" < "ecommerce"."products"."merchant_id" -Ref:"ecommerce"."product_tags"."id" <> "ecommerce"."products"."id" +Ref: "ecommerce"."product_tags"."id" <> "ecommerce"."products"."id" -Ref:"ecommerce"."merchants".("id", "country_code") < "ecommerce"."merchant_periods".("merchant_id", "country_code") +Ref: "ecommerce"."merchants".("id", "country_code") < "ecommerce"."merchant_periods".("merchant_id", "country_code") diff --git a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/general_schema.out.dbml b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/general_schema.out.dbml index 4ba4e7f91..1bbc1b2ce 100644 --- a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/general_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/general_schema.out.dbml @@ -61,14 +61,14 @@ Table "countries" { "continent_name" varchar } -Ref:"orders"."id" < "order_items"."order_id" +Ref: "orders"."id" < "order_items"."order_id" -Ref:"products"."id" < "order_items"."product_id" +Ref: "products"."id" < "order_items"."product_id" -Ref:"countries"."code" < "users"."country_code" +Ref: "countries"."code" < "users"."country_code" -Ref:"countries"."code" < "merchants"."country_code" +Ref: "countries"."code" < "merchants"."country_code" -Ref:"merchants"."id" < "products"."merchant_id" +Ref: "merchants"."id" < "products"."merchant_id" -Ref:"users"."id" < "merchants"."admin_id" +Ref: "users"."id" < "merchants"."admin_id" diff --git a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/referential_actions.out.dbml b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/referential_actions.out.dbml index cc4900773..56bcf9366 100644 --- a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/referential_actions.out.dbml +++ b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/referential_actions.out.dbml @@ -38,10 +38,10 @@ Table "countries" { "continent_name" varchar(255) } -Ref:"users"."id" < "orders"."user_id" [delete: restrict] +Ref: "users"."id" < "orders"."user_id" [delete: restrict] -Ref:"orders"."id" < "order_items"."order_id" [delete: cascade] +Ref: "orders"."id" < "order_items"."order_id" [delete: cascade] -Ref:"products".("id", "name") < "order_items".("product_id", "product_name") [delete: set null] +Ref: "products".("id", "name") < "order_items".("product_id", "product_name") [delete: set null] -Ref:"countries"."code" < "users"."country_code" [delete: no action] +Ref: "countries"."code" < "users"."country_code" [delete: no action] diff --git a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/sticky_notes.out.dbml b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/sticky_notes.out.dbml index 3bcd1ae5e..03705e5ab 100644 --- a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/sticky_notes.out.dbml +++ b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/sticky_notes.out.dbml @@ -20,11 +20,11 @@ Table "posts" { "created_at" timestamp } -Ref:"users"."id" < "posts"."user_id" +Ref: "users"."id" < "posts"."user_id" -Ref:"users"."id" < "follows"."following_user_id" +Ref: "users"."id" < "follows"."following_user_id" -Ref:"users"."id" < "follows"."followed_user_id" +Ref: "users"."id" < "follows"."followed_user_id" Note note1 { 'sticky note with one line content' diff --git a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/table_group_settings.out.dbml b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/table_group_settings.out.dbml index e62c1feaf..319beea71 100644 --- a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/table_group_settings.out.dbml +++ b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/table_group_settings.out.dbml @@ -90,18 +90,18 @@ TableGroup "e_commerce_orders" [color: #24BAB1] { "ecommerce"."order_items" } -Ref:"users"."id" < "ecommerce"."merchants"."admin_id" +Ref: "users"."id" < "ecommerce"."merchants"."admin_id" -Ref:"countries"."code" < "users"."country_code" +Ref: "countries"."code" < "users"."country_code" -Ref:"countries"."code" < "ecommerce"."merchants"."country_code" +Ref: "countries"."code" < "ecommerce"."merchants"."country_code" -Ref:"ecommerce"."orders"."id" < "ecommerce"."order_items"."order_id" +Ref: "ecommerce"."orders"."id" < "ecommerce"."order_items"."order_id" -Ref:"ecommerce"."products"."id" < "ecommerce"."order_items"."product_id" +Ref: "ecommerce"."products"."id" < "ecommerce"."order_items"."product_id" -Ref:"ecommerce"."merchants"."id" < "ecommerce"."products"."merchant_id" +Ref: "ecommerce"."merchants"."id" < "ecommerce"."products"."merchant_id" -Ref:"ecommerce"."product_tags"."id" <> "ecommerce"."products"."id" +Ref: "ecommerce"."product_tags"."id" <> "ecommerce"."products"."id" -Ref:"ecommerce"."merchants".("id", "country_code") < "ecommerce"."merchant_periods".("merchant_id", "country_code") +Ref: "ecommerce"."merchants".("id", "country_code") < "ecommerce"."merchant_periods".("merchant_id", "country_code") From f74727f7566a38d2597086f082d8a639454892bc Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 11:50:11 +0700 Subject: [PATCH 16/35] fix: revert big breaking change regarding space after : --- .../output/general_schema.out.dbml | 12 +- .../output/general_schema.out.dbml | 12 +- .../output/multiple_schema.out.dbml | 12 +- .../output/shorthand_syntax.out.dbml | 4 +- .../mysql_importer/output/ddl_create.out.dbml | 2 +- .../output/general_schema.out.dbml | 18 +- .../output/legacy_issues.out.dbml | 4 +- .../mysql_importer/output/lower_case.out.dbml | 18 +- .../output/multiple_schema.out.dbml | 16 +- .../output/alter_table_add_fk.out.dbml | 24 +-- .../output/alter_table_add_pk.out.dbml | 2 +- .../output/alter_table_add_unique.out.dbml | 2 +- .../alter_table_check_constraints.out.dbml | 2 +- .../output/alter_table_set_nullable.out.dbml | 2 +- .../output/column_def_fk.out.dbml | 82 ++++---- .../output/create_table.out.dbml | 2 +- .../output/in_table_fk.out.dbml | 26 +-- .../output/quoted_names.out.dbml | 2 +- .../output/cmd_create_table.out.dbml | 4 +- .../postgres_importer/output/db_dump.out.dbml | 180 +++++++++--------- .../output/general_schema.out.dbml | 16 +- .../output/multiple_schema.out.dbml | 10 +- .../output/shorthand_syntax.out.dbml | 4 +- .../snowflake_importer/output/alter.out.dbml | 2 +- .../output/create_table.out.dbml | 26 +-- .../output/column_settings.out.dbml | 16 +- .../output/general_schema.out.dbml | 12 +- .../output/referential_actions.out.dbml | 8 +- .../output/sticky_notes.out.dbml | 6 +- .../output/table_group_settings.out.dbml | 16 +- packages/dbml-core/src/export/DbmlExporter.ts | 4 +- 31 files changed, 273 insertions(+), 273 deletions(-) diff --git a/packages/dbml-core/__tests__/examples/importer/json_importer/output/general_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/json_importer/output/general_schema.out.dbml index 1bbc1b2ce..4ba4e7f91 100644 --- a/packages/dbml-core/__tests__/examples/importer/json_importer/output/general_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/json_importer/output/general_schema.out.dbml @@ -61,14 +61,14 @@ Table "countries" { "continent_name" varchar } -Ref: "orders"."id" < "order_items"."order_id" +Ref:"orders"."id" < "order_items"."order_id" -Ref: "products"."id" < "order_items"."product_id" +Ref:"products"."id" < "order_items"."product_id" -Ref: "countries"."code" < "users"."country_code" +Ref:"countries"."code" < "users"."country_code" -Ref: "countries"."code" < "merchants"."country_code" +Ref:"countries"."code" < "merchants"."country_code" -Ref: "merchants"."id" < "products"."merchant_id" +Ref:"merchants"."id" < "products"."merchant_id" -Ref: "users"."id" < "merchants"."admin_id" +Ref:"users"."id" < "merchants"."admin_id" diff --git a/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/general_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/general_schema.out.dbml index d0e8f22e4..4458f5269 100644 --- a/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/general_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/general_schema.out.dbml @@ -50,14 +50,14 @@ Table "countries" { "continent_name" varchar(255) } -Ref: "orders"."id" < "order_items"."order_id" +Ref:"orders"."id" < "order_items"."order_id" -Ref: "products"."id" < "order_items"."product_id" +Ref:"products"."id" < "order_items"."product_id" -Ref: "countries"."code" < "users"."country_code" +Ref:"countries"."code" < "users"."country_code" -Ref: "countries"."code" < "merchants"."country_code" +Ref:"countries"."code" < "merchants"."country_code" -Ref: "merchants"."id" < "products"."merchant_id" +Ref:"merchants"."id" < "products"."merchant_id" -Ref: "users"."id" < "merchants"."admin_id" +Ref:"users"."id" < "merchants"."admin_id" diff --git a/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/multiple_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/multiple_schema.out.dbml index 5cbc6d1bb..f5531b77e 100644 --- a/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/multiple_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/multiple_schema.out.dbml @@ -12,17 +12,17 @@ Table "products" { "name" nvarchar(255) [note: 'Product name'] } -Ref: "schemaA"."locations"."id" < "schemaA"."products"."lid" +Ref:"schemaA"."locations"."id" < "schemaA"."products"."lid" -Ref "FK_1": "schemaA"."locations"."id" < "schemaA"."products"."lid2" +Ref "FK_1":"schemaA"."locations"."id" < "schemaA"."products"."lid2" -Ref: "users"."id" < "ecommerce"."users"."id" +Ref:"users"."id" < "ecommerce"."users"."id" -Ref "name_optional": "users"."name" < "ecommerce"."users"."id" +Ref "name_optional":"users"."name" < "ecommerce"."users"."id" -Ref: "ecommerce"."users"."id" < "schemaA"."products"."name" +Ref:"ecommerce"."users"."id" < "schemaA"."products"."name" -Ref: "users"."id" < "schemaA"."locations"."name" +Ref:"users"."id" < "schemaA"."locations"."name" Table "ecommerce"."users" { "id" int [pk] diff --git a/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/shorthand_syntax.out.dbml b/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/shorthand_syntax.out.dbml index cc0781dcc..5b5bfff33 100644 --- a/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/shorthand_syntax.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mssql_importer/output/shorthand_syntax.out.dbml @@ -35,6 +35,6 @@ Table "users" { "country_code" int } -Ref "fk_country_code": "countries"."code" < "users"."country_code" +Ref "fk_country_code":"countries"."code" < "users"."country_code" -Ref "fk_composite": "booking_reference".("reference_id", "cust_id") < "br_flight".("reference_id", "cust_id") +Ref "fk_composite":"booking_reference".("reference_id", "cust_id") < "br_flight".("reference_id", "cust_id") diff --git a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/ddl_create.out.dbml b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/ddl_create.out.dbml index 4ceb44970..c001ef8f7 100644 --- a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/ddl_create.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/ddl_create.out.dbml @@ -531,4 +531,4 @@ Table "tab1" { "mi" MIDDLEINT } -Ref: "parent_table"."id" < "child_table"."id_parent" [update: cascade, delete: set null] +Ref:"parent_table"."id" < "child_table"."id_parent" [update: cascade, delete: set null] diff --git a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/general_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/general_schema.out.dbml index 1b1563b0e..32a519a96 100644 --- a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/general_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/general_schema.out.dbml @@ -111,20 +111,20 @@ Table "Cities" { } } -Ref "FK_States_Countries_CountryId": "Countries"."Id" < "States"."CountryId" [delete: cascade] +Ref "FK_States_Countries_CountryId":"Countries"."Id" < "States"."CountryId" [delete: cascade] -Ref "FK_Cities_Countries_CountryId": "Countries"."Id" < "Cities"."CountryId" [delete: cascade] +Ref "FK_Cities_Countries_CountryId":"Countries"."Id" < "Cities"."CountryId" [delete: cascade] -Ref "FK_Cities_States_StateId_CountryId": "States".("Id", "CountryId") < "Cities".("StateId", "CountryId") [delete: cascade] +Ref "FK_Cities_States_StateId_CountryId":"States".("Id", "CountryId") < "Cities".("StateId", "CountryId") [delete: cascade] -Ref: "orders"."id" < "order_items"."order_id" +Ref:"orders"."id" < "order_items"."order_id" -Ref: "products"."id" < "order_items"."product_id" +Ref:"products"."id" < "order_items"."product_id" -Ref: "countries"."code" < "users"."country_code" +Ref:"countries"."code" < "users"."country_code" -Ref: "countries"."code" < "merchants"."country_code" +Ref:"countries"."code" < "merchants"."country_code" -Ref: "merchants"."id" < "products"."merchant_id" +Ref:"merchants"."id" < "products"."merchant_id" -Ref: "users"."id" < "merchants"."admin_id" +Ref:"users"."id" < "merchants"."admin_id" diff --git a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/legacy_issues.out.dbml b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/legacy_issues.out.dbml index 97c69f161..2be98b1b2 100644 --- a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/legacy_issues.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/legacy_issues.out.dbml @@ -61,6 +61,6 @@ Table "example" { "id" varchar(255) } -Ref "user_role_links_fk_1": "users"."id" < "user_role_links"."user_id" +Ref "user_role_links_fk_1":"users"."id" < "user_role_links"."user_id" -Ref "user_role_links_fk_2": "user_roles"."id" < "user_role_links"."role_id" +Ref "user_role_links_fk_2":"user_roles"."id" < "user_role_links"."role_id" diff --git a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/lower_case.out.dbml b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/lower_case.out.dbml index 1b1563b0e..32a519a96 100644 --- a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/lower_case.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/lower_case.out.dbml @@ -111,20 +111,20 @@ Table "Cities" { } } -Ref "FK_States_Countries_CountryId": "Countries"."Id" < "States"."CountryId" [delete: cascade] +Ref "FK_States_Countries_CountryId":"Countries"."Id" < "States"."CountryId" [delete: cascade] -Ref "FK_Cities_Countries_CountryId": "Countries"."Id" < "Cities"."CountryId" [delete: cascade] +Ref "FK_Cities_Countries_CountryId":"Countries"."Id" < "Cities"."CountryId" [delete: cascade] -Ref "FK_Cities_States_StateId_CountryId": "States".("Id", "CountryId") < "Cities".("StateId", "CountryId") [delete: cascade] +Ref "FK_Cities_States_StateId_CountryId":"States".("Id", "CountryId") < "Cities".("StateId", "CountryId") [delete: cascade] -Ref: "orders"."id" < "order_items"."order_id" +Ref:"orders"."id" < "order_items"."order_id" -Ref: "products"."id" < "order_items"."product_id" +Ref:"products"."id" < "order_items"."product_id" -Ref: "countries"."code" < "users"."country_code" +Ref:"countries"."code" < "users"."country_code" -Ref: "countries"."code" < "merchants"."country_code" +Ref:"countries"."code" < "merchants"."country_code" -Ref: "merchants"."id" < "products"."merchant_id" +Ref:"merchants"."id" < "products"."merchant_id" -Ref: "users"."id" < "merchants"."admin_id" +Ref:"users"."id" < "merchants"."admin_id" diff --git a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/multiple_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/multiple_schema.out.dbml index a95181637..530a0f1ce 100644 --- a/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/multiple_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/mysql_importer/output/multiple_schema.out.dbml @@ -70,21 +70,21 @@ Table "orders4" { } } -Ref "FK_1": "schemaA"."locations"."id" < "schemaA"."products"."lid" [delete: cascade] +Ref "FK_1":"schemaA"."locations"."id" < "schemaA"."products"."lid" [delete: cascade] -Ref: "schemaA"."products"."id" < "orders"."pid" +Ref:"schemaA"."products"."id" < "orders"."pid" -Ref: "schemaA"."products"."id" < "orders2"."pid" +Ref:"schemaA"."products"."id" < "orders2"."pid" -Ref "CFK_1": "orders".("id1", "id2") < "orders2".("id1", "id2") [update: set null, delete: cascade] +Ref "CFK_1":"orders".("id1", "id2") < "orders2".("id1", "id2") [update: set null, delete: cascade] -Ref: "users"."id" < "ecommerce"."users"."id" +Ref:"users"."id" < "ecommerce"."users"."id" -Ref "name_optional": "users"."name" < "ecommerce"."users"."id" +Ref "name_optional":"users"."name" < "ecommerce"."users"."id" -Ref: "ecommerce"."users"."id" < "schemaA"."products"."name" +Ref:"ecommerce"."users"."id" < "schemaA"."products"."name" -Ref: "users"."id" < "schemaA"."locations"."name" +Ref:"users"."id" < "schemaA"."locations"."name" Enum "ecommerce"."users_ejs_enum" { "created2" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_fk.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_fk.out.dbml index 9974c65d9..1a17848dd 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_fk.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_fk.out.dbml @@ -58,26 +58,26 @@ Table "task_hours" { "hours_worked" NUMBER(5,2) } -Ref "fk_emp_dept": "departments"."dept_id" < "employees"."department_id" +Ref "fk_emp_dept":"departments"."dept_id" < "employees"."department_id" -Ref "fk_dept_location": "locations"."location_id" < "departments"."location_id" +Ref "fk_dept_location":"locations"."location_id" < "departments"."location_id" -Ref "fk_loc_country": "countries"."country_code" < "locations"."country_code" +Ref "fk_loc_country":"countries"."country_code" < "locations"."country_code" -Ref "fk_emp_manager": "employees"."emp_id" < "employees"."manager_id" +Ref "fk_emp_manager":"employees"."emp_id" < "employees"."manager_id" -Ref "fk_dept_manager": "employees"."emp_id" < "departments"."manager_id" +Ref "fk_dept_manager":"employees"."emp_id" < "departments"."manager_id" -Ref: "departments"."dept_id" < "projects"."dept_id" +Ref:"departments"."dept_id" < "projects"."dept_id" -Ref: "employees"."emp_id" < "assignments"."emp_id" +Ref:"employees"."emp_id" < "assignments"."emp_id" -Ref: "projects"."project_id" < "assignments"."project_id" +Ref:"projects"."project_id" < "assignments"."project_id" -Ref "fk_task_hours_project_task": "project_tasks".("project_id", "task_id") < "task_hours".("project_id", "task_id") +Ref "fk_task_hours_project_task":"project_tasks".("project_id", "task_id") < "task_hours".("project_id", "task_id") -Ref "fk_proj_lead_emp": "employees"."emp_id" < "projects"."lead_emp_id" +Ref "fk_proj_lead_emp":"employees"."emp_id" < "projects"."lead_emp_id" -Ref "fk_proj_backup_emp": "employees"."emp_id" < "projects"."backup_emp_id" +Ref "fk_proj_backup_emp":"employees"."emp_id" < "projects"."backup_emp_id" -Ref "fk_task_assigned_emp": "employees"."emp_id" < "project_tasks"."assigned_emp_id" +Ref "fk_task_assigned_emp":"employees"."emp_id" < "project_tasks"."assigned_emp_id" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_pk.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_pk.out.dbml index 82c66b0da..f228e3d50 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_pk.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_pk.out.dbml @@ -61,4 +61,4 @@ Table "combo_test" { "col2" VARCHAR2(50) } -Ref "fk_complex_parent": "complex_test"."id" < "complex_test"."parent_id" +Ref "fk_complex_parent":"complex_test"."id" < "complex_test"."parent_id" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_unique.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_unique.out.dbml index dda02e64f..ea56339ff 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_unique.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_add_unique.out.dbml @@ -61,4 +61,4 @@ Table "combo_test" { "col2" VARCHAR2(50) } -Ref "fk_complex_parent": "complex_test"."id" < "complex_test"."parent_id" +Ref "fk_complex_parent":"complex_test"."id" < "complex_test"."parent_id" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_check_constraints.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_check_constraints.out.dbml index ee254aa1c..bfe70496c 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_check_constraints.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_check_constraints.out.dbml @@ -67,4 +67,4 @@ Table "combo_test" { "col2" VARCHAR2(50) } -Ref "fk_complex_parent": "complex_test"."id" < "complex_test"."parent_id" +Ref "fk_complex_parent":"complex_test"."id" < "complex_test"."parent_id" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_set_nullable.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_set_nullable.out.dbml index 4478d2698..61b640b1f 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_set_nullable.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/alter_table_set_nullable.out.dbml @@ -63,4 +63,4 @@ Table "combo_test" { "col2" VARCHAR2(50) } -Ref "fk_complex_parent": "complex_test"."id" < "complex_test"."parent_id" +Ref "fk_complex_parent":"complex_test"."id" < "complex_test"."parent_id" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/column_def_fk.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/column_def_fk.out.dbml index 0a3d2fc62..fb5eb5838 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/column_def_fk.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/column_def_fk.out.dbml @@ -188,84 +188,84 @@ Table "test_set_null" { "cust_id" NUMBER(10) } -Ref "fk_emp_dept": "departments"."dept_id" < "employees"."dept_id" +Ref "fk_emp_dept":"departments"."dept_id" < "employees"."dept_id" -Ref "fk_emp_manager": "employees"."emp_id" < "employees"."manager_id" +Ref "fk_emp_manager":"employees"."emp_id" < "employees"."manager_id" -Ref "fk_order_cust": "customers"."cust_id" < "orders"."customer_id" +Ref "fk_order_cust":"customers"."cust_id" < "orders"."customer_id" -Ref "fk_order_track": "shipments"."tracking_number" < "orders"."tracking_number" +Ref "fk_order_track":"shipments"."tracking_number" < "orders"."tracking_number" -Ref "fk_ship_cust": "customers"."cust_id" < "shipments"."customer_id" +Ref "fk_ship_cust":"customers"."cust_id" < "shipments"."customer_id" -Ref "fk_child_complex": "complex_test"."id" < "complex_child"."parent_id" +Ref "fk_child_complex":"complex_test"."id" < "complex_child"."parent_id" -Ref "fk_child_code": "complex_test"."code" < "complex_child"."parent_code" +Ref "fk_child_code":"complex_test"."code" < "complex_child"."parent_code" -Ref "fk_enable_dept": "departments"."dept_id" < "test_enable"."dept_id" +Ref "fk_enable_dept":"departments"."dept_id" < "test_enable"."dept_id" -Ref "fk_enable_supp": "suppliers"."supp_id" < "test_enable"."supp_id" +Ref "fk_enable_supp":"suppliers"."supp_id" < "test_enable"."supp_id" -Ref "fk_disable_dept": "departments"."dept_id" < "test_disable"."dept_id" +Ref "fk_disable_dept":"departments"."dept_id" < "test_disable"."dept_id" -Ref "fk_disable_cust": "customers"."cust_id" < "test_disable"."cust_id" +Ref "fk_disable_cust":"customers"."cust_id" < "test_disable"."cust_id" -Ref "fk_validate_item": "inventory"."item_id" < "test_validate"."item_id" +Ref "fk_validate_item":"inventory"."item_id" < "test_validate"."item_id" -Ref "fk_validate_supp": "suppliers"."supp_id" < "test_validate"."supp_id" +Ref "fk_validate_supp":"suppliers"."supp_id" < "test_validate"."supp_id" -Ref "fk_noval_dept": "departments"."dept_id" < "test_novalidate"."dept_id" +Ref "fk_noval_dept":"departments"."dept_id" < "test_novalidate"."dept_id" -Ref "fk_noval_cust": "customers"."cust_id" < "test_novalidate"."cust_id" +Ref "fk_noval_cust":"customers"."cust_id" < "test_novalidate"."cust_id" -Ref "fk_dis_noval_item": "inventory"."item_id" < "test_disable_novalidate"."item_id" +Ref "fk_dis_noval_item":"inventory"."item_id" < "test_disable_novalidate"."item_id" -Ref "fk_dis_noval_supp": "suppliers"."supp_id" < "test_disable_novalidate"."supp_id" +Ref "fk_dis_noval_supp":"suppliers"."supp_id" < "test_disable_novalidate"."supp_id" -Ref "fk_rely_dept": "departments"."dept_id" < "test_rely"."dept_id" +Ref "fk_rely_dept":"departments"."dept_id" < "test_rely"."dept_id" -Ref "fk_rely_cust": "customers"."cust_id" < "test_rely"."cust_id" +Ref "fk_rely_cust":"customers"."cust_id" < "test_rely"."cust_id" -Ref "fk_norely_item": "inventory"."item_id" < "test_norely"."item_id" +Ref "fk_norely_item":"inventory"."item_id" < "test_norely"."item_id" -Ref "fk_norely_supp": "suppliers"."supp_id" < "test_norely"."supp_id" +Ref "fk_norely_supp":"suppliers"."supp_id" < "test_norely"."supp_id" -Ref "fk_en_rely_dept": "departments"."dept_id" < "test_enable_rely"."dept_id" +Ref "fk_en_rely_dept":"departments"."dept_id" < "test_enable_rely"."dept_id" -Ref "fk_en_rely_cust": "customers"."cust_id" < "test_enable_rely"."cust_id" +Ref "fk_en_rely_cust":"customers"."cust_id" < "test_enable_rely"."cust_id" -Ref "fk_dis_norely_item": "inventory"."item_id" < "test_disable_norely"."item_id" +Ref "fk_dis_norely_item":"inventory"."item_id" < "test_disable_norely"."item_id" -Ref "fk_dis_norely_supp": "suppliers"."supp_id" < "test_disable_norely"."supp_id" +Ref "fk_dis_norely_supp":"suppliers"."supp_id" < "test_disable_norely"."supp_id" -Ref "fk_def_deferred_dept": "departments"."dept_id" < "test_deferrable_deferred"."dept_id" +Ref "fk_def_deferred_dept":"departments"."dept_id" < "test_deferrable_deferred"."dept_id" -Ref "fk_def_deferred_cust": "customers"."cust_id" < "test_deferrable_deferred"."cust_id" +Ref "fk_def_deferred_cust":"customers"."cust_id" < "test_deferrable_deferred"."cust_id" -Ref "fk_def_immed_item": "inventory"."item_id" < "test_deferrable_immediate"."item_id" +Ref "fk_def_immed_item":"inventory"."item_id" < "test_deferrable_immediate"."item_id" -Ref "fk_def_immed_supp": "suppliers"."supp_id" < "test_deferrable_immediate"."supp_id" +Ref "fk_def_immed_supp":"suppliers"."supp_id" < "test_deferrable_immediate"."supp_id" -Ref "fk_not_def_dept": "departments"."dept_id" < "test_not_deferrable"."dept_id" +Ref "fk_not_def_dept":"departments"."dept_id" < "test_not_deferrable"."dept_id" -Ref "fk_not_def_cust": "customers"."cust_id" < "test_not_deferrable"."cust_id" +Ref "fk_not_def_cust":"customers"."cust_id" < "test_not_deferrable"."cust_id" -Ref "fk_dt_number": "test_datatypes"."col_number" < "test_datatypes_fk"."col_number" +Ref "fk_dt_number":"test_datatypes"."col_number" < "test_datatypes_fk"."col_number" -Ref "fk_dt_varchar2": "test_datatypes"."col_varchar2" < "test_datatypes_fk"."col_varchar2" +Ref "fk_dt_varchar2":"test_datatypes"."col_varchar2" < "test_datatypes_fk"."col_varchar2" -Ref "fk_self_emp": "employees_self"."emp_id" < "employees_self"."manager_id" +Ref "fk_self_emp":"employees_self"."emp_id" < "employees_self"."manager_id" -Ref "fk_self_dept": "departments"."dept_id" < "employees_self"."dept_id" +Ref "fk_self_dept":"departments"."dept_id" < "employees_self"."dept_id" -Ref "fk_detail_item": "inventory"."item_id" < "order_details"."item_id" +Ref "fk_detail_item":"inventory"."item_id" < "order_details"."item_id" -Ref "fk_detail_cust": "customers"."cust_id" < "order_details"."cust_id" +Ref "fk_detail_cust":"customers"."cust_id" < "order_details"."cust_id" -Ref "fk_cascade_dept": "departments"."dept_id" < "test_cascade"."dept_id" +Ref "fk_cascade_dept":"departments"."dept_id" < "test_cascade"."dept_id" -Ref "fk_cascade_supp": "suppliers"."supp_id" < "test_cascade"."supp_id" +Ref "fk_cascade_supp":"suppliers"."supp_id" < "test_cascade"."supp_id" -Ref "fk_set_null_dept": "departments"."dept_id" < "test_set_null"."dept_id" +Ref "fk_set_null_dept":"departments"."dept_id" < "test_set_null"."dept_id" -Ref "fk_set_null_cust": "customers"."cust_id" < "test_set_null"."cust_id" +Ref "fk_set_null_cust":"customers"."cust_id" < "test_set_null"."cust_id" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/create_table.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/create_table.out.dbml index 496b9ba5b..4a44240d5 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/create_table.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/create_table.out.dbml @@ -159,7 +159,7 @@ Table "Order Details#2023" { } } -Ref "fk_order_cust": "customers"."cust_id" < "order details"."cust_id" +Ref "fk_order_cust":"customers"."cust_id" < "order details"."cust_id" Table "hr"."employees" { "emp_id" NUMBER(10) diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/in_table_fk.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/in_table_fk.out.dbml index 39d22d8d6..ef5b090fe 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/in_table_fk.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/in_table_fk.out.dbml @@ -98,28 +98,28 @@ Table "project_assignments" { "project_id" NUMBER(10) } -Ref: "departments"."dept_id" < "employees"."dept_id" +Ref:"departments"."dept_id" < "employees"."dept_id" -Ref "fk_order_cust": "customers"."cust_id" < "orders"."customer_id" +Ref "fk_order_cust":"customers"."cust_id" < "orders"."customer_id" -Ref "fk_ship_cust": "customers"."cust_id" < "shipments"."customer_id" +Ref "fk_ship_cust":"customers"."cust_id" < "shipments"."customer_id" -Ref "fk_child_complex": "complex_parent".("id", "code") < "complex_child".("parent_id", "parent_code") +Ref "fk_child_complex":"complex_parent".("id", "code") < "complex_child".("parent_id", "parent_code") -Ref "fk_detail_item": "inventory"."item_id" < "order_details"."item_id" +Ref "fk_detail_item":"inventory"."item_id" < "order_details"."item_id" -Ref "fk_assign_dept": "departments"."dept_id" < "assignments"."dept_id" +Ref "fk_assign_dept":"departments"."dept_id" < "assignments"."dept_id" -Ref "fk_member_dept": "departments"."dept_id" < "project_members"."dept_id" +Ref "fk_member_dept":"departments"."dept_id" < "project_members"."dept_id" -Ref "fk_dt_number": "test_datatypes"."col_number" < "test_datatypes_fk"."col_number" +Ref "fk_dt_number":"test_datatypes"."col_number" < "test_datatypes_fk"."col_number" -Ref "fk_dt_varchar2": "test_datatypes"."col_varchar2" < "test_datatypes_fk"."col_varchar2" +Ref "fk_dt_varchar2":"test_datatypes"."col_varchar2" < "test_datatypes_fk"."col_varchar2" -Ref "fk_self_manager": "employees_self"."emp_id" < "employees_self"."manager_id" +Ref "fk_self_manager":"employees_self"."emp_id" < "employees_self"."manager_id" -Ref "fk_very_long_constraint_name_for_testing_parser_limits": "orders"."order_id" < "order_history"."order_id" +Ref "fk_very_long_constraint_name_for_testing_parser_limits":"orders"."order_id" < "order_history"."order_id" -Ref "fk_ship_detail": "shipments"."ship_id" < "shipment_details"."ship_id" +Ref "fk_ship_detail":"shipments"."ship_id" < "shipment_details"."ship_id" -Ref "fk_assign_emp": "employees"."emp_id" < "project_assignments"."emp_id" +Ref "fk_assign_emp":"employees"."emp_id" < "project_assignments"."emp_id" diff --git a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/quoted_names.out.dbml b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/quoted_names.out.dbml index 0dc90eae3..4cf4f079d 100644 --- a/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/quoted_names.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/oracle_importer/output/quoted_names.out.dbml @@ -20,4 +20,4 @@ Table "silver users" { } } -Ref " foreign key bd ": "silver users"."date of birth" < " aba "."date of birth" +Ref " foreign key bd ":"silver users"."date of birth" < " aba "."date of birth" diff --git a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/cmd_create_table.out.dbml b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/cmd_create_table.out.dbml index f6588d53b..c2bcfbdf9 100644 --- a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/cmd_create_table.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/cmd_create_table.out.dbml @@ -42,6 +42,6 @@ Table "foo" { "bar15" "character varying[76]" } -Ref: "a"."id" < "b"."id" [delete: no action] +Ref:"a"."id" < "b"."id" [delete: no action] -Ref "c_time_constraint": "a"."name" < "c"."time" +Ref "c_time_constraint":"a"."name" < "c"."time" diff --git a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/db_dump.out.dbml b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/db_dump.out.dbml index cd6d62528..62e8e53ec 100644 --- a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/db_dump.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/db_dump.out.dbml @@ -998,182 +998,182 @@ Table "sales"."salestaxrate" { Note: 'Tax rate lookup table.' } -Ref "FK_EmployeeDepartmentHistory_Department_DepartmentID": "humanresources"."department"."departmentid" < "humanresources"."employeedepartmenthistory"."departmentid" +Ref "FK_EmployeeDepartmentHistory_Department_DepartmentID":"humanresources"."department"."departmentid" < "humanresources"."employeedepartmenthistory"."departmentid" -Ref "FK_EmployeeDepartmentHistory_Employee_BusinessEntityID": "humanresources"."employee"."businessentityid" < "humanresources"."employeedepartmenthistory"."businessentityid" +Ref "FK_EmployeeDepartmentHistory_Employee_BusinessEntityID":"humanresources"."employee"."businessentityid" < "humanresources"."employeedepartmenthistory"."businessentityid" -Ref "FK_EmployeeDepartmentHistory_Shift_ShiftID": "humanresources"."shift"."shiftid" < "humanresources"."employeedepartmenthistory"."shiftid" +Ref "FK_EmployeeDepartmentHistory_Shift_ShiftID":"humanresources"."shift"."shiftid" < "humanresources"."employeedepartmenthistory"."shiftid" -Ref "FK_EmployeePayHistory_Employee_BusinessEntityID": "humanresources"."employee"."businessentityid" < "humanresources"."employeepayhistory"."businessentityid" +Ref "FK_EmployeePayHistory_Employee_BusinessEntityID":"humanresources"."employee"."businessentityid" < "humanresources"."employeepayhistory"."businessentityid" -Ref "FK_Employee_Person_BusinessEntityID": "person"."person"."businessentityid" < "humanresources"."employee"."businessentityid" +Ref "FK_Employee_Person_BusinessEntityID":"person"."person"."businessentityid" < "humanresources"."employee"."businessentityid" -Ref "FK_JobCandidate_Employee_BusinessEntityID": "humanresources"."employee"."businessentityid" < "humanresources"."jobcandidate"."businessentityid" +Ref "FK_JobCandidate_Employee_BusinessEntityID":"humanresources"."employee"."businessentityid" < "humanresources"."jobcandidate"."businessentityid" -Ref "FK_Address_StateProvince_StateProvinceID": "person"."stateprovince"."stateprovinceid" < "person"."address"."stateprovinceid" +Ref "FK_Address_StateProvince_StateProvinceID":"person"."stateprovince"."stateprovinceid" < "person"."address"."stateprovinceid" -Ref "FK_BusinessEntityAddress_AddressType_AddressTypeID": "person"."addresstype"."addresstypeid" < "person"."businessentityaddress"."addresstypeid" +Ref "FK_BusinessEntityAddress_AddressType_AddressTypeID":"person"."addresstype"."addresstypeid" < "person"."businessentityaddress"."addresstypeid" -Ref "FK_BusinessEntityAddress_Address_AddressID": "person"."address"."addressid" < "person"."businessentityaddress"."addressid" +Ref "FK_BusinessEntityAddress_Address_AddressID":"person"."address"."addressid" < "person"."businessentityaddress"."addressid" -Ref "FK_BusinessEntityAddress_BusinessEntity_BusinessEntityID": "person"."businessentity"."businessentityid" < "person"."businessentityaddress"."businessentityid" +Ref "FK_BusinessEntityAddress_BusinessEntity_BusinessEntityID":"person"."businessentity"."businessentityid" < "person"."businessentityaddress"."businessentityid" -Ref "FK_BusinessEntityContact_BusinessEntity_BusinessEntityID": "person"."businessentity"."businessentityid" < "person"."businessentitycontact"."businessentityid" +Ref "FK_BusinessEntityContact_BusinessEntity_BusinessEntityID":"person"."businessentity"."businessentityid" < "person"."businessentitycontact"."businessentityid" -Ref "FK_BusinessEntityContact_ContactType_ContactTypeID": "person"."contacttype"."contacttypeid" < "person"."businessentitycontact"."contacttypeid" +Ref "FK_BusinessEntityContact_ContactType_ContactTypeID":"person"."contacttype"."contacttypeid" < "person"."businessentitycontact"."contacttypeid" -Ref "FK_BusinessEntityContact_Person_PersonID": "person"."person"."businessentityid" < "person"."businessentitycontact"."personid" +Ref "FK_BusinessEntityContact_Person_PersonID":"person"."person"."businessentityid" < "person"."businessentitycontact"."personid" -Ref "FK_EmailAddress_Person_BusinessEntityID": "person"."person"."businessentityid" < "person"."emailaddress"."businessentityid" +Ref "FK_EmailAddress_Person_BusinessEntityID":"person"."person"."businessentityid" < "person"."emailaddress"."businessentityid" -Ref "FK_Password_Person_BusinessEntityID": "person"."person"."businessentityid" < "person"."password"."businessentityid" +Ref "FK_Password_Person_BusinessEntityID":"person"."person"."businessentityid" < "person"."password"."businessentityid" -Ref "FK_PersonPhone_Person_BusinessEntityID": "person"."person"."businessentityid" < "person"."personphone"."businessentityid" +Ref "FK_PersonPhone_Person_BusinessEntityID":"person"."person"."businessentityid" < "person"."personphone"."businessentityid" -Ref "FK_PersonPhone_PhoneNumberType_PhoneNumberTypeID": "person"."phonenumbertype"."phonenumbertypeid" < "person"."personphone"."phonenumbertypeid" +Ref "FK_PersonPhone_PhoneNumberType_PhoneNumberTypeID":"person"."phonenumbertype"."phonenumbertypeid" < "person"."personphone"."phonenumbertypeid" -Ref "FK_Person_BusinessEntity_BusinessEntityID": "person"."businessentity"."businessentityid" < "person"."person"."businessentityid" +Ref "FK_Person_BusinessEntity_BusinessEntityID":"person"."businessentity"."businessentityid" < "person"."person"."businessentityid" -Ref "FK_StateProvince_CountryRegion_CountryRegionCode": "person"."countryregion"."countryregioncode" < "person"."stateprovince"."countryregioncode" +Ref "FK_StateProvince_CountryRegion_CountryRegionCode":"person"."countryregion"."countryregioncode" < "person"."stateprovince"."countryregioncode" -Ref "FK_StateProvince_SalesTerritory_TerritoryID": "sales"."salesterritory"."territoryid" < "person"."stateprovince"."territoryid" +Ref "FK_StateProvince_SalesTerritory_TerritoryID":"sales"."salesterritory"."territoryid" < "person"."stateprovince"."territoryid" -Ref "FK_BillOfMaterials_Product_ComponentID": "production"."product"."productid" < "production"."billofmaterials"."componentid" +Ref "FK_BillOfMaterials_Product_ComponentID":"production"."product"."productid" < "production"."billofmaterials"."componentid" -Ref "FK_BillOfMaterials_Product_ProductAssemblyID": "production"."product"."productid" < "production"."billofmaterials"."productassemblyid" +Ref "FK_BillOfMaterials_Product_ProductAssemblyID":"production"."product"."productid" < "production"."billofmaterials"."productassemblyid" -Ref "FK_BillOfMaterials_UnitMeasure_UnitMeasureCode": "production"."unitmeasure"."unitmeasurecode" < "production"."billofmaterials"."unitmeasurecode" +Ref "FK_BillOfMaterials_UnitMeasure_UnitMeasureCode":"production"."unitmeasure"."unitmeasurecode" < "production"."billofmaterials"."unitmeasurecode" -Ref "FK_Document_Employee_Owner": "humanresources"."employee"."businessentityid" < "production"."document"."owner" +Ref "FK_Document_Employee_Owner":"humanresources"."employee"."businessentityid" < "production"."document"."owner" -Ref "FK_ProductCostHistory_Product_ProductID": "production"."product"."productid" < "production"."productcosthistory"."productid" +Ref "FK_ProductCostHistory_Product_ProductID":"production"."product"."productid" < "production"."productcosthistory"."productid" -Ref "FK_ProductDocument_Document_DocumentNode": "production"."document"."documentnode" < "production"."productdocument"."documentnode" +Ref "FK_ProductDocument_Document_DocumentNode":"production"."document"."documentnode" < "production"."productdocument"."documentnode" -Ref "FK_ProductDocument_Product_ProductID": "production"."product"."productid" < "production"."productdocument"."productid" +Ref "FK_ProductDocument_Product_ProductID":"production"."product"."productid" < "production"."productdocument"."productid" -Ref "FK_ProductInventory_Location_LocationID": "production"."location"."locationid" < "production"."productinventory"."locationid" +Ref "FK_ProductInventory_Location_LocationID":"production"."location"."locationid" < "production"."productinventory"."locationid" -Ref "FK_ProductInventory_Product_ProductID": "production"."product"."productid" < "production"."productinventory"."productid" +Ref "FK_ProductInventory_Product_ProductID":"production"."product"."productid" < "production"."productinventory"."productid" -Ref "FK_ProductListPriceHistory_Product_ProductID": "production"."product"."productid" < "production"."productlistpricehistory"."productid" +Ref "FK_ProductListPriceHistory_Product_ProductID":"production"."product"."productid" < "production"."productlistpricehistory"."productid" -Ref "FK_ProductModelIllustration_Illustration_IllustrationID": "production"."illustration"."illustrationid" < "production"."productmodelillustration"."illustrationid" +Ref "FK_ProductModelIllustration_Illustration_IllustrationID":"production"."illustration"."illustrationid" < "production"."productmodelillustration"."illustrationid" -Ref "FK_ProductModelIllustration_ProductModel_ProductModelID": "production"."productmodel"."productmodelid" < "production"."productmodelillustration"."productmodelid" +Ref "FK_ProductModelIllustration_ProductModel_ProductModelID":"production"."productmodel"."productmodelid" < "production"."productmodelillustration"."productmodelid" -Ref "FK_ProductModelProductDescriptionCulture_Culture_CultureID": "production"."culture"."cultureid" < "production"."productmodelproductdescriptionculture"."cultureid" +Ref "FK_ProductModelProductDescriptionCulture_Culture_CultureID":"production"."culture"."cultureid" < "production"."productmodelproductdescriptionculture"."cultureid" -Ref "FK_ProductModelProductDescriptionCulture_ProductDescription_Pro": "production"."productdescription"."productdescriptionid" < "production"."productmodelproductdescriptionculture"."productdescriptionid" +Ref "FK_ProductModelProductDescriptionCulture_ProductDescription_Pro":"production"."productdescription"."productdescriptionid" < "production"."productmodelproductdescriptionculture"."productdescriptionid" -Ref "FK_ProductModelProductDescriptionCulture_ProductModel_ProductMo": "production"."productmodel"."productmodelid" < "production"."productmodelproductdescriptionculture"."productmodelid" +Ref "FK_ProductModelProductDescriptionCulture_ProductModel_ProductMo":"production"."productmodel"."productmodelid" < "production"."productmodelproductdescriptionculture"."productmodelid" -Ref "FK_ProductProductPhoto_ProductPhoto_ProductPhotoID": "production"."productphoto"."productphotoid" < "production"."productproductphoto"."productphotoid" +Ref "FK_ProductProductPhoto_ProductPhoto_ProductPhotoID":"production"."productphoto"."productphotoid" < "production"."productproductphoto"."productphotoid" -Ref "FK_ProductProductPhoto_Product_ProductID": "production"."product"."productid" < "production"."productproductphoto"."productid" +Ref "FK_ProductProductPhoto_Product_ProductID":"production"."product"."productid" < "production"."productproductphoto"."productid" -Ref "FK_ProductReview_Product_ProductID": "production"."product"."productid" < "production"."productreview"."productid" +Ref "FK_ProductReview_Product_ProductID":"production"."product"."productid" < "production"."productreview"."productid" -Ref "FK_ProductSubcategory_ProductCategory_ProductCategoryID": "production"."productcategory"."productcategoryid" < "production"."productsubcategory"."productcategoryid" +Ref "FK_ProductSubcategory_ProductCategory_ProductCategoryID":"production"."productcategory"."productcategoryid" < "production"."productsubcategory"."productcategoryid" -Ref "FK_Product_ProductModel_ProductModelID": "production"."productmodel"."productmodelid" < "production"."product"."productmodelid" +Ref "FK_Product_ProductModel_ProductModelID":"production"."productmodel"."productmodelid" < "production"."product"."productmodelid" -Ref "FK_Product_ProductSubcategory_ProductSubcategoryID": "production"."productsubcategory"."productsubcategoryid" < "production"."product"."productsubcategoryid" +Ref "FK_Product_ProductSubcategory_ProductSubcategoryID":"production"."productsubcategory"."productsubcategoryid" < "production"."product"."productsubcategoryid" -Ref "FK_Product_UnitMeasure_SizeUnitMeasureCode": "production"."unitmeasure"."unitmeasurecode" < "production"."product"."sizeunitmeasurecode" +Ref "FK_Product_UnitMeasure_SizeUnitMeasureCode":"production"."unitmeasure"."unitmeasurecode" < "production"."product"."sizeunitmeasurecode" -Ref "FK_Product_UnitMeasure_WeightUnitMeasureCode": "production"."unitmeasure"."unitmeasurecode" < "production"."product"."weightunitmeasurecode" +Ref "FK_Product_UnitMeasure_WeightUnitMeasureCode":"production"."unitmeasure"."unitmeasurecode" < "production"."product"."weightunitmeasurecode" -Ref "FK_TransactionHistory_Product_ProductID": "production"."product"."productid" < "production"."transactionhistory"."productid" +Ref "FK_TransactionHistory_Product_ProductID":"production"."product"."productid" < "production"."transactionhistory"."productid" -Ref "FK_WorkOrderRouting_Location_LocationID": "production"."location"."locationid" < "production"."workorderrouting"."locationid" +Ref "FK_WorkOrderRouting_Location_LocationID":"production"."location"."locationid" < "production"."workorderrouting"."locationid" -Ref "FK_WorkOrderRouting_WorkOrder_WorkOrderID": "production"."workorder"."workorderid" < "production"."workorderrouting"."workorderid" +Ref "FK_WorkOrderRouting_WorkOrder_WorkOrderID":"production"."workorder"."workorderid" < "production"."workorderrouting"."workorderid" -Ref "FK_WorkOrder_Product_ProductID": "production"."product"."productid" < "production"."workorder"."productid" +Ref "FK_WorkOrder_Product_ProductID":"production"."product"."productid" < "production"."workorder"."productid" -Ref "FK_WorkOrder_ScrapReason_ScrapReasonID": "production"."scrapreason"."scrapreasonid" < "production"."workorder"."scrapreasonid" +Ref "FK_WorkOrder_ScrapReason_ScrapReasonID":"production"."scrapreason"."scrapreasonid" < "production"."workorder"."scrapreasonid" -Ref "FK_ProductVendor_Product_ProductID": "production"."product"."productid" < "purchasing"."productvendor"."productid" +Ref "FK_ProductVendor_Product_ProductID":"production"."product"."productid" < "purchasing"."productvendor"."productid" -Ref "FK_ProductVendor_UnitMeasure_UnitMeasureCode": "production"."unitmeasure"."unitmeasurecode" < "purchasing"."productvendor"."unitmeasurecode" +Ref "FK_ProductVendor_UnitMeasure_UnitMeasureCode":"production"."unitmeasure"."unitmeasurecode" < "purchasing"."productvendor"."unitmeasurecode" -Ref "FK_ProductVendor_Vendor_BusinessEntityID": "purchasing"."vendor"."businessentityid" < "purchasing"."productvendor"."businessentityid" +Ref "FK_ProductVendor_Vendor_BusinessEntityID":"purchasing"."vendor"."businessentityid" < "purchasing"."productvendor"."businessentityid" -Ref "FK_PurchaseOrderDetail_Product_ProductID": "production"."product"."productid" < "purchasing"."purchaseorderdetail"."productid" +Ref "FK_PurchaseOrderDetail_Product_ProductID":"production"."product"."productid" < "purchasing"."purchaseorderdetail"."productid" -Ref "FK_PurchaseOrderDetail_PurchaseOrderHeader_PurchaseOrderID": "purchasing"."purchaseorderheader"."purchaseorderid" < "purchasing"."purchaseorderdetail"."purchaseorderid" +Ref "FK_PurchaseOrderDetail_PurchaseOrderHeader_PurchaseOrderID":"purchasing"."purchaseorderheader"."purchaseorderid" < "purchasing"."purchaseorderdetail"."purchaseorderid" -Ref "FK_PurchaseOrderHeader_Employee_EmployeeID": "humanresources"."employee"."businessentityid" < "purchasing"."purchaseorderheader"."employeeid" +Ref "FK_PurchaseOrderHeader_Employee_EmployeeID":"humanresources"."employee"."businessentityid" < "purchasing"."purchaseorderheader"."employeeid" -Ref "FK_PurchaseOrderHeader_ShipMethod_ShipMethodID": "purchasing"."shipmethod"."shipmethodid" < "purchasing"."purchaseorderheader"."shipmethodid" +Ref "FK_PurchaseOrderHeader_ShipMethod_ShipMethodID":"purchasing"."shipmethod"."shipmethodid" < "purchasing"."purchaseorderheader"."shipmethodid" -Ref "FK_PurchaseOrderHeader_Vendor_VendorID": "purchasing"."vendor"."businessentityid" < "purchasing"."purchaseorderheader"."vendorid" +Ref "FK_PurchaseOrderHeader_Vendor_VendorID":"purchasing"."vendor"."businessentityid" < "purchasing"."purchaseorderheader"."vendorid" -Ref "FK_Vendor_BusinessEntity_BusinessEntityID": "person"."businessentity"."businessentityid" < "purchasing"."vendor"."businessentityid" +Ref "FK_Vendor_BusinessEntity_BusinessEntityID":"person"."businessentity"."businessentityid" < "purchasing"."vendor"."businessentityid" -Ref "FK_CountryRegionCurrency_CountryRegion_CountryRegionCode": "person"."countryregion"."countryregioncode" < "sales"."countryregioncurrency"."countryregioncode" +Ref "FK_CountryRegionCurrency_CountryRegion_CountryRegionCode":"person"."countryregion"."countryregioncode" < "sales"."countryregioncurrency"."countryregioncode" -Ref "FK_CountryRegionCurrency_Currency_CurrencyCode": "sales"."currency"."currencycode" < "sales"."countryregioncurrency"."currencycode" +Ref "FK_CountryRegionCurrency_Currency_CurrencyCode":"sales"."currency"."currencycode" < "sales"."countryregioncurrency"."currencycode" -Ref "FK_CurrencyRate_Currency_FromCurrencyCode": "sales"."currency"."currencycode" < "sales"."currencyrate"."fromcurrencycode" +Ref "FK_CurrencyRate_Currency_FromCurrencyCode":"sales"."currency"."currencycode" < "sales"."currencyrate"."fromcurrencycode" -Ref "FK_CurrencyRate_Currency_ToCurrencyCode": "sales"."currency"."currencycode" < "sales"."currencyrate"."tocurrencycode" +Ref "FK_CurrencyRate_Currency_ToCurrencyCode":"sales"."currency"."currencycode" < "sales"."currencyrate"."tocurrencycode" -Ref "FK_Customer_Person_PersonID": "person"."person"."businessentityid" < "sales"."customer"."personid" +Ref "FK_Customer_Person_PersonID":"person"."person"."businessentityid" < "sales"."customer"."personid" -Ref "FK_Customer_SalesTerritory_TerritoryID": "sales"."salesterritory"."territoryid" < "sales"."customer"."territoryid" +Ref "FK_Customer_SalesTerritory_TerritoryID":"sales"."salesterritory"."territoryid" < "sales"."customer"."territoryid" -Ref "FK_Customer_Store_StoreID": "sales"."store"."businessentityid" < "sales"."customer"."storeid" +Ref "FK_Customer_Store_StoreID":"sales"."store"."businessentityid" < "sales"."customer"."storeid" -Ref "FK_PersonCreditCard_CreditCard_CreditCardID": "sales"."creditcard"."creditcardid" < "sales"."personcreditcard"."creditcardid" +Ref "FK_PersonCreditCard_CreditCard_CreditCardID":"sales"."creditcard"."creditcardid" < "sales"."personcreditcard"."creditcardid" -Ref "FK_PersonCreditCard_Person_BusinessEntityID": "person"."person"."businessentityid" < "sales"."personcreditcard"."businessentityid" +Ref "FK_PersonCreditCard_Person_BusinessEntityID":"person"."person"."businessentityid" < "sales"."personcreditcard"."businessentityid" -Ref "FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID": "sales"."salesorderheader"."salesorderid" < "sales"."salesorderdetail"."salesorderid" [delete: cascade] +Ref "FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID":"sales"."salesorderheader"."salesorderid" < "sales"."salesorderdetail"."salesorderid" [delete: cascade] -Ref "FK_SalesOrderDetail_SpecialOfferProduct_SpecialOfferIDProductID": "sales"."specialofferproduct".("specialofferid", "productid") < "sales"."salesorderdetail".("specialofferid", "productid") +Ref "FK_SalesOrderDetail_SpecialOfferProduct_SpecialOfferIDProductID":"sales"."specialofferproduct".("specialofferid", "productid") < "sales"."salesorderdetail".("specialofferid", "productid") -Ref "FK_SalesOrderHeaderSalesReason_SalesOrderHeader_SalesOrderID": "sales"."salesorderheader"."salesorderid" < "sales"."salesorderheadersalesreason"."salesorderid" [delete: cascade] +Ref "FK_SalesOrderHeaderSalesReason_SalesOrderHeader_SalesOrderID":"sales"."salesorderheader"."salesorderid" < "sales"."salesorderheadersalesreason"."salesorderid" [delete: cascade] -Ref "FK_SalesOrderHeaderSalesReason_SalesReason_SalesReasonID": "sales"."salesreason"."salesreasonid" < "sales"."salesorderheadersalesreason"."salesreasonid" +Ref "FK_SalesOrderHeaderSalesReason_SalesReason_SalesReasonID":"sales"."salesreason"."salesreasonid" < "sales"."salesorderheadersalesreason"."salesreasonid" -Ref "FK_SalesOrderHeader_Address_BillToAddressID": "person"."address"."addressid" < "sales"."salesorderheader"."billtoaddressid" +Ref "FK_SalesOrderHeader_Address_BillToAddressID":"person"."address"."addressid" < "sales"."salesorderheader"."billtoaddressid" -Ref "FK_SalesOrderHeader_Address_ShipToAddressID": "person"."address"."addressid" < "sales"."salesorderheader"."shiptoaddressid" +Ref "FK_SalesOrderHeader_Address_ShipToAddressID":"person"."address"."addressid" < "sales"."salesorderheader"."shiptoaddressid" -Ref "FK_SalesOrderHeader_CreditCard_CreditCardID": "sales"."creditcard"."creditcardid" < "sales"."salesorderheader"."creditcardid" +Ref "FK_SalesOrderHeader_CreditCard_CreditCardID":"sales"."creditcard"."creditcardid" < "sales"."salesorderheader"."creditcardid" -Ref "FK_SalesOrderHeader_CurrencyRate_CurrencyRateID": "sales"."currencyrate"."currencyrateid" < "sales"."salesorderheader"."currencyrateid" +Ref "FK_SalesOrderHeader_CurrencyRate_CurrencyRateID":"sales"."currencyrate"."currencyrateid" < "sales"."salesorderheader"."currencyrateid" -Ref "FK_SalesOrderHeader_Customer_CustomerID": "sales"."customer"."customerid" < "sales"."salesorderheader"."customerid" +Ref "FK_SalesOrderHeader_Customer_CustomerID":"sales"."customer"."customerid" < "sales"."salesorderheader"."customerid" -Ref "FK_SalesOrderHeader_SalesPerson_SalesPersonID": "sales"."salesperson"."businessentityid" < "sales"."salesorderheader"."salespersonid" +Ref "FK_SalesOrderHeader_SalesPerson_SalesPersonID":"sales"."salesperson"."businessentityid" < "sales"."salesorderheader"."salespersonid" -Ref "FK_SalesOrderHeader_SalesTerritory_TerritoryID": "sales"."salesterritory"."territoryid" < "sales"."salesorderheader"."territoryid" +Ref "FK_SalesOrderHeader_SalesTerritory_TerritoryID":"sales"."salesterritory"."territoryid" < "sales"."salesorderheader"."territoryid" -Ref "FK_SalesOrderHeader_ShipMethod_ShipMethodID": "purchasing"."shipmethod"."shipmethodid" < "sales"."salesorderheader"."shipmethodid" +Ref "FK_SalesOrderHeader_ShipMethod_ShipMethodID":"purchasing"."shipmethod"."shipmethodid" < "sales"."salesorderheader"."shipmethodid" -Ref "FK_SalesPersonQuotaHistory_SalesPerson_BusinessEntityID": "sales"."salesperson"."businessentityid" < "sales"."salespersonquotahistory"."businessentityid" +Ref "FK_SalesPersonQuotaHistory_SalesPerson_BusinessEntityID":"sales"."salesperson"."businessentityid" < "sales"."salespersonquotahistory"."businessentityid" -Ref "FK_SalesPerson_Employee_BusinessEntityID": "humanresources"."employee"."businessentityid" < "sales"."salesperson"."businessentityid" +Ref "FK_SalesPerson_Employee_BusinessEntityID":"humanresources"."employee"."businessentityid" < "sales"."salesperson"."businessentityid" -Ref "FK_SalesPerson_SalesTerritory_TerritoryID": "sales"."salesterritory"."territoryid" < "sales"."salesperson"."territoryid" +Ref "FK_SalesPerson_SalesTerritory_TerritoryID":"sales"."salesterritory"."territoryid" < "sales"."salesperson"."territoryid" -Ref "FK_SalesTaxRate_StateProvince_StateProvinceID": "person"."stateprovince"."stateprovinceid" < "sales"."salestaxrate"."stateprovinceid" +Ref "FK_SalesTaxRate_StateProvince_StateProvinceID":"person"."stateprovince"."stateprovinceid" < "sales"."salestaxrate"."stateprovinceid" -Ref "FK_SalesTerritoryHistory_SalesPerson_BusinessEntityID": "sales"."salesperson"."businessentityid" < "sales"."salesterritoryhistory"."businessentityid" +Ref "FK_SalesTerritoryHistory_SalesPerson_BusinessEntityID":"sales"."salesperson"."businessentityid" < "sales"."salesterritoryhistory"."businessentityid" -Ref "FK_SalesTerritoryHistory_SalesTerritory_TerritoryID": "sales"."salesterritory"."territoryid" < "sales"."salesterritoryhistory"."territoryid" +Ref "FK_SalesTerritoryHistory_SalesTerritory_TerritoryID":"sales"."salesterritory"."territoryid" < "sales"."salesterritoryhistory"."territoryid" -Ref "FK_SalesTerritory_CountryRegion_CountryRegionCode": "person"."countryregion"."countryregioncode" < "sales"."salesterritory"."countryregioncode" +Ref "FK_SalesTerritory_CountryRegion_CountryRegionCode":"person"."countryregion"."countryregioncode" < "sales"."salesterritory"."countryregioncode" -Ref "FK_ShoppingCartItem_Product_ProductID": "production"."product"."productid" < "sales"."shoppingcartitem"."productid" +Ref "FK_ShoppingCartItem_Product_ProductID":"production"."product"."productid" < "sales"."shoppingcartitem"."productid" -Ref "FK_SpecialOfferProduct_Product_ProductID": "production"."product"."productid" < "sales"."specialofferproduct"."productid" +Ref "FK_SpecialOfferProduct_Product_ProductID":"production"."product"."productid" < "sales"."specialofferproduct"."productid" -Ref "FK_SpecialOfferProduct_SpecialOffer_SpecialOfferID": "sales"."specialoffer"."specialofferid" < "sales"."specialofferproduct"."specialofferid" +Ref "FK_SpecialOfferProduct_SpecialOffer_SpecialOfferID":"sales"."specialoffer"."specialofferid" < "sales"."specialofferproduct"."specialofferid" -Ref "FK_Store_BusinessEntity_BusinessEntityID": "person"."businessentity"."businessentityid" < "sales"."store"."businessentityid" +Ref "FK_Store_BusinessEntity_BusinessEntityID":"person"."businessentity"."businessentityid" < "sales"."store"."businessentityid" -Ref "FK_Store_SalesPerson_SalesPersonID": "sales"."salesperson"."businessentityid" < "sales"."store"."salespersonid" +Ref "FK_Store_SalesPerson_SalesPersonID":"sales"."salesperson"."businessentityid" < "sales"."store"."salespersonid" diff --git a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/general_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/general_schema.out.dbml index 5adfa4fad..efb3b0813 100644 --- a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/general_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/general_schema.out.dbml @@ -138,18 +138,18 @@ Table "test_table" { } } -Ref: "orders"."id" < "order_items"."order_id" +Ref:"orders"."id" < "order_items"."order_id" -Ref: "products"."id" < "order_items"."product_id" +Ref:"products"."id" < "order_items"."product_id" -Ref: "countries"."code" < "users"."country_code" +Ref:"countries"."code" < "users"."country_code" -Ref: "countries"."code" < "merchants"."country_code" +Ref:"countries"."code" < "merchants"."country_code" -Ref: "merchants"."id" < "products"."merchant_id" +Ref:"merchants"."id" < "products"."merchant_id" -Ref: "users"."id" < "merchants"."admin_id" +Ref:"users"."id" < "merchants"."admin_id" -Ref: "table1".("field", "field2") < "table2".("field", "field2") +Ref:"table1".("field", "field2") < "table2".("field", "field2") -Ref "test_table_fk": "test_table".("name", "id") < "test_table".("linked_name", "id") +Ref "test_table_fk":"test_table".("name", "id") < "test_table".("linked_name", "id") diff --git a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/multiple_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/multiple_schema.out.dbml index dc97f7757..d76041a88 100644 --- a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/multiple_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/multiple_schema.out.dbml @@ -28,15 +28,15 @@ Table "products" { } } -Ref "fk_1": "schemaA"."locations"."id" < "schemaA"."products"."lid" +Ref "fk_1":"schemaA"."locations"."id" < "schemaA"."products"."lid" -Ref: "users"."id" < "ecommerce"."users"."id" +Ref:"users"."id" < "ecommerce"."users"."id" -Ref "name_optional": "users"."name" < "ecommerce"."users"."id" +Ref "name_optional":"users"."name" < "ecommerce"."users"."id" -Ref: "ecommerce"."users"."id" < "schemaA"."products"."name" +Ref:"ecommerce"."users"."id" < "schemaA"."products"."name" -Ref: "users"."id" < "schemaA"."locations"."name" +Ref:"users"."id" < "schemaA"."locations"."name" Enum "schemaB"."gender" { "male" diff --git a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/shorthand_syntax.out.dbml b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/shorthand_syntax.out.dbml index fdf16c4e7..8bc5dc5c6 100644 --- a/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/shorthand_syntax.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/postgres_importer/output/shorthand_syntax.out.dbml @@ -35,6 +35,6 @@ Table "countries" { "continent_name" varchar } -Ref "fk_country_code": "countries"."code" < "users"."country_code" +Ref "fk_country_code":"countries"."code" < "users"."country_code" -Ref "fk_composite": "booking_reference".("reference_id", "cust_id") < "br_flight".("reference_id", "cust_id") +Ref "fk_composite":"booking_reference".("reference_id", "cust_id") < "br_flight".("reference_id", "cust_id") diff --git a/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/alter.out.dbml b/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/alter.out.dbml index 907ed8700..c31496b19 100644 --- a/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/alter.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/alter.out.dbml @@ -25,4 +25,4 @@ Table "employees" { "department" VARCHAR(50) } -Ref: "customers"."customer_id" < "orders"."customer_id" +Ref:"customers"."customer_id" < "orders"."customer_id" diff --git a/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/create_table.out.dbml b/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/create_table.out.dbml index dabb8fae5..054442bca 100644 --- a/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/create_table.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/snowflake_importer/output/create_table.out.dbml @@ -446,28 +446,28 @@ Table "floor" { "getdate" int } -Ref: "HP_TEST"."CUSTOMERS"."CUSTOMER_ID" < "HP_TEST"."ORDERS"."CUSTOMER_ID" +Ref:"HP_TEST"."CUSTOMERS"."CUSTOMER_ID" < "HP_TEST"."ORDERS"."CUSTOMER_ID" -Ref: "HP_TEST"."ORDERS"."ORDER_ID" < "HP_TEST"."ORDER_ITEMS"."ORDER_ID" +Ref:"HP_TEST"."ORDERS"."ORDER_ID" < "HP_TEST"."ORDER_ITEMS"."ORDER_ID" -Ref: "HP_TEST"."PRODUCTS"."PRODUCT_ID" < "HP_TEST"."ORDER_ITEMS"."PRODUCT_ID" +Ref:"HP_TEST"."PRODUCTS"."PRODUCT_ID" < "HP_TEST"."ORDER_ITEMS"."PRODUCT_ID" -Ref: "TEST_2024_06_28_10_01_ZQMIMY"."ONE".("BAR", "FOO") < "TEST_2024_06_28_10_01_ZQMIMY"."TWO".("XYZ", "ABC") +Ref:"TEST_2024_06_28_10_01_ZQMIMY"."ONE".("BAR", "FOO") < "TEST_2024_06_28_10_01_ZQMIMY"."TWO".("XYZ", "ABC") -Ref: "TEST_2024_06_28_10_01_ZQMIMY"."ONE"."FOO" < "TEST_2024_06_28_10_01_ZQMIMY"."TWO"."QWE" +Ref:"TEST_2024_06_28_10_01_ZQMIMY"."ONE"."FOO" < "TEST_2024_06_28_10_01_ZQMIMY"."TWO"."QWE" -Ref: "T_OUT_2"."C2" < "T_OUT_3"."C3" +Ref:"T_OUT_2"."C2" < "T_OUT_3"."C3" -Ref: "T_OUT_5"."C2" < "T_OUT_6"."C3" +Ref:"T_OUT_5"."C2" < "T_OUT_6"."C3" -Ref: "T_OUT_5"."C2" < "T_CONSTRAINT_3"."C3" +Ref:"T_OUT_5"."C2" < "T_CONSTRAINT_3"."C3" -Ref: "T_OUT_5"."C2" < "T_CONSTRAINT_4"."C3" +Ref:"T_OUT_5"."C2" < "T_CONSTRAINT_4"."C3" -Ref: "T_OUT_5"."C2" < "T_CONSTRAINT_5"."C3" +Ref:"T_OUT_5"."C2" < "T_CONSTRAINT_5"."C3" -Ref: "T_OUT_5"."C2" < "T_CONSTRAINT_6"."C3" +Ref:"T_OUT_5"."C2" < "T_CONSTRAINT_6"."C3" -Ref: "T_OUT_5"."C2" < "T_CONSTRAINT_8"."C3" +Ref:"T_OUT_5"."C2" < "T_CONSTRAINT_8"."C3" -Ref: "T_OUT_5"."C2" < "T_CONSTRAINT_9"."C3" +Ref:"T_OUT_5"."C2" < "T_CONSTRAINT_9"."C3" diff --git a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/column_settings.out.dbml b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/column_settings.out.dbml index 934f53849..98ce82870 100644 --- a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/column_settings.out.dbml +++ b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/column_settings.out.dbml @@ -84,18 +84,18 @@ TableGroup "e_commerce_orders" [color: #24BAB1] { "ecommerce"."order_items" } -Ref: "users"."id" < "ecommerce"."merchants"."admin_id" +Ref:"users"."id" < "ecommerce"."merchants"."admin_id" -Ref: "countries"."code" < "users"."country_code" +Ref:"countries"."code" < "users"."country_code" -Ref: "countries"."code" < "ecommerce"."merchants"."country_code" +Ref:"countries"."code" < "ecommerce"."merchants"."country_code" -Ref: "ecommerce"."orders"."id" < "ecommerce"."order_items"."order_id" +Ref:"ecommerce"."orders"."id" < "ecommerce"."order_items"."order_id" -Ref: "ecommerce"."products"."id" < "ecommerce"."order_items"."product_id" +Ref:"ecommerce"."products"."id" < "ecommerce"."order_items"."product_id" -Ref: "ecommerce"."merchants"."id" < "ecommerce"."products"."merchant_id" +Ref:"ecommerce"."merchants"."id" < "ecommerce"."products"."merchant_id" -Ref: "ecommerce"."product_tags"."id" <> "ecommerce"."products"."id" +Ref:"ecommerce"."product_tags"."id" <> "ecommerce"."products"."id" -Ref: "ecommerce"."merchants".("id", "country_code") < "ecommerce"."merchant_periods".("merchant_id", "country_code") +Ref:"ecommerce"."merchants".("id", "country_code") < "ecommerce"."merchant_periods".("merchant_id", "country_code") diff --git a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/general_schema.out.dbml b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/general_schema.out.dbml index 1bbc1b2ce..4ba4e7f91 100644 --- a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/general_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/general_schema.out.dbml @@ -61,14 +61,14 @@ Table "countries" { "continent_name" varchar } -Ref: "orders"."id" < "order_items"."order_id" +Ref:"orders"."id" < "order_items"."order_id" -Ref: "products"."id" < "order_items"."product_id" +Ref:"products"."id" < "order_items"."product_id" -Ref: "countries"."code" < "users"."country_code" +Ref:"countries"."code" < "users"."country_code" -Ref: "countries"."code" < "merchants"."country_code" +Ref:"countries"."code" < "merchants"."country_code" -Ref: "merchants"."id" < "products"."merchant_id" +Ref:"merchants"."id" < "products"."merchant_id" -Ref: "users"."id" < "merchants"."admin_id" +Ref:"users"."id" < "merchants"."admin_id" diff --git a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/referential_actions.out.dbml b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/referential_actions.out.dbml index 56bcf9366..cc4900773 100644 --- a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/referential_actions.out.dbml +++ b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/referential_actions.out.dbml @@ -38,10 +38,10 @@ Table "countries" { "continent_name" varchar(255) } -Ref: "users"."id" < "orders"."user_id" [delete: restrict] +Ref:"users"."id" < "orders"."user_id" [delete: restrict] -Ref: "orders"."id" < "order_items"."order_id" [delete: cascade] +Ref:"orders"."id" < "order_items"."order_id" [delete: cascade] -Ref: "products".("id", "name") < "order_items".("product_id", "product_name") [delete: set null] +Ref:"products".("id", "name") < "order_items".("product_id", "product_name") [delete: set null] -Ref: "countries"."code" < "users"."country_code" [delete: no action] +Ref:"countries"."code" < "users"."country_code" [delete: no action] diff --git a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/sticky_notes.out.dbml b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/sticky_notes.out.dbml index 03705e5ab..3bcd1ae5e 100644 --- a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/sticky_notes.out.dbml +++ b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/sticky_notes.out.dbml @@ -20,11 +20,11 @@ Table "posts" { "created_at" timestamp } -Ref: "users"."id" < "posts"."user_id" +Ref:"users"."id" < "posts"."user_id" -Ref: "users"."id" < "follows"."following_user_id" +Ref:"users"."id" < "follows"."following_user_id" -Ref: "users"."id" < "follows"."followed_user_id" +Ref:"users"."id" < "follows"."followed_user_id" Note note1 { 'sticky note with one line content' diff --git a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/table_group_settings.out.dbml b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/table_group_settings.out.dbml index 319beea71..e62c1feaf 100644 --- a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/table_group_settings.out.dbml +++ b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/table_group_settings.out.dbml @@ -90,18 +90,18 @@ TableGroup "e_commerce_orders" [color: #24BAB1] { "ecommerce"."order_items" } -Ref: "users"."id" < "ecommerce"."merchants"."admin_id" +Ref:"users"."id" < "ecommerce"."merchants"."admin_id" -Ref: "countries"."code" < "users"."country_code" +Ref:"countries"."code" < "users"."country_code" -Ref: "countries"."code" < "ecommerce"."merchants"."country_code" +Ref:"countries"."code" < "ecommerce"."merchants"."country_code" -Ref: "ecommerce"."orders"."id" < "ecommerce"."order_items"."order_id" +Ref:"ecommerce"."orders"."id" < "ecommerce"."order_items"."order_id" -Ref: "ecommerce"."products"."id" < "ecommerce"."order_items"."product_id" +Ref:"ecommerce"."products"."id" < "ecommerce"."order_items"."product_id" -Ref: "ecommerce"."merchants"."id" < "ecommerce"."products"."merchant_id" +Ref:"ecommerce"."merchants"."id" < "ecommerce"."products"."merchant_id" -Ref: "ecommerce"."product_tags"."id" <> "ecommerce"."products"."id" +Ref:"ecommerce"."product_tags"."id" <> "ecommerce"."products"."id" -Ref: "ecommerce"."merchants".("id", "country_code") < "ecommerce"."merchant_periods".("merchant_id", "country_code") +Ref:"ecommerce"."merchants".("id", "country_code") < "ecommerce"."merchant_periods".("merchant_id", "country_code") diff --git a/packages/dbml-core/src/export/DbmlExporter.ts b/packages/dbml-core/src/export/DbmlExporter.ts index 8631c67f0..aa57524f1 100644 --- a/packages/dbml-core/src/export/DbmlExporter.ts +++ b/packages/dbml-core/src/export/DbmlExporter.ts @@ -1,5 +1,5 @@ import { groupBy, isEmpty, reduce } from 'lodash-es'; -import { formatRecordValue, getRelationshipOp, parseCardinality } from '@dbml/parse'; +import { addDoubleQuoteIfNeeded, formatRecordValue, getRelationshipOp, parseCardinality } from '@dbml/parse'; import { shouldPrintSchema } from './utils'; import { DEFAULT_SCHEMA_NAME } from '../model_structure/config'; import type { NormalizedModel, RecordValue } from '../../types/model_structure/database'; @@ -292,7 +292,7 @@ class DbmlExporter { ? `"${model.schemas[ref.schemaId].name}".` : ''}"${ref.name}"`; } - line += ': '; + line += ':'; line += `${shouldPrintSchema(leftSchema, model) ? `"${leftSchema.name}".` : ''}"${leftTable.name}".${leftFieldName} `; From dc822c654875cabd8aeb587abe697c83479f49be Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 11:59:57 +0700 Subject: [PATCH 17/35] refactor: more concise optional ref test --- .../examples/lexer/optional_ref.test.ts | 48 + .../examples/parser/optional_ref.test.ts | 39 + .../examples/validator/optional_ref.test.ts | 39 + .../lexer/input/optional_ref.in.dbml | 4 - .../lexer/output/optional_ref.out.json | 157 - .../parser/input/optional_ref.in.dbml | 25 - .../parser/output/optional_ref.out.json | 2997 -------------- .../validator/input/optional_ref.in.dbml | 26 - .../validator/output/optional_ref.out.json | 3479 ----------------- 9 files changed, 126 insertions(+), 6688 deletions(-) create mode 100644 packages/dbml-parse/__tests__/examples/lexer/optional_ref.test.ts create mode 100644 packages/dbml-parse/__tests__/examples/parser/optional_ref.test.ts create mode 100644 packages/dbml-parse/__tests__/examples/validator/optional_ref.test.ts delete mode 100644 packages/dbml-parse/__tests__/snapshots/lexer/input/optional_ref.in.dbml delete mode 100644 packages/dbml-parse/__tests__/snapshots/lexer/output/optional_ref.out.json delete mode 100644 packages/dbml-parse/__tests__/snapshots/parser/input/optional_ref.in.dbml delete mode 100644 packages/dbml-parse/__tests__/snapshots/parser/output/optional_ref.out.json delete mode 100644 packages/dbml-parse/__tests__/snapshots/validator/input/optional_ref.in.dbml delete mode 100644 packages/dbml-parse/__tests__/snapshots/validator/output/optional_ref.out.json diff --git a/packages/dbml-parse/__tests__/examples/lexer/optional_ref.test.ts b/packages/dbml-parse/__tests__/examples/lexer/optional_ref.test.ts new file mode 100644 index 000000000..4b966dd9c --- /dev/null +++ b/packages/dbml-parse/__tests__/examples/lexer/optional_ref.test.ts @@ -0,0 +1,48 @@ +import { + describe, expect, test, +} from 'vitest'; +import { + SyntaxTokenKind, isTriviaToken, +} from '@/core/types/tokens'; +import { + lex, +} from '@tests/utils'; + +function getTokens (source: string) { + return lex(source).getValue().filter((t) => !isTriviaToken(t) && t.kind !== SyntaxTokenKind.EOF); +} + +describe('[example] optional ref operators', () => { + test.each([ + '-', '-?', '?-', '?-?', + '>', '>?', '?>', '?>?', + '<', '', '<>?', '?<>', '?<>?', + ])('should tokenize %s as a single operator', (op) => { + const tokens = getTokens(op); + + expect(tokens).toHaveLength(1); + expect(tokens[0].kind).toBe(SyntaxTokenKind.OP); + expect(tokens[0].value).toBe(op); + }); + + test('should tokenize all optional ref operators in sequence', () => { + const source = '- -? ?- ?-? > >? ?> ?>? < <>? ?<> ?<>?'; + const tokens = getTokens(source); + + expect(tokens).toHaveLength(16); + expect(tokens.map((t) => t.value)).toEqual([ + '-', '-?', '?-', '?-?', + '>', '>?', '?>', '?>?', + '<', '', '<>?', '?<>', '?<>?', + ]); + }); + + test('should not produce errors for optional ref operators', () => { + const source = '-? ?- ?-? >? ?> ?>? ? ?<> ?<>?'; + const result = lex(source); + + expect(result.getErrors()).toHaveLength(0); + }); +}); diff --git a/packages/dbml-parse/__tests__/examples/parser/optional_ref.test.ts b/packages/dbml-parse/__tests__/examples/parser/optional_ref.test.ts new file mode 100644 index 000000000..662718ab1 --- /dev/null +++ b/packages/dbml-parse/__tests__/examples/parser/optional_ref.test.ts @@ -0,0 +1,39 @@ +import { + describe, expect, test, +} from 'vitest'; +import { + parse, +} from '@tests/utils'; + +describe('[example] optional ref parsing', () => { + test.each([ + '-', '-?', '?-', '?-?', + '>', '>?', '?>', '?>?', + '<', '', '<>?', '?<>', '?<>?', + ])('should parse standalone ref with operator %s without errors', (op) => { + const source = ` + Table users { id int } + Table posts { user_id int } + Ref: posts.user_id ${op} users.id + `; + const result = parse(source); + expect(result.getErrors()).toHaveLength(0); + }); + + test.each([ + '>', '>?', '?>', '?>?', + '<', '', '<>?', '?<>', '?<>?', + ])('should parse inline ref with operator %s without errors', (op) => { + const source = ` + Table users { id int } + Table posts { + user_id int [ref: ${op} users.id] + } + `; + const result = parse(source); + expect(result.getErrors()).toHaveLength(0); + }); +}); diff --git a/packages/dbml-parse/__tests__/examples/validator/optional_ref.test.ts b/packages/dbml-parse/__tests__/examples/validator/optional_ref.test.ts new file mode 100644 index 000000000..278bc4d14 --- /dev/null +++ b/packages/dbml-parse/__tests__/examples/validator/optional_ref.test.ts @@ -0,0 +1,39 @@ +import { + describe, expect, test, +} from 'vitest'; +import { + analyze, +} from '@tests/utils'; + +describe('[example] optional ref validation', () => { + test.each([ + '-', '-?', '?-', '?-?', + '>', '>?', '?>', '?>?', + '<', '', '<>?', '?<>', '?<>?', + ])('should accept standalone ref with operator %s', (op) => { + const source = ` + Table users { id int } + Table posts { user_id int } + Ref: posts.user_id ${op} users.id + `; + const errors = analyze(source).getErrors(); + expect(errors).toHaveLength(0); + }); + + test.each([ + '>', '>?', '?>', '?>?', + '<', '', '<>?', '?<>', '?<>?', + ])('should accept inline ref with operator %s', (op) => { + const source = ` + Table users { id int } + Table posts { + user_id int [ref: ${op} users.id] + } + `; + const errors = analyze(source).getErrors(); + expect(errors).toHaveLength(0); + }); +}); diff --git a/packages/dbml-parse/__tests__/snapshots/lexer/input/optional_ref.in.dbml b/packages/dbml-parse/__tests__/snapshots/lexer/input/optional_ref.in.dbml deleted file mode 100644 index 8be4f7d2d..000000000 --- a/packages/dbml-parse/__tests__/snapshots/lexer/input/optional_ref.in.dbml +++ /dev/null @@ -1,4 +0,0 @@ -- -? ?- ?-? -> >? ?> ?>? -< <>? ?<> ?<>? diff --git a/packages/dbml-parse/__tests__/snapshots/lexer/output/optional_ref.out.json b/packages/dbml-parse/__tests__/snapshots/lexer/output/optional_ref.out.json deleted file mode 100644 index 937ef66bd..000000000 --- a/packages/dbml-parse/__tests__/snapshots/lexer/output/optional_ref.out.json +++ /dev/null @@ -1,157 +0,0 @@ -{ - "tokens": [ - { - "context": { - "id": "token@@-@[L0:C0, L0:C1]", - "snippet": "-" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "-" - }, - { - "context": { - "id": "token@@-?@[L0:C2, L0:C4]", - "snippet": "-?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "-?" - }, - { - "context": { - "id": "token@@?-@[L0:C5, L0:C7]", - "snippet": "?-" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?-" - }, - { - "context": { - "id": "token@@?-?@[L0:C8, L0:C11]", - "snippet": "?-?" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "?-?" - }, - { - "context": { - "id": "token@@>@[L1:C0, L1:C1]", - "snippet": ">" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ">" - }, - { - "context": { - "id": "token@@>?@[L1:C2, L1:C4]", - "snippet": ">?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ">?" - }, - { - "context": { - "id": "token@@?>@[L1:C5, L1:C7]", - "snippet": "?>" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?>" - }, - { - "context": { - "id": "token@@?>?@[L1:C8, L1:C11]", - "snippet": "?>?" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "?>?" - }, - { - "context": { - "id": "token@@<@[L2:C0, L2:C1]", - "snippet": "<" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "<" - }, - { - "context": { - "id": "token@@@?<@[L2:C5, L2:C7]", - "snippet": "?<" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?<" - }, - { - "context": { - "id": "token@@?@<>@[L3:C0, L3:C2]", - "snippet": "<>" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "<>" - }, - { - "context": { - "id": "token@@<>?@[L3:C3, L3:C6]", - "snippet": "<>?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "<>?" - }, - { - "context": { - "id": "token@@?<>@[L3:C7, L3:C10]", - "snippet": "?<>" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?<>" - }, - { - "context": { - "id": "token@@?<>?@[L3:C11, L3:C15]", - "snippet": "?<>?" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "?<>?" - }, - { - "context": { - "id": "token@@@[L4:C0, L4:C0]", - "snippet": "" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "" - } - ] -} \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/parser/input/optional_ref.in.dbml b/packages/dbml-parse/__tests__/snapshots/parser/input/optional_ref.in.dbml deleted file mode 100644 index 0e86213da..000000000 --- a/packages/dbml-parse/__tests__/snapshots/parser/input/optional_ref.in.dbml +++ /dev/null @@ -1,25 +0,0 @@ -Table users { - id integer -} - -Table posts { - id integer - user_id integer - - Ref: user_id > users.id - Ref: user_id >? users.id - Ref: user_id ?> users.id - Ref: user_id ?>? users.id - Ref: user_id < users.id - Ref: user_id users.id - Ref: user_id <>? users.id - Ref: user_id ?<> users.id - Ref: user_id ?<>? users.id -} diff --git a/packages/dbml-parse/__tests__/snapshots/parser/output/optional_ref.out.json b/packages/dbml-parse/__tests__/snapshots/parser/output/optional_ref.out.json deleted file mode 100644 index 91a416f17..000000000 --- a/packages/dbml-parse/__tests__/snapshots/parser/output/optional_ref.out.json +++ /dev/null @@ -1,2997 +0,0 @@ -{ - "program": { - "context": { - "id": "node@@@[L0:C0, L25:C0]", - "snippet": "Table user...sers.id\n}\n" - }, - "children": { - "body": [ - { - "context": { - "id": "node@@users@[L0:C0, L2:C1]", - "snippet": "Table user... integer\n}" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L0:C12, L2:C1]", - "snippet": "{\n id integer\n}" - }, - "children": { - "blockCloseBrace": { - "context": { - "id": "token@@}@[L2:C0, L2:C1]", - "snippet": "}" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "}" - }, - "blockOpenBrace": { - "context": { - "id": "token@@{@[L0:C12, L0:C13]", - "snippet": "{" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "{" - }, - "body": [ - { - "context": { - "id": "node@@id@[L1:C4, L1:C14]", - "snippet": "id integer" - }, - "children": { - "args": [ - { - "context": { - "id": "node@@integer@[L1:C7, L1:C14]", - "snippet": "integer" - }, - "children": { - "expression": { - "context": { - "id": "node@@integer@[L1:C7, L1:C14]", - "snippet": "integer" - }, - "children": { - "variable": { - "context": { - "id": "token@@integer@[L1:C7, L1:C14]", - "snippet": "integer" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "integer" - } - }, - "fullEnd": 29, - "fullStart": 21 - } - }, - "fullEnd": 29, - "fullStart": 21 - } - ], - "callee": { - "context": { - "id": "node@@id@[L1:C4, L1:C6]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L1:C4, L1:C6]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L1:C4, L1:C6]", - "snippet": "id" - }, - "leadingTrivia": " ", - "trailingTrivia": " ", - "value": "id" - } - }, - "fullEnd": 21, - "fullStart": 14 - } - }, - "fullEnd": 21, - "fullStart": 14 - } - }, - "fullEnd": 29, - "fullStart": 14 - } - ] - }, - "fullEnd": 31, - "fullStart": 12 - }, - "name": { - "context": { - "id": "node@@users@[L0:C6, L0:C11]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L0:C6, L0:C11]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L0:C6, L0:C11]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "users" - } - }, - "fullEnd": 12, - "fullStart": 6 - } - }, - "fullEnd": 12, - "fullStart": 6 - }, - "type": { - "context": { - "id": "token@@Table@[L0:C0, L0:C5]", - "snippet": "Table" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "Table" - } - }, - "fullEnd": 31, - "fullStart": 0 - }, - { - "context": { - "id": "node@@posts@[L4:C0, L24:C1]", - "snippet": "Table post...users.id\n}" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L4:C12, L24:C1]", - "snippet": "{\n id i...users.id\n}" - }, - "children": { - "blockCloseBrace": { - "context": { - "id": "token@@}@[L24:C0, L24:C1]", - "snippet": "}" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "}" - }, - "blockOpenBrace": { - "context": { - "id": "token@@{@[L4:C12, L4:C13]", - "snippet": "{" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "{" - }, - "body": [ - { - "context": { - "id": "node@@id@[L5:C4, L5:C14]", - "snippet": "id integer" - }, - "children": { - "args": [ - { - "context": { - "id": "node@@integer@[L5:C7, L5:C14]", - "snippet": "integer" - }, - "children": { - "expression": { - "context": { - "id": "node@@integer@[L5:C7, L5:C14]", - "snippet": "integer" - }, - "children": { - "variable": { - "context": { - "id": "token@@integer@[L5:C7, L5:C14]", - "snippet": "integer" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "integer" - } - }, - "fullEnd": 61, - "fullStart": 53 - } - }, - "fullEnd": 61, - "fullStart": 53 - } - ], - "callee": { - "context": { - "id": "node@@id@[L5:C4, L5:C6]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L5:C4, L5:C6]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L5:C4, L5:C6]", - "snippet": "id" - }, - "leadingTrivia": " ", - "trailingTrivia": " ", - "value": "id" - } - }, - "fullEnd": 53, - "fullStart": 46 - } - }, - "fullEnd": 53, - "fullStart": 46 - } - }, - "fullEnd": 61, - "fullStart": 46 - }, - { - "context": { - "id": "node@@user_id@[L6:C4, L6:C19]", - "snippet": "user_id integer" - }, - "children": { - "args": [ - { - "context": { - "id": "node@@integer@[L6:C12, L6:C19]", - "snippet": "integer" - }, - "children": { - "expression": { - "context": { - "id": "node@@integer@[L6:C12, L6:C19]", - "snippet": "integer" - }, - "children": { - "variable": { - "context": { - "id": "token@@integer@[L6:C12, L6:C19]", - "snippet": "integer" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "integer" - } - }, - "fullEnd": 81, - "fullStart": 73 - } - }, - "fullEnd": 81, - "fullStart": 73 - } - ], - "callee": { - "context": { - "id": "node@@user_id@[L6:C4, L6:C11]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L6:C4, L6:C11]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L6:C4, L6:C11]", - "snippet": "user_id" - }, - "leadingTrivia": " ", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 73, - "fullStart": 61 - } - }, - "fullEnd": 73, - "fullStart": 61 - } - }, - "fullEnd": 81, - "fullStart": 61 - }, - { - "context": { - "id": "node@@@[L8:C4, L8:C27]", - "snippet": "Ref: user_...> users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L8:C9, L8:C27]", - "snippet": "user_id > users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L8:C9, L8:C27]", - "snippet": "user_id > users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L8:C9, L8:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L8:C9, L8:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L8:C9, L8:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 99, - "fullStart": 91 - } - }, - "fullEnd": 99, - "fullStart": 91 - }, - "op": { - "context": { - "id": "token@@>@[L8:C17, L8:C18]", - "snippet": ">" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ">" - }, - "rightExpression": { - "context": { - "id": "node@@@[L8:C19, L8:C27]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L8:C19, L8:C24]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L8:C19, L8:C24]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L8:C19, L8:C24]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 106, - "fullStart": 101 - } - }, - "fullEnd": 106, - "fullStart": 101 - }, - "op": { - "context": { - "id": "token@@.@[L8:C24, L8:C25]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L8:C25, L8:C27]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L8:C25, L8:C27]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L8:C25, L8:C27]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 110, - "fullStart": 107 - } - }, - "fullEnd": 110, - "fullStart": 107 - } - }, - "fullEnd": 110, - "fullStart": 101 - } - }, - "fullEnd": 110, - "fullStart": 91 - } - }, - "fullEnd": 110, - "fullStart": 91 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L8:C7, L8:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L8:C4, L8:C7]", - "snippet": "Ref" - }, - "leadingTrivia": "\n ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 110, - "fullStart": 81 - }, - { - "context": { - "id": "node@@@[L9:C4, L9:C28]", - "snippet": "Ref: user_...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L9:C9, L9:C28]", - "snippet": "user_id >? users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L9:C9, L9:C28]", - "snippet": "user_id >? users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L9:C9, L9:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L9:C9, L9:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L9:C9, L9:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 127, - "fullStart": 119 - } - }, - "fullEnd": 127, - "fullStart": 119 - }, - "op": { - "context": { - "id": "token@@>?@[L9:C17, L9:C19]", - "snippet": ">?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ">?" - }, - "rightExpression": { - "context": { - "id": "node@@@[L9:C20, L9:C28]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L9:C20, L9:C25]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L9:C20, L9:C25]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L9:C20, L9:C25]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 135, - "fullStart": 130 - } - }, - "fullEnd": 135, - "fullStart": 130 - }, - "op": { - "context": { - "id": "token@@.@[L9:C25, L9:C26]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L9:C26, L9:C28]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L9:C26, L9:C28]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L9:C26, L9:C28]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 139, - "fullStart": 136 - } - }, - "fullEnd": 139, - "fullStart": 136 - } - }, - "fullEnd": 139, - "fullStart": 130 - } - }, - "fullEnd": 139, - "fullStart": 119 - } - }, - "fullEnd": 139, - "fullStart": 119 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L9:C7, L9:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L9:C4, L9:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 139, - "fullStart": 110 - }, - { - "context": { - "id": "node@@@[L10:C4, L10:C28]", - "snippet": "Ref: user_...> users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L10:C9, L10:C28]", - "snippet": "user_id ?> users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L10:C9, L10:C28]", - "snippet": "user_id ?> users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L10:C9, L10:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L10:C9, L10:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L10:C9, L10:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 156, - "fullStart": 148 - } - }, - "fullEnd": 156, - "fullStart": 148 - }, - "op": { - "context": { - "id": "token@@?>@[L10:C17, L10:C19]", - "snippet": "?>" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?>" - }, - "rightExpression": { - "context": { - "id": "node@@@[L10:C20, L10:C28]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L10:C20, L10:C25]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L10:C20, L10:C25]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L10:C20, L10:C25]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 164, - "fullStart": 159 - } - }, - "fullEnd": 164, - "fullStart": 159 - }, - "op": { - "context": { - "id": "token@@.@[L10:C25, L10:C26]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L10:C26, L10:C28]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L10:C26, L10:C28]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L10:C26, L10:C28]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 168, - "fullStart": 165 - } - }, - "fullEnd": 168, - "fullStart": 165 - } - }, - "fullEnd": 168, - "fullStart": 159 - } - }, - "fullEnd": 168, - "fullStart": 148 - } - }, - "fullEnd": 168, - "fullStart": 148 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L10:C7, L10:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L10:C4, L10:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 168, - "fullStart": 139 - }, - { - "context": { - "id": "node@@@[L11:C4, L11:C29]", - "snippet": "Ref: user_...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L11:C9, L11:C29]", - "snippet": "user_id ?>? users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L11:C9, L11:C29]", - "snippet": "user_id ?>? users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L11:C9, L11:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L11:C9, L11:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L11:C9, L11:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 185, - "fullStart": 177 - } - }, - "fullEnd": 185, - "fullStart": 177 - }, - "op": { - "context": { - "id": "token@@?>?@[L11:C17, L11:C20]", - "snippet": "?>?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?>?" - }, - "rightExpression": { - "context": { - "id": "node@@@[L11:C21, L11:C29]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L11:C21, L11:C26]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L11:C21, L11:C26]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L11:C21, L11:C26]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 194, - "fullStart": 189 - } - }, - "fullEnd": 194, - "fullStart": 189 - }, - "op": { - "context": { - "id": "token@@.@[L11:C26, L11:C27]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L11:C27, L11:C29]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L11:C27, L11:C29]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L11:C27, L11:C29]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 198, - "fullStart": 195 - } - }, - "fullEnd": 198, - "fullStart": 195 - } - }, - "fullEnd": 198, - "fullStart": 189 - } - }, - "fullEnd": 198, - "fullStart": 177 - } - }, - "fullEnd": 198, - "fullStart": 177 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L11:C7, L11:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L11:C4, L11:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 198, - "fullStart": 168 - }, - { - "context": { - "id": "node@@@[L12:C4, L12:C27]", - "snippet": "Ref: user_...< users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L12:C9, L12:C27]", - "snippet": "user_id < users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L12:C9, L12:C27]", - "snippet": "user_id < users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L12:C9, L12:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L12:C9, L12:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L12:C9, L12:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 215, - "fullStart": 207 - } - }, - "fullEnd": 215, - "fullStart": 207 - }, - "op": { - "context": { - "id": "token@@<@[L12:C17, L12:C18]", - "snippet": "<" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "<" - }, - "rightExpression": { - "context": { - "id": "node@@@[L12:C19, L12:C27]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L12:C19, L12:C24]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L12:C19, L12:C24]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L12:C19, L12:C24]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 222, - "fullStart": 217 - } - }, - "fullEnd": 222, - "fullStart": 217 - }, - "op": { - "context": { - "id": "token@@.@[L12:C24, L12:C25]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L12:C25, L12:C27]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L12:C25, L12:C27]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L12:C25, L12:C27]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 226, - "fullStart": 223 - } - }, - "fullEnd": 226, - "fullStart": 223 - } - }, - "fullEnd": 226, - "fullStart": 217 - } - }, - "fullEnd": 226, - "fullStart": 207 - } - }, - "fullEnd": 226, - "fullStart": 207 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L12:C7, L12:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L12:C4, L12:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 226, - "fullStart": 198 - }, - { - "context": { - "id": "node@@@[L13:C4, L13:C28]", - "snippet": "Ref: user_...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L13:C9, L13:C28]", - "snippet": "user_id @@[L13:C9, L13:C28]", - "snippet": "user_id @user_id@[L13:C9, L13:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L13:C9, L13:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L13:C9, L13:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 243, - "fullStart": 235 - } - }, - "fullEnd": 243, - "fullStart": 235 - }, - "op": { - "context": { - "id": "token@@@@[L13:C20, L13:C28]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L13:C20, L13:C25]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L13:C20, L13:C25]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L13:C20, L13:C25]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 251, - "fullStart": 246 - } - }, - "fullEnd": 251, - "fullStart": 246 - }, - "op": { - "context": { - "id": "token@@.@[L13:C25, L13:C26]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L13:C26, L13:C28]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L13:C26, L13:C28]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L13:C26, L13:C28]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 255, - "fullStart": 252 - } - }, - "fullEnd": 255, - "fullStart": 252 - } - }, - "fullEnd": 255, - "fullStart": 246 - } - }, - "fullEnd": 255, - "fullStart": 235 - } - }, - "fullEnd": 255, - "fullStart": 235 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L13:C7, L13:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L13:C4, L13:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 255, - "fullStart": 226 - }, - { - "context": { - "id": "node@@@[L14:C4, L14:C28]", - "snippet": "Ref: user_...< users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L14:C9, L14:C28]", - "snippet": "user_id ?< users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L14:C9, L14:C28]", - "snippet": "user_id ?< users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L14:C9, L14:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L14:C9, L14:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L14:C9, L14:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 272, - "fullStart": 264 - } - }, - "fullEnd": 272, - "fullStart": 264 - }, - "op": { - "context": { - "id": "token@@?<@[L14:C17, L14:C19]", - "snippet": "?<" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?<" - }, - "rightExpression": { - "context": { - "id": "node@@@[L14:C20, L14:C28]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L14:C20, L14:C25]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L14:C20, L14:C25]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L14:C20, L14:C25]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 280, - "fullStart": 275 - } - }, - "fullEnd": 280, - "fullStart": 275 - }, - "op": { - "context": { - "id": "token@@.@[L14:C25, L14:C26]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L14:C26, L14:C28]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L14:C26, L14:C28]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L14:C26, L14:C28]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 284, - "fullStart": 281 - } - }, - "fullEnd": 284, - "fullStart": 281 - } - }, - "fullEnd": 284, - "fullStart": 275 - } - }, - "fullEnd": 284, - "fullStart": 264 - } - }, - "fullEnd": 284, - "fullStart": 264 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L14:C7, L14:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L14:C4, L14:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 284, - "fullStart": 255 - }, - { - "context": { - "id": "node@@@[L15:C4, L15:C29]", - "snippet": "Ref: user_...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L15:C9, L15:C29]", - "snippet": "user_id ?@@[L15:C9, L15:C29]", - "snippet": "user_id ?@user_id@[L15:C9, L15:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L15:C9, L15:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L15:C9, L15:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 301, - "fullStart": 293 - } - }, - "fullEnd": 301, - "fullStart": 293 - }, - "op": { - "context": { - "id": "token@@?@@[L15:C21, L15:C29]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L15:C21, L15:C26]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L15:C21, L15:C26]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L15:C21, L15:C26]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 310, - "fullStart": 305 - } - }, - "fullEnd": 310, - "fullStart": 305 - }, - "op": { - "context": { - "id": "token@@.@[L15:C26, L15:C27]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L15:C27, L15:C29]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L15:C27, L15:C29]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L15:C27, L15:C29]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 314, - "fullStart": 311 - } - }, - "fullEnd": 314, - "fullStart": 311 - } - }, - "fullEnd": 314, - "fullStart": 305 - } - }, - "fullEnd": 314, - "fullStart": 293 - } - }, - "fullEnd": 314, - "fullStart": 293 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L15:C7, L15:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L15:C4, L15:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 314, - "fullStart": 284 - }, - { - "context": { - "id": "node@@@[L16:C4, L16:C27]", - "snippet": "Ref: user_...- users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L16:C9, L16:C27]", - "snippet": "user_id - users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L16:C9, L16:C27]", - "snippet": "user_id - users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L16:C9, L16:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L16:C9, L16:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L16:C9, L16:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 331, - "fullStart": 323 - } - }, - "fullEnd": 331, - "fullStart": 323 - }, - "op": { - "context": { - "id": "token@@-@[L16:C17, L16:C18]", - "snippet": "-" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "-" - }, - "rightExpression": { - "context": { - "id": "node@@@[L16:C19, L16:C27]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L16:C19, L16:C24]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L16:C19, L16:C24]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L16:C19, L16:C24]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 338, - "fullStart": 333 - } - }, - "fullEnd": 338, - "fullStart": 333 - }, - "op": { - "context": { - "id": "token@@.@[L16:C24, L16:C25]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L16:C25, L16:C27]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L16:C25, L16:C27]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L16:C25, L16:C27]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 342, - "fullStart": 339 - } - }, - "fullEnd": 342, - "fullStart": 339 - } - }, - "fullEnd": 342, - "fullStart": 333 - } - }, - "fullEnd": 342, - "fullStart": 323 - } - }, - "fullEnd": 342, - "fullStart": 323 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L16:C7, L16:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L16:C4, L16:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 342, - "fullStart": 314 - }, - { - "context": { - "id": "node@@@[L17:C4, L17:C28]", - "snippet": "Ref: user_...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L17:C9, L17:C28]", - "snippet": "user_id -? users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L17:C9, L17:C28]", - "snippet": "user_id -? users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L17:C9, L17:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L17:C9, L17:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L17:C9, L17:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 359, - "fullStart": 351 - } - }, - "fullEnd": 359, - "fullStart": 351 - }, - "op": { - "context": { - "id": "token@@-?@[L17:C17, L17:C19]", - "snippet": "-?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "-?" - }, - "rightExpression": { - "context": { - "id": "node@@@[L17:C20, L17:C28]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L17:C20, L17:C25]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L17:C20, L17:C25]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L17:C20, L17:C25]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 367, - "fullStart": 362 - } - }, - "fullEnd": 367, - "fullStart": 362 - }, - "op": { - "context": { - "id": "token@@.@[L17:C25, L17:C26]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L17:C26, L17:C28]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L17:C26, L17:C28]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L17:C26, L17:C28]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 371, - "fullStart": 368 - } - }, - "fullEnd": 371, - "fullStart": 368 - } - }, - "fullEnd": 371, - "fullStart": 362 - } - }, - "fullEnd": 371, - "fullStart": 351 - } - }, - "fullEnd": 371, - "fullStart": 351 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L17:C7, L17:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L17:C4, L17:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 371, - "fullStart": 342 - }, - { - "context": { - "id": "node@@@[L18:C4, L18:C28]", - "snippet": "Ref: user_...- users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L18:C9, L18:C28]", - "snippet": "user_id ?- users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L18:C9, L18:C28]", - "snippet": "user_id ?- users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L18:C9, L18:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L18:C9, L18:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L18:C9, L18:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 388, - "fullStart": 380 - } - }, - "fullEnd": 388, - "fullStart": 380 - }, - "op": { - "context": { - "id": "token@@?-@[L18:C17, L18:C19]", - "snippet": "?-" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?-" - }, - "rightExpression": { - "context": { - "id": "node@@@[L18:C20, L18:C28]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L18:C20, L18:C25]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L18:C20, L18:C25]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L18:C20, L18:C25]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 396, - "fullStart": 391 - } - }, - "fullEnd": 396, - "fullStart": 391 - }, - "op": { - "context": { - "id": "token@@.@[L18:C25, L18:C26]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L18:C26, L18:C28]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L18:C26, L18:C28]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L18:C26, L18:C28]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 400, - "fullStart": 397 - } - }, - "fullEnd": 400, - "fullStart": 397 - } - }, - "fullEnd": 400, - "fullStart": 391 - } - }, - "fullEnd": 400, - "fullStart": 380 - } - }, - "fullEnd": 400, - "fullStart": 380 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L18:C7, L18:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L18:C4, L18:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 400, - "fullStart": 371 - }, - { - "context": { - "id": "node@@@[L19:C4, L19:C29]", - "snippet": "Ref: user_...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L19:C9, L19:C29]", - "snippet": "user_id ?-? users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L19:C9, L19:C29]", - "snippet": "user_id ?-? users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L19:C9, L19:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L19:C9, L19:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L19:C9, L19:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 417, - "fullStart": 409 - } - }, - "fullEnd": 417, - "fullStart": 409 - }, - "op": { - "context": { - "id": "token@@?-?@[L19:C17, L19:C20]", - "snippet": "?-?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?-?" - }, - "rightExpression": { - "context": { - "id": "node@@@[L19:C21, L19:C29]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L19:C21, L19:C26]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L19:C21, L19:C26]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L19:C21, L19:C26]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 426, - "fullStart": 421 - } - }, - "fullEnd": 426, - "fullStart": 421 - }, - "op": { - "context": { - "id": "token@@.@[L19:C26, L19:C27]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L19:C27, L19:C29]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L19:C27, L19:C29]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L19:C27, L19:C29]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 430, - "fullStart": 427 - } - }, - "fullEnd": 430, - "fullStart": 427 - } - }, - "fullEnd": 430, - "fullStart": 421 - } - }, - "fullEnd": 430, - "fullStart": 409 - } - }, - "fullEnd": 430, - "fullStart": 409 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L19:C7, L19:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L19:C4, L19:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 430, - "fullStart": 400 - }, - { - "context": { - "id": "node@@@[L20:C4, L20:C28]", - "snippet": "Ref: user_...> users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L20:C9, L20:C28]", - "snippet": "user_id <> users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L20:C9, L20:C28]", - "snippet": "user_id <> users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L20:C9, L20:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L20:C9, L20:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L20:C9, L20:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 447, - "fullStart": 439 - } - }, - "fullEnd": 447, - "fullStart": 439 - }, - "op": { - "context": { - "id": "token@@<>@[L20:C17, L20:C19]", - "snippet": "<>" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "<>" - }, - "rightExpression": { - "context": { - "id": "node@@@[L20:C20, L20:C28]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L20:C20, L20:C25]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L20:C20, L20:C25]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L20:C20, L20:C25]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 455, - "fullStart": 450 - } - }, - "fullEnd": 455, - "fullStart": 450 - }, - "op": { - "context": { - "id": "token@@.@[L20:C25, L20:C26]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L20:C26, L20:C28]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L20:C26, L20:C28]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L20:C26, L20:C28]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 459, - "fullStart": 456 - } - }, - "fullEnd": 459, - "fullStart": 456 - } - }, - "fullEnd": 459, - "fullStart": 450 - } - }, - "fullEnd": 459, - "fullStart": 439 - } - }, - "fullEnd": 459, - "fullStart": 439 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L20:C7, L20:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L20:C4, L20:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 459, - "fullStart": 430 - }, - { - "context": { - "id": "node@@@[L21:C4, L21:C29]", - "snippet": "Ref: user_...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L21:C9, L21:C29]", - "snippet": "user_id <>? users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L21:C9, L21:C29]", - "snippet": "user_id <>? users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L21:C9, L21:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L21:C9, L21:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L21:C9, L21:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 476, - "fullStart": 468 - } - }, - "fullEnd": 476, - "fullStart": 468 - }, - "op": { - "context": { - "id": "token@@<>?@[L21:C17, L21:C20]", - "snippet": "<>?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "<>?" - }, - "rightExpression": { - "context": { - "id": "node@@@[L21:C21, L21:C29]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L21:C21, L21:C26]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L21:C21, L21:C26]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L21:C21, L21:C26]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 485, - "fullStart": 480 - } - }, - "fullEnd": 485, - "fullStart": 480 - }, - "op": { - "context": { - "id": "token@@.@[L21:C26, L21:C27]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L21:C27, L21:C29]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L21:C27, L21:C29]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L21:C27, L21:C29]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 489, - "fullStart": 486 - } - }, - "fullEnd": 489, - "fullStart": 486 - } - }, - "fullEnd": 489, - "fullStart": 480 - } - }, - "fullEnd": 489, - "fullStart": 468 - } - }, - "fullEnd": 489, - "fullStart": 468 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L21:C7, L21:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L21:C4, L21:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 489, - "fullStart": 459 - }, - { - "context": { - "id": "node@@@[L22:C4, L22:C29]", - "snippet": "Ref: user_...> users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L22:C9, L22:C29]", - "snippet": "user_id ?<> users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L22:C9, L22:C29]", - "snippet": "user_id ?<> users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L22:C9, L22:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L22:C9, L22:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L22:C9, L22:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 506, - "fullStart": 498 - } - }, - "fullEnd": 506, - "fullStart": 498 - }, - "op": { - "context": { - "id": "token@@?<>@[L22:C17, L22:C20]", - "snippet": "?<>" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?<>" - }, - "rightExpression": { - "context": { - "id": "node@@@[L22:C21, L22:C29]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L22:C21, L22:C26]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L22:C21, L22:C26]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L22:C21, L22:C26]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 515, - "fullStart": 510 - } - }, - "fullEnd": 515, - "fullStart": 510 - }, - "op": { - "context": { - "id": "token@@.@[L22:C26, L22:C27]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L22:C27, L22:C29]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L22:C27, L22:C29]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L22:C27, L22:C29]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 519, - "fullStart": 516 - } - }, - "fullEnd": 519, - "fullStart": 516 - } - }, - "fullEnd": 519, - "fullStart": 510 - } - }, - "fullEnd": 519, - "fullStart": 498 - } - }, - "fullEnd": 519, - "fullStart": 498 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L22:C7, L22:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L22:C4, L22:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 519, - "fullStart": 489 - }, - { - "context": { - "id": "node@@@[L23:C4, L23:C30]", - "snippet": "Ref: user_...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L23:C9, L23:C30]", - "snippet": "user_id ?<...? users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L23:C9, L23:C30]", - "snippet": "user_id ?<...? users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L23:C9, L23:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L23:C9, L23:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L23:C9, L23:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 536, - "fullStart": 528 - } - }, - "fullEnd": 536, - "fullStart": 528 - }, - "op": { - "context": { - "id": "token@@?<>?@[L23:C17, L23:C21]", - "snippet": "?<>?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?<>?" - }, - "rightExpression": { - "context": { - "id": "node@@@[L23:C22, L23:C30]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L23:C22, L23:C27]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L23:C22, L23:C27]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L23:C22, L23:C27]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 546, - "fullStart": 541 - } - }, - "fullEnd": 546, - "fullStart": 541 - }, - "op": { - "context": { - "id": "token@@.@[L23:C27, L23:C28]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L23:C28, L23:C30]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L23:C28, L23:C30]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L23:C28, L23:C30]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 550, - "fullStart": 547 - } - }, - "fullEnd": 550, - "fullStart": 547 - } - }, - "fullEnd": 550, - "fullStart": 541 - } - }, - "fullEnd": 550, - "fullStart": 528 - } - }, - "fullEnd": 550, - "fullStart": 528 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L23:C7, L23:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L23:C4, L23:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 550, - "fullStart": 519 - } - ] - }, - "fullEnd": 552, - "fullStart": 44 - }, - "name": { - "context": { - "id": "node@@posts@[L4:C6, L4:C11]", - "snippet": "posts" - }, - "children": { - "expression": { - "context": { - "id": "node@@posts@[L4:C6, L4:C11]", - "snippet": "posts" - }, - "children": { - "variable": { - "context": { - "id": "token@@posts@[L4:C6, L4:C11]", - "snippet": "posts" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "posts" - } - }, - "fullEnd": 44, - "fullStart": 38 - } - }, - "fullEnd": 44, - "fullStart": 38 - }, - "type": { - "context": { - "id": "token@@Table@[L4:C0, L4:C5]", - "snippet": "Table" - }, - "leadingTrivia": "\n", - "trailingTrivia": " ", - "value": "Table" - } - }, - "fullEnd": 552, - "fullStart": 31 - } - ], - "eof": { - "context": { - "id": "token@@@[L25:C0, L25:C0]", - "snippet": "" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "" - } - }, - "fullEnd": 552, - "fullStart": 0 - } -} \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/validator/input/optional_ref.in.dbml b/packages/dbml-parse/__tests__/snapshots/validator/input/optional_ref.in.dbml deleted file mode 100644 index 9905c5f23..000000000 --- a/packages/dbml-parse/__tests__/snapshots/validator/input/optional_ref.in.dbml +++ /dev/null @@ -1,26 +0,0 @@ -Table users { - id integer -} - -Table posts { - id integer - user_id integer - - Ref: user_id > users.id - Ref: user_id >? users.id - Ref: user_id ?> users.id - Ref: user_id ?>? users.id - Ref: user_id - users.id - Ref: user_id -? users.id - Ref: user_id ?- users.id - Ref: user_id ?-? users.id - Ref: user_id <> users.id - Ref: user_id <>? users.id - Ref: user_id ?<> users.id - Ref: user_id ?<>? users.id -} - -Ref: posts.user_id < users.id -Ref: posts.user_id @@[L8:C4, L8:C27]", - "snippet": "Ref: user_...> users.id" - } - } - }, - { - "code": "INVALID_REF_FIELD", - "diagnostic": "Invalid column reference", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@user_id@[L8:C9, L8:C16]", - "snippet": "user_id" - } - } - }, - { - "code": "INVALID_REF_CONTEXT", - "diagnostic": "A Ref must appear top-level", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L9:C4, L9:C28]", - "snippet": "Ref: user_...? users.id" - } - } - }, - { - "code": "INVALID_REF_FIELD", - "diagnostic": "Invalid column reference", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@user_id@[L9:C9, L9:C16]", - "snippet": "user_id" - } - } - }, - { - "code": "INVALID_REF_CONTEXT", - "diagnostic": "A Ref must appear top-level", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L10:C4, L10:C28]", - "snippet": "Ref: user_...> users.id" - } - } - }, - { - "code": "INVALID_REF_FIELD", - "diagnostic": "Invalid column reference", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@user_id@[L10:C9, L10:C16]", - "snippet": "user_id" - } - } - }, - { - "code": "INVALID_REF_CONTEXT", - "diagnostic": "A Ref must appear top-level", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L11:C4, L11:C29]", - "snippet": "Ref: user_...? users.id" - } - } - }, - { - "code": "INVALID_REF_FIELD", - "diagnostic": "Invalid column reference", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@user_id@[L11:C9, L11:C16]", - "snippet": "user_id" - } - } - }, - { - "code": "INVALID_REF_CONTEXT", - "diagnostic": "A Ref must appear top-level", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L12:C4, L12:C27]", - "snippet": "Ref: user_...- users.id" - } - } - }, - { - "code": "INVALID_REF_FIELD", - "diagnostic": "Invalid column reference", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@user_id@[L12:C9, L12:C16]", - "snippet": "user_id" - } - } - }, - { - "code": "INVALID_REF_CONTEXT", - "diagnostic": "A Ref must appear top-level", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L13:C4, L13:C28]", - "snippet": "Ref: user_...? users.id" - } - } - }, - { - "code": "INVALID_REF_FIELD", - "diagnostic": "Invalid column reference", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@user_id@[L13:C9, L13:C16]", - "snippet": "user_id" - } - } - }, - { - "code": "INVALID_REF_CONTEXT", - "diagnostic": "A Ref must appear top-level", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L14:C4, L14:C28]", - "snippet": "Ref: user_...- users.id" - } - } - }, - { - "code": "INVALID_REF_FIELD", - "diagnostic": "Invalid column reference", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@user_id@[L14:C9, L14:C16]", - "snippet": "user_id" - } - } - }, - { - "code": "INVALID_REF_CONTEXT", - "diagnostic": "A Ref must appear top-level", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L15:C4, L15:C29]", - "snippet": "Ref: user_...? users.id" - } - } - }, - { - "code": "INVALID_REF_FIELD", - "diagnostic": "Invalid column reference", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@user_id@[L15:C9, L15:C16]", - "snippet": "user_id" - } - } - }, - { - "code": "INVALID_REF_CONTEXT", - "diagnostic": "A Ref must appear top-level", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L16:C4, L16:C28]", - "snippet": "Ref: user_...> users.id" - } - } - }, - { - "code": "INVALID_REF_FIELD", - "diagnostic": "Invalid column reference", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@user_id@[L16:C9, L16:C16]", - "snippet": "user_id" - } - } - }, - { - "code": "INVALID_REF_CONTEXT", - "diagnostic": "A Ref must appear top-level", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L17:C4, L17:C29]", - "snippet": "Ref: user_...? users.id" - } - } - }, - { - "code": "INVALID_REF_FIELD", - "diagnostic": "Invalid column reference", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@user_id@[L17:C9, L17:C16]", - "snippet": "user_id" - } - } - }, - { - "code": "INVALID_REF_CONTEXT", - "diagnostic": "A Ref must appear top-level", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L18:C4, L18:C29]", - "snippet": "Ref: user_...> users.id" - } - } - }, - { - "code": "INVALID_REF_FIELD", - "diagnostic": "Invalid column reference", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@user_id@[L18:C9, L18:C16]", - "snippet": "user_id" - } - } - }, - { - "code": "INVALID_REF_CONTEXT", - "diagnostic": "A Ref must appear top-level", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L19:C4, L19:C30]", - "snippet": "Ref: user_...? users.id" - } - } - }, - { - "code": "INVALID_REF_FIELD", - "diagnostic": "Invalid column reference", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@user_id@[L19:C9, L19:C16]", - "snippet": "user_id" - } - } - } - ], - "program": { - "context": { - "id": "node@@@[L0:C0, L26:C0]", - "snippet": "Table user... users.id\n" - }, - "children": { - "body": [ - { - "context": { - "id": "node@@users@[L0:C0, L2:C1]", - "snippet": "Table user... integer\n}" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L0:C12, L2:C1]", - "snippet": "{\n id integer\n}" - }, - "children": { - "blockCloseBrace": { - "context": { - "id": "token@@}@[L2:C0, L2:C1]", - "snippet": "}" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "}" - }, - "blockOpenBrace": { - "context": { - "id": "token@@{@[L0:C12, L0:C13]", - "snippet": "{" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "{" - }, - "body": [ - { - "context": { - "id": "node@@id@[L1:C4, L1:C14]", - "snippet": "id integer" - }, - "children": { - "args": [ - { - "context": { - "id": "node@@integer@[L1:C7, L1:C14]", - "snippet": "integer" - }, - "children": { - "expression": { - "context": { - "id": "node@@integer@[L1:C7, L1:C14]", - "snippet": "integer" - }, - "children": { - "variable": { - "context": { - "id": "token@@integer@[L1:C7, L1:C14]", - "snippet": "integer" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "integer" - } - }, - "fullEnd": 29, - "fullStart": 21 - } - }, - "fullEnd": 29, - "fullStart": 21 - } - ], - "callee": { - "context": { - "id": "node@@id@[L1:C4, L1:C6]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L1:C4, L1:C6]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L1:C4, L1:C6]", - "snippet": "id" - }, - "leadingTrivia": " ", - "trailingTrivia": " ", - "value": "id" - } - }, - "fullEnd": 21, - "fullStart": 14 - } - }, - "fullEnd": 21, - "fullStart": 14 - } - }, - "fullEnd": 29, - "fullStart": 14 - } - ] - }, - "fullEnd": 31, - "fullStart": 12 - }, - "name": { - "context": { - "id": "node@@users@[L0:C6, L0:C11]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L0:C6, L0:C11]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L0:C6, L0:C11]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "users" - } - }, - "fullEnd": 12, - "fullStart": 6 - } - }, - "fullEnd": 12, - "fullStart": 6 - }, - "type": { - "context": { - "id": "token@@Table@[L0:C0, L0:C5]", - "snippet": "Table" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "Table" - } - }, - "fullEnd": 31, - "fullStart": 0 - }, - { - "context": { - "id": "node@@posts@[L4:C0, L20:C1]", - "snippet": "Table post...users.id\n}" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L4:C12, L20:C1]", - "snippet": "{\n id i...users.id\n}" - }, - "children": { - "blockCloseBrace": { - "context": { - "id": "token@@}@[L20:C0, L20:C1]", - "snippet": "}" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "}" - }, - "blockOpenBrace": { - "context": { - "id": "token@@{@[L4:C12, L4:C13]", - "snippet": "{" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "{" - }, - "body": [ - { - "context": { - "id": "node@@id@[L5:C4, L5:C14]", - "snippet": "id integer" - }, - "children": { - "args": [ - { - "context": { - "id": "node@@integer@[L5:C7, L5:C14]", - "snippet": "integer" - }, - "children": { - "expression": { - "context": { - "id": "node@@integer@[L5:C7, L5:C14]", - "snippet": "integer" - }, - "children": { - "variable": { - "context": { - "id": "token@@integer@[L5:C7, L5:C14]", - "snippet": "integer" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "integer" - } - }, - "fullEnd": 61, - "fullStart": 53 - } - }, - "fullEnd": 61, - "fullStart": 53 - } - ], - "callee": { - "context": { - "id": "node@@id@[L5:C4, L5:C6]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L5:C4, L5:C6]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L5:C4, L5:C6]", - "snippet": "id" - }, - "leadingTrivia": " ", - "trailingTrivia": " ", - "value": "id" - } - }, - "fullEnd": 53, - "fullStart": 46 - } - }, - "fullEnd": 53, - "fullStart": 46 - } - }, - "fullEnd": 61, - "fullStart": 46 - }, - { - "context": { - "id": "node@@user_id@[L6:C4, L6:C19]", - "snippet": "user_id integer" - }, - "children": { - "args": [ - { - "context": { - "id": "node@@integer@[L6:C12, L6:C19]", - "snippet": "integer" - }, - "children": { - "expression": { - "context": { - "id": "node@@integer@[L6:C12, L6:C19]", - "snippet": "integer" - }, - "children": { - "variable": { - "context": { - "id": "token@@integer@[L6:C12, L6:C19]", - "snippet": "integer" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "integer" - } - }, - "fullEnd": 81, - "fullStart": 73 - } - }, - "fullEnd": 81, - "fullStart": 73 - } - ], - "callee": { - "context": { - "id": "node@@user_id@[L6:C4, L6:C11]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L6:C4, L6:C11]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L6:C4, L6:C11]", - "snippet": "user_id" - }, - "leadingTrivia": " ", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 73, - "fullStart": 61 - } - }, - "fullEnd": 73, - "fullStart": 61 - } - }, - "fullEnd": 81, - "fullStart": 61 - }, - { - "context": { - "id": "node@@@[L8:C4, L8:C27]", - "snippet": "Ref: user_...> users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L8:C9, L8:C27]", - "snippet": "user_id > users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L8:C9, L8:C27]", - "snippet": "user_id > users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L8:C9, L8:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L8:C9, L8:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L8:C9, L8:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 99, - "fullStart": 91 - } - }, - "fullEnd": 99, - "fullStart": 91 - }, - "op": { - "context": { - "id": "token@@>@[L8:C17, L8:C18]", - "snippet": ">" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ">" - }, - "rightExpression": { - "context": { - "id": "node@@@[L8:C19, L8:C27]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L8:C19, L8:C24]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L8:C19, L8:C24]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L8:C19, L8:C24]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 106, - "fullStart": 101 - } - }, - "fullEnd": 106, - "fullStart": 101 - }, - "op": { - "context": { - "id": "token@@.@[L8:C24, L8:C25]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L8:C25, L8:C27]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L8:C25, L8:C27]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L8:C25, L8:C27]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 110, - "fullStart": 107 - } - }, - "fullEnd": 110, - "fullStart": 107 - } - }, - "fullEnd": 110, - "fullStart": 101 - } - }, - "fullEnd": 110, - "fullStart": 91 - } - }, - "fullEnd": 110, - "fullStart": 91 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L8:C7, L8:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L8:C4, L8:C7]", - "snippet": "Ref" - }, - "leadingTrivia": "\n ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 110, - "fullStart": 81 - }, - { - "context": { - "id": "node@@@[L9:C4, L9:C28]", - "snippet": "Ref: user_...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L9:C9, L9:C28]", - "snippet": "user_id >? users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L9:C9, L9:C28]", - "snippet": "user_id >? users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L9:C9, L9:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L9:C9, L9:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L9:C9, L9:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 127, - "fullStart": 119 - } - }, - "fullEnd": 127, - "fullStart": 119 - }, - "op": { - "context": { - "id": "token@@>?@[L9:C17, L9:C19]", - "snippet": ">?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ">?" - }, - "rightExpression": { - "context": { - "id": "node@@@[L9:C20, L9:C28]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L9:C20, L9:C25]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L9:C20, L9:C25]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L9:C20, L9:C25]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 135, - "fullStart": 130 - } - }, - "fullEnd": 135, - "fullStart": 130 - }, - "op": { - "context": { - "id": "token@@.@[L9:C25, L9:C26]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L9:C26, L9:C28]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L9:C26, L9:C28]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L9:C26, L9:C28]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 139, - "fullStart": 136 - } - }, - "fullEnd": 139, - "fullStart": 136 - } - }, - "fullEnd": 139, - "fullStart": 130 - } - }, - "fullEnd": 139, - "fullStart": 119 - } - }, - "fullEnd": 139, - "fullStart": 119 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L9:C7, L9:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L9:C4, L9:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 139, - "fullStart": 110 - }, - { - "context": { - "id": "node@@@[L10:C4, L10:C28]", - "snippet": "Ref: user_...> users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L10:C9, L10:C28]", - "snippet": "user_id ?> users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L10:C9, L10:C28]", - "snippet": "user_id ?> users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L10:C9, L10:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L10:C9, L10:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L10:C9, L10:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 156, - "fullStart": 148 - } - }, - "fullEnd": 156, - "fullStart": 148 - }, - "op": { - "context": { - "id": "token@@?>@[L10:C17, L10:C19]", - "snippet": "?>" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?>" - }, - "rightExpression": { - "context": { - "id": "node@@@[L10:C20, L10:C28]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L10:C20, L10:C25]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L10:C20, L10:C25]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L10:C20, L10:C25]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 164, - "fullStart": 159 - } - }, - "fullEnd": 164, - "fullStart": 159 - }, - "op": { - "context": { - "id": "token@@.@[L10:C25, L10:C26]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L10:C26, L10:C28]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L10:C26, L10:C28]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L10:C26, L10:C28]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 168, - "fullStart": 165 - } - }, - "fullEnd": 168, - "fullStart": 165 - } - }, - "fullEnd": 168, - "fullStart": 159 - } - }, - "fullEnd": 168, - "fullStart": 148 - } - }, - "fullEnd": 168, - "fullStart": 148 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L10:C7, L10:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L10:C4, L10:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 168, - "fullStart": 139 - }, - { - "context": { - "id": "node@@@[L11:C4, L11:C29]", - "snippet": "Ref: user_...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L11:C9, L11:C29]", - "snippet": "user_id ?>? users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L11:C9, L11:C29]", - "snippet": "user_id ?>? users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L11:C9, L11:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L11:C9, L11:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L11:C9, L11:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 185, - "fullStart": 177 - } - }, - "fullEnd": 185, - "fullStart": 177 - }, - "op": { - "context": { - "id": "token@@?>?@[L11:C17, L11:C20]", - "snippet": "?>?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?>?" - }, - "rightExpression": { - "context": { - "id": "node@@@[L11:C21, L11:C29]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L11:C21, L11:C26]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L11:C21, L11:C26]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L11:C21, L11:C26]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 194, - "fullStart": 189 - } - }, - "fullEnd": 194, - "fullStart": 189 - }, - "op": { - "context": { - "id": "token@@.@[L11:C26, L11:C27]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L11:C27, L11:C29]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L11:C27, L11:C29]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L11:C27, L11:C29]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 198, - "fullStart": 195 - } - }, - "fullEnd": 198, - "fullStart": 195 - } - }, - "fullEnd": 198, - "fullStart": 189 - } - }, - "fullEnd": 198, - "fullStart": 177 - } - }, - "fullEnd": 198, - "fullStart": 177 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L11:C7, L11:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L11:C4, L11:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 198, - "fullStart": 168 - }, - { - "context": { - "id": "node@@@[L12:C4, L12:C27]", - "snippet": "Ref: user_...- users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L12:C9, L12:C27]", - "snippet": "user_id - users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L12:C9, L12:C27]", - "snippet": "user_id - users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L12:C9, L12:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L12:C9, L12:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L12:C9, L12:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 215, - "fullStart": 207 - } - }, - "fullEnd": 215, - "fullStart": 207 - }, - "op": { - "context": { - "id": "token@@-@[L12:C17, L12:C18]", - "snippet": "-" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "-" - }, - "rightExpression": { - "context": { - "id": "node@@@[L12:C19, L12:C27]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L12:C19, L12:C24]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L12:C19, L12:C24]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L12:C19, L12:C24]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 222, - "fullStart": 217 - } - }, - "fullEnd": 222, - "fullStart": 217 - }, - "op": { - "context": { - "id": "token@@.@[L12:C24, L12:C25]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L12:C25, L12:C27]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L12:C25, L12:C27]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L12:C25, L12:C27]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 226, - "fullStart": 223 - } - }, - "fullEnd": 226, - "fullStart": 223 - } - }, - "fullEnd": 226, - "fullStart": 217 - } - }, - "fullEnd": 226, - "fullStart": 207 - } - }, - "fullEnd": 226, - "fullStart": 207 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L12:C7, L12:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L12:C4, L12:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 226, - "fullStart": 198 - }, - { - "context": { - "id": "node@@@[L13:C4, L13:C28]", - "snippet": "Ref: user_...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L13:C9, L13:C28]", - "snippet": "user_id -? users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L13:C9, L13:C28]", - "snippet": "user_id -? users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L13:C9, L13:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L13:C9, L13:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L13:C9, L13:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 243, - "fullStart": 235 - } - }, - "fullEnd": 243, - "fullStart": 235 - }, - "op": { - "context": { - "id": "token@@-?@[L13:C17, L13:C19]", - "snippet": "-?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "-?" - }, - "rightExpression": { - "context": { - "id": "node@@@[L13:C20, L13:C28]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L13:C20, L13:C25]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L13:C20, L13:C25]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L13:C20, L13:C25]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 251, - "fullStart": 246 - } - }, - "fullEnd": 251, - "fullStart": 246 - }, - "op": { - "context": { - "id": "token@@.@[L13:C25, L13:C26]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L13:C26, L13:C28]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L13:C26, L13:C28]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L13:C26, L13:C28]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 255, - "fullStart": 252 - } - }, - "fullEnd": 255, - "fullStart": 252 - } - }, - "fullEnd": 255, - "fullStart": 246 - } - }, - "fullEnd": 255, - "fullStart": 235 - } - }, - "fullEnd": 255, - "fullStart": 235 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L13:C7, L13:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L13:C4, L13:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 255, - "fullStart": 226 - }, - { - "context": { - "id": "node@@@[L14:C4, L14:C28]", - "snippet": "Ref: user_...- users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L14:C9, L14:C28]", - "snippet": "user_id ?- users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L14:C9, L14:C28]", - "snippet": "user_id ?- users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L14:C9, L14:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L14:C9, L14:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L14:C9, L14:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 272, - "fullStart": 264 - } - }, - "fullEnd": 272, - "fullStart": 264 - }, - "op": { - "context": { - "id": "token@@?-@[L14:C17, L14:C19]", - "snippet": "?-" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?-" - }, - "rightExpression": { - "context": { - "id": "node@@@[L14:C20, L14:C28]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L14:C20, L14:C25]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L14:C20, L14:C25]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L14:C20, L14:C25]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 280, - "fullStart": 275 - } - }, - "fullEnd": 280, - "fullStart": 275 - }, - "op": { - "context": { - "id": "token@@.@[L14:C25, L14:C26]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L14:C26, L14:C28]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L14:C26, L14:C28]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L14:C26, L14:C28]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 284, - "fullStart": 281 - } - }, - "fullEnd": 284, - "fullStart": 281 - } - }, - "fullEnd": 284, - "fullStart": 275 - } - }, - "fullEnd": 284, - "fullStart": 264 - } - }, - "fullEnd": 284, - "fullStart": 264 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L14:C7, L14:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L14:C4, L14:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 284, - "fullStart": 255 - }, - { - "context": { - "id": "node@@@[L15:C4, L15:C29]", - "snippet": "Ref: user_...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L15:C9, L15:C29]", - "snippet": "user_id ?-? users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L15:C9, L15:C29]", - "snippet": "user_id ?-? users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L15:C9, L15:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L15:C9, L15:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L15:C9, L15:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 301, - "fullStart": 293 - } - }, - "fullEnd": 301, - "fullStart": 293 - }, - "op": { - "context": { - "id": "token@@?-?@[L15:C17, L15:C20]", - "snippet": "?-?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?-?" - }, - "rightExpression": { - "context": { - "id": "node@@@[L15:C21, L15:C29]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L15:C21, L15:C26]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L15:C21, L15:C26]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L15:C21, L15:C26]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 310, - "fullStart": 305 - } - }, - "fullEnd": 310, - "fullStart": 305 - }, - "op": { - "context": { - "id": "token@@.@[L15:C26, L15:C27]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L15:C27, L15:C29]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L15:C27, L15:C29]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L15:C27, L15:C29]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 314, - "fullStart": 311 - } - }, - "fullEnd": 314, - "fullStart": 311 - } - }, - "fullEnd": 314, - "fullStart": 305 - } - }, - "fullEnd": 314, - "fullStart": 293 - } - }, - "fullEnd": 314, - "fullStart": 293 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L15:C7, L15:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L15:C4, L15:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 314, - "fullStart": 284 - }, - { - "context": { - "id": "node@@@[L16:C4, L16:C28]", - "snippet": "Ref: user_...> users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L16:C9, L16:C28]", - "snippet": "user_id <> users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L16:C9, L16:C28]", - "snippet": "user_id <> users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L16:C9, L16:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L16:C9, L16:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L16:C9, L16:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 331, - "fullStart": 323 - } - }, - "fullEnd": 331, - "fullStart": 323 - }, - "op": { - "context": { - "id": "token@@<>@[L16:C17, L16:C19]", - "snippet": "<>" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "<>" - }, - "rightExpression": { - "context": { - "id": "node@@@[L16:C20, L16:C28]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L16:C20, L16:C25]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L16:C20, L16:C25]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L16:C20, L16:C25]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 339, - "fullStart": 334 - } - }, - "fullEnd": 339, - "fullStart": 334 - }, - "op": { - "context": { - "id": "token@@.@[L16:C25, L16:C26]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L16:C26, L16:C28]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L16:C26, L16:C28]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L16:C26, L16:C28]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 343, - "fullStart": 340 - } - }, - "fullEnd": 343, - "fullStart": 340 - } - }, - "fullEnd": 343, - "fullStart": 334 - } - }, - "fullEnd": 343, - "fullStart": 323 - } - }, - "fullEnd": 343, - "fullStart": 323 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L16:C7, L16:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L16:C4, L16:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 343, - "fullStart": 314 - }, - { - "context": { - "id": "node@@@[L17:C4, L17:C29]", - "snippet": "Ref: user_...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L17:C9, L17:C29]", - "snippet": "user_id <>? users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L17:C9, L17:C29]", - "snippet": "user_id <>? users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L17:C9, L17:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L17:C9, L17:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L17:C9, L17:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 360, - "fullStart": 352 - } - }, - "fullEnd": 360, - "fullStart": 352 - }, - "op": { - "context": { - "id": "token@@<>?@[L17:C17, L17:C20]", - "snippet": "<>?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "<>?" - }, - "rightExpression": { - "context": { - "id": "node@@@[L17:C21, L17:C29]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L17:C21, L17:C26]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L17:C21, L17:C26]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L17:C21, L17:C26]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 369, - "fullStart": 364 - } - }, - "fullEnd": 369, - "fullStart": 364 - }, - "op": { - "context": { - "id": "token@@.@[L17:C26, L17:C27]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L17:C27, L17:C29]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L17:C27, L17:C29]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L17:C27, L17:C29]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 373, - "fullStart": 370 - } - }, - "fullEnd": 373, - "fullStart": 370 - } - }, - "fullEnd": 373, - "fullStart": 364 - } - }, - "fullEnd": 373, - "fullStart": 352 - } - }, - "fullEnd": 373, - "fullStart": 352 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L17:C7, L17:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L17:C4, L17:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 373, - "fullStart": 343 - }, - { - "context": { - "id": "node@@@[L18:C4, L18:C29]", - "snippet": "Ref: user_...> users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L18:C9, L18:C29]", - "snippet": "user_id ?<> users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L18:C9, L18:C29]", - "snippet": "user_id ?<> users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L18:C9, L18:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L18:C9, L18:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L18:C9, L18:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 390, - "fullStart": 382 - } - }, - "fullEnd": 390, - "fullStart": 382 - }, - "op": { - "context": { - "id": "token@@?<>@[L18:C17, L18:C20]", - "snippet": "?<>" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?<>" - }, - "rightExpression": { - "context": { - "id": "node@@@[L18:C21, L18:C29]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L18:C21, L18:C26]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L18:C21, L18:C26]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L18:C21, L18:C26]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 399, - "fullStart": 394 - } - }, - "fullEnd": 399, - "fullStart": 394 - }, - "op": { - "context": { - "id": "token@@.@[L18:C26, L18:C27]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L18:C27, L18:C29]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L18:C27, L18:C29]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L18:C27, L18:C29]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 403, - "fullStart": 400 - } - }, - "fullEnd": 403, - "fullStart": 400 - } - }, - "fullEnd": 403, - "fullStart": 394 - } - }, - "fullEnd": 403, - "fullStart": 382 - } - }, - "fullEnd": 403, - "fullStart": 382 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L18:C7, L18:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L18:C4, L18:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 403, - "fullStart": 373 - }, - { - "context": { - "id": "node@@@[L19:C4, L19:C30]", - "snippet": "Ref: user_...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L19:C9, L19:C30]", - "snippet": "user_id ?<...? users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L19:C9, L19:C30]", - "snippet": "user_id ?<...? users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@user_id@[L19:C9, L19:C16]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L19:C9, L19:C16]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L19:C9, L19:C16]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 420, - "fullStart": 412 - } - }, - "fullEnd": 420, - "fullStart": 412 - }, - "op": { - "context": { - "id": "token@@?<>?@[L19:C17, L19:C21]", - "snippet": "?<>?" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?<>?" - }, - "rightExpression": { - "context": { - "id": "node@@@[L19:C22, L19:C30]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L19:C22, L19:C27]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L19:C22, L19:C27]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L19:C22, L19:C27]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 430, - "fullStart": 425 - } - }, - "fullEnd": 430, - "fullStart": 425 - }, - "op": { - "context": { - "id": "token@@.@[L19:C27, L19:C28]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L19:C28, L19:C30]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L19:C28, L19:C30]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L19:C28, L19:C30]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 434, - "fullStart": 431 - } - }, - "fullEnd": 434, - "fullStart": 431 - } - }, - "fullEnd": 434, - "fullStart": 425 - } - }, - "fullEnd": 434, - "fullStart": 412 - } - }, - "fullEnd": 434, - "fullStart": 412 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L19:C7, L19:C8]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L19:C4, L19:C7]", - "snippet": "Ref" - }, - "leadingTrivia": " ", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 434, - "fullStart": 403 - } - ] - }, - "fullEnd": 436, - "fullStart": 44 - }, - "name": { - "context": { - "id": "node@@posts@[L4:C6, L4:C11]", - "snippet": "posts" - }, - "children": { - "expression": { - "context": { - "id": "node@@posts@[L4:C6, L4:C11]", - "snippet": "posts" - }, - "children": { - "variable": { - "context": { - "id": "token@@posts@[L4:C6, L4:C11]", - "snippet": "posts" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "posts" - } - }, - "fullEnd": 44, - "fullStart": 38 - } - }, - "fullEnd": 44, - "fullStart": 38 - }, - "type": { - "context": { - "id": "token@@Table@[L4:C0, L4:C5]", - "snippet": "Table" - }, - "leadingTrivia": "\n", - "trailingTrivia": " ", - "value": "Table" - } - }, - "fullEnd": 436, - "fullStart": 31 - }, - { - "context": { - "id": "node@@@[L22:C0, L22:C29]", - "snippet": "Ref: posts...< users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L22:C5, L22:C29]", - "snippet": "posts.user...< users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L22:C5, L22:C29]", - "snippet": "posts.user...< users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@@[L22:C5, L22:C18]", - "snippet": "posts.user_id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@posts@[L22:C5, L22:C10]", - "snippet": "posts" - }, - "children": { - "expression": { - "context": { - "id": "node@@posts@[L22:C5, L22:C10]", - "snippet": "posts" - }, - "children": { - "variable": { - "context": { - "id": "token@@posts@[L22:C5, L22:C10]", - "snippet": "posts" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "posts" - } - }, - "fullEnd": 447, - "fullStart": 442 - } - }, - "fullEnd": 447, - "fullStart": 442 - }, - "op": { - "context": { - "id": "token@@.@[L22:C10, L22:C11]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@user_id@[L22:C11, L22:C18]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L22:C11, L22:C18]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L22:C11, L22:C18]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 456, - "fullStart": 448 - } - }, - "fullEnd": 456, - "fullStart": 448 - } - }, - "fullEnd": 456, - "fullStart": 442 - }, - "op": { - "context": { - "id": "token@@<@[L22:C19, L22:C20]", - "snippet": "<" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "<" - }, - "rightExpression": { - "context": { - "id": "node@@@[L22:C21, L22:C29]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L22:C21, L22:C26]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L22:C21, L22:C26]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L22:C21, L22:C26]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 463, - "fullStart": 458 - } - }, - "fullEnd": 463, - "fullStart": 458 - }, - "op": { - "context": { - "id": "token@@.@[L22:C26, L22:C27]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L22:C27, L22:C29]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L22:C27, L22:C29]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L22:C27, L22:C29]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 467, - "fullStart": 464 - } - }, - "fullEnd": 467, - "fullStart": 464 - } - }, - "fullEnd": 467, - "fullStart": 458 - } - }, - "fullEnd": 467, - "fullStart": 442 - } - }, - "fullEnd": 467, - "fullStart": 442 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L22:C3, L22:C4]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L22:C0, L22:C3]", - "snippet": "Ref" - }, - "leadingTrivia": "\n", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 467, - "fullStart": 436 - }, - { - "context": { - "id": "node@@@[L23:C0, L23:C30]", - "snippet": "Ref: posts...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L23:C5, L23:C30]", - "snippet": "posts.user...? users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L23:C5, L23:C30]", - "snippet": "posts.user...? users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@@[L23:C5, L23:C18]", - "snippet": "posts.user_id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@posts@[L23:C5, L23:C10]", - "snippet": "posts" - }, - "children": { - "expression": { - "context": { - "id": "node@@posts@[L23:C5, L23:C10]", - "snippet": "posts" - }, - "children": { - "variable": { - "context": { - "id": "token@@posts@[L23:C5, L23:C10]", - "snippet": "posts" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "posts" - } - }, - "fullEnd": 477, - "fullStart": 472 - } - }, - "fullEnd": 477, - "fullStart": 472 - }, - "op": { - "context": { - "id": "token@@.@[L23:C10, L23:C11]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@user_id@[L23:C11, L23:C18]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L23:C11, L23:C18]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L23:C11, L23:C18]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 486, - "fullStart": 478 - } - }, - "fullEnd": 486, - "fullStart": 478 - } - }, - "fullEnd": 486, - "fullStart": 472 - }, - "op": { - "context": { - "id": "token@@@@[L23:C22, L23:C30]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L23:C22, L23:C27]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L23:C22, L23:C27]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L23:C22, L23:C27]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 494, - "fullStart": 489 - } - }, - "fullEnd": 494, - "fullStart": 489 - }, - "op": { - "context": { - "id": "token@@.@[L23:C27, L23:C28]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L23:C28, L23:C30]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L23:C28, L23:C30]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L23:C28, L23:C30]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 498, - "fullStart": 495 - } - }, - "fullEnd": 498, - "fullStart": 495 - } - }, - "fullEnd": 498, - "fullStart": 489 - } - }, - "fullEnd": 498, - "fullStart": 472 - } - }, - "fullEnd": 498, - "fullStart": 472 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L23:C3, L23:C4]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L23:C0, L23:C3]", - "snippet": "Ref" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 498, - "fullStart": 467 - }, - { - "context": { - "id": "node@@@[L24:C0, L24:C30]", - "snippet": "Ref: posts...< users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L24:C5, L24:C30]", - "snippet": "posts.user...< users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L24:C5, L24:C30]", - "snippet": "posts.user...< users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@@[L24:C5, L24:C18]", - "snippet": "posts.user_id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@posts@[L24:C5, L24:C10]", - "snippet": "posts" - }, - "children": { - "expression": { - "context": { - "id": "node@@posts@[L24:C5, L24:C10]", - "snippet": "posts" - }, - "children": { - "variable": { - "context": { - "id": "token@@posts@[L24:C5, L24:C10]", - "snippet": "posts" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "posts" - } - }, - "fullEnd": 508, - "fullStart": 503 - } - }, - "fullEnd": 508, - "fullStart": 503 - }, - "op": { - "context": { - "id": "token@@.@[L24:C10, L24:C11]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@user_id@[L24:C11, L24:C18]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L24:C11, L24:C18]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L24:C11, L24:C18]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 517, - "fullStart": 509 - } - }, - "fullEnd": 517, - "fullStart": 509 - } - }, - "fullEnd": 517, - "fullStart": 503 - }, - "op": { - "context": { - "id": "token@@?<@[L24:C19, L24:C21]", - "snippet": "?<" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "?<" - }, - "rightExpression": { - "context": { - "id": "node@@@[L24:C22, L24:C30]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L24:C22, L24:C27]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L24:C22, L24:C27]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L24:C22, L24:C27]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 525, - "fullStart": 520 - } - }, - "fullEnd": 525, - "fullStart": 520 - }, - "op": { - "context": { - "id": "token@@.@[L24:C27, L24:C28]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L24:C28, L24:C30]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L24:C28, L24:C30]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L24:C28, L24:C30]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 529, - "fullStart": 526 - } - }, - "fullEnd": 529, - "fullStart": 526 - } - }, - "fullEnd": 529, - "fullStart": 520 - } - }, - "fullEnd": 529, - "fullStart": 503 - } - }, - "fullEnd": 529, - "fullStart": 503 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L24:C3, L24:C4]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L24:C0, L24:C3]", - "snippet": "Ref" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 529, - "fullStart": 498 - }, - { - "context": { - "id": "node@@@[L25:C0, L25:C31]", - "snippet": "Ref: posts...? users.id" - }, - "children": { - "body": { - "context": { - "id": "node@@@[L25:C5, L25:C31]", - "snippet": "posts.user...? users.id" - }, - "children": { - "callee": { - "context": { - "id": "node@@@[L25:C5, L25:C31]", - "snippet": "posts.user...? users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@@[L25:C5, L25:C18]", - "snippet": "posts.user_id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@posts@[L25:C5, L25:C10]", - "snippet": "posts" - }, - "children": { - "expression": { - "context": { - "id": "node@@posts@[L25:C5, L25:C10]", - "snippet": "posts" - }, - "children": { - "variable": { - "context": { - "id": "token@@posts@[L25:C5, L25:C10]", - "snippet": "posts" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "posts" - } - }, - "fullEnd": 539, - "fullStart": 534 - } - }, - "fullEnd": 539, - "fullStart": 534 - }, - "op": { - "context": { - "id": "token@@.@[L25:C10, L25:C11]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@user_id@[L25:C11, L25:C18]", - "snippet": "user_id" - }, - "children": { - "expression": { - "context": { - "id": "node@@user_id@[L25:C11, L25:C18]", - "snippet": "user_id" - }, - "children": { - "variable": { - "context": { - "id": "token@@user_id@[L25:C11, L25:C18]", - "snippet": "user_id" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": "user_id" - } - }, - "fullEnd": 548, - "fullStart": 540 - } - }, - "fullEnd": 548, - "fullStart": 540 - } - }, - "fullEnd": 548, - "fullStart": 534 - }, - "op": { - "context": { - "id": "token@@?@@[L25:C23, L25:C31]", - "snippet": "users.id" - }, - "children": { - "leftExpression": { - "context": { - "id": "node@@users@[L25:C23, L25:C28]", - "snippet": "users" - }, - "children": { - "expression": { - "context": { - "id": "node@@users@[L25:C23, L25:C28]", - "snippet": "users" - }, - "children": { - "variable": { - "context": { - "id": "token@@users@[L25:C23, L25:C28]", - "snippet": "users" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "users" - } - }, - "fullEnd": 557, - "fullStart": 552 - } - }, - "fullEnd": 557, - "fullStart": 552 - }, - "op": { - "context": { - "id": "token@@.@[L25:C28, L25:C29]", - "snippet": "." - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "." - }, - "rightExpression": { - "context": { - "id": "node@@id@[L25:C29, L25:C31]", - "snippet": "id" - }, - "children": { - "expression": { - "context": { - "id": "node@@id@[L25:C29, L25:C31]", - "snippet": "id" - }, - "children": { - "variable": { - "context": { - "id": "token@@id@[L25:C29, L25:C31]", - "snippet": "id" - }, - "leadingTrivia": "", - "trailingTrivia": "\n", - "value": "id" - } - }, - "fullEnd": 561, - "fullStart": 558 - } - }, - "fullEnd": 561, - "fullStart": 558 - } - }, - "fullEnd": 561, - "fullStart": 552 - } - }, - "fullEnd": 561, - "fullStart": 534 - } - }, - "fullEnd": 561, - "fullStart": 534 - }, - "bodyColon": { - "context": { - "id": "token@@:@[L25:C3, L25:C4]", - "snippet": ":" - }, - "leadingTrivia": "", - "trailingTrivia": " ", - "value": ":" - }, - "type": { - "context": { - "id": "token@@Ref@[L25:C0, L25:C3]", - "snippet": "Ref" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "Ref" - } - }, - "fullEnd": 561, - "fullStart": 529 - } - ], - "eof": { - "context": { - "id": "token@@@[L26:C0, L26:C0]", - "snippet": "" - }, - "leadingTrivia": "", - "trailingTrivia": "", - "value": "" - } - }, - "fullEnd": 561, - "fullStart": 0 - } -} \ No newline at end of file From cb1fca11704b28bd8c8587141fd3c8e920db81a4 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 12:19:44 +0700 Subject: [PATCH 18/35] feat: add exporter tests for optional refs --- .../examples/exporter/exporter.spec.ts | 52 +++++++++++++++++++ packages/dbml-parse/src/index.ts | 4 ++ 2 files changed, 56 insertions(+) diff --git a/packages/dbml-core/__tests__/examples/exporter/exporter.spec.ts b/packages/dbml-core/__tests__/examples/exporter/exporter.spec.ts index f2b5d5094..2135b43d0 100644 --- a/packages/dbml-core/__tests__/examples/exporter/exporter.spec.ts +++ b/packages/dbml-core/__tests__/examples/exporter/exporter.spec.ts @@ -91,6 +91,58 @@ describe('@dbml/core - ref inactive setting', () => { }); }); +describe('@dbml/core - optional ref operators', () => { + const OPTIONAL_REF_OPS = [ + '-', '-?', '?-', '?-?', + '>', '>?', '?>', '?>?', + '<', '', '<>?', '?<>', '?<>?', + ]; + + const EXPECTED_OP: Record = { + '-': '-', '-?': '-?', '?-': '?-', '?-?': '?-?', + '>': '<', '>?': '?<', '?>': '?': '?': '<>', '<>?': '<>?', '?<>': '?<>', '?<>?': '?<>?', + }; + + describe('dbml exporter', () => { + test.each(OPTIONAL_REF_OPS)('should export ref with operator %s', (op) => { + const input = ` + Table users { id integer [pk] } + Table posts { user_id integer } + Ref: posts.user_id ${op} users.id + `.trim(); + const res = exporter.export(input, 'dbml'); + expect(res).toContain(EXPECTED_OP[op]); + }); + }); + + describe('sql exporters', () => { + const sqlFormats: ExportFormat[] = ['mysql', 'postgres', 'mssql']; + + test.each(sqlFormats)('%s exporter should handle optional ref without error', (format) => { + const input = ` + Table users { id integer [pk] } + Table posts { user_id integer } + Ref: posts.user_id >? users.id + `.trim(); + expect(() => exporter.export(input, format)).not.toThrow(); + }); + + test.each(sqlFormats)('%s exporter should produce FK constraint for optional ref', (format) => { + const input = ` + Table users { id integer [pk] } + Table posts { user_id integer } + Ref: posts.user_id >? users.id + `.trim(); + const res = exporter.export(input, format); + expect(res).toContain('FOREIGN KEY'); + expect(res).toContain('REFERENCES'); + }); + }); +}); + describe('@dbml/core - exporter flags', () => { describe('includeRecords', () => { test('includes records by default', () => { diff --git a/packages/dbml-parse/src/index.ts b/packages/dbml-parse/src/index.ts index 1f1bbc18b..1afc38676 100644 --- a/packages/dbml-parse/src/index.ts +++ b/packages/dbml-parse/src/index.ts @@ -92,6 +92,10 @@ export { getRelationshipOp, getMultiplicities, parseCardinality, + CARDINALITY_ONE, + CARDINALITY_MAYBE, + CARDINALITY_SOME, + CARDINALITY_MANY, } from '@/core/types/relation'; // DiagramView types From ecc449034842abfffa14fe16902efeba2c6f2aed Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 12:35:57 +0700 Subject: [PATCH 19/35] feat: adjust cardinality in endpoint --- packages/dbml-core/src/model_structure/endpoint.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/dbml-core/src/model_structure/endpoint.js b/packages/dbml-core/src/model_structure/endpoint.js index 7c73b2f3a..915544f5d 100644 --- a/packages/dbml-core/src/model_structure/endpoint.js +++ b/packages/dbml-core/src/model_structure/endpoint.js @@ -1,3 +1,4 @@ +import { CARDINALITY_SOME, CARDINALITY_MANY, CARDINALITY_ONE, CARDINALITY_MAYBE } from '@dbml/parse'; import { DEFAULT_SCHEMA_NAME } from './config'; import Element from './element'; import { shouldPrintSchema, shouldPrintSchemaName } from './utils'; @@ -37,6 +38,12 @@ class Endpoint extends Element { : ''}"${tableName}"`); } this.setFields(fieldNames, table); + + // Adjust cardinality based on field nullability + if (this.fields.length > 0 && this.fields.every((f) => !f.not_null && !f.pk)) { + if (this.relation === CARDINALITY_SOME) this.relation = CARDINALITY_MANY; + else if (this.relation === CARDINALITY_ONE) this.relation = CARDINALITY_MAYBE; + } } generateId () { From fe269e00612041bb90f186776a2edd6f9363bf89 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 15:27:21 +0700 Subject: [PATCH 20/35] test: add tests for optional ref importer to DBML --- .../examples/importer/importer.spec.ts | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/packages/dbml-core/__tests__/examples/importer/importer.spec.ts b/packages/dbml-core/__tests__/examples/importer/importer.spec.ts index cf58ebe9c..2b5fab5fe 100644 --- a/packages/dbml-core/__tests__/examples/importer/importer.spec.ts +++ b/packages/dbml-core/__tests__/examples/importer/importer.spec.ts @@ -5,6 +5,62 @@ import { readFileSync } from 'fs'; import path from 'path'; import { test, expect, describe } from 'vitest'; +describe('@dbml/core - importer optional refs', () => { + const POSTGRES_NULLABLE_FK = ` + CREATE TABLE users (id int PRIMARY KEY); + CREATE TABLE posts ( + id int PRIMARY KEY, + user_id int REFERENCES users(id) + ); + `; + + const POSTGRES_NOT_NULL_FK = ` + CREATE TABLE users (id int PRIMARY KEY); + CREATE TABLE posts ( + id int PRIMARY KEY, + user_id int NOT NULL REFERENCES users(id) + ); + `; + + const MYSQL_NULLABLE_FK = ` + CREATE TABLE users (id int PRIMARY KEY); + CREATE TABLE posts ( + id int PRIMARY KEY, + user_id int, + FOREIGN KEY (user_id) REFERENCES users(id) + ); + `; + + const MYSQL_NOT_NULL_FK = ` + CREATE TABLE users (id int PRIMARY KEY); + CREATE TABLE posts ( + id int PRIMARY KEY, + user_id int NOT NULL, + FOREIGN KEY (user_id) REFERENCES users(id) + ); + `; + + test('postgres: nullable FK column produces optional ref', () => { + const res = importer.import(POSTGRES_NULLABLE_FK, 'postgres'); + expect(res).toContain('Ref:"users"."id" { + const res = importer.import(POSTGRES_NOT_NULL_FK, 'postgres'); + expect(res).toContain('Ref:"users"."id" < "posts"."user_id"'); + }); + + test('mysql: nullable FK column produces optional ref', () => { + const res = importer.import(MYSQL_NULLABLE_FK, 'mysql'); + expect(res).toContain('Ref:"users"."id" { + const res = importer.import(MYSQL_NOT_NULL_FK, 'mysql'); + expect(res).toContain('Ref:"users"."id" < "posts"."user_id"'); + }); +}); + describe('@dbml/core - importer', () => { const runTest = async (fileName: string, testDir: string, format: ParseFormat) => { const fileExtension = getFileExtension(format); From 5e3778ba6d084c12ee692bba1f2111afc8ac5d78 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 16:06:55 +0700 Subject: [PATCH 21/35] fix: adjust cardinality in normalized model --- .../output/general_schema.out.dbml | 10 +-- .../output/multiple_schema.out.dbml | 6 +- .../output/general_schema.out.dbml | 10 +-- .../output/multiple_schema.out.dbml | 10 +-- .../output/shorthand_syntax.out.dbml | 2 +- .../mysql_importer/output/ddl_create.out.dbml | 2 +- .../output/general_schema.out.dbml | 10 +-- .../mysql_importer/output/lower_case.out.dbml | 10 +-- .../output/multiple_schema.out.dbml | 14 ++-- .../output/alter_table_add_fk.out.dbml | 24 +++--- .../output/alter_table_add_pk.out.dbml | 2 +- .../output/alter_table_add_unique.out.dbml | 2 +- .../alter_table_check_constraints.out.dbml | 2 +- .../output/column_def_fk.out.dbml | 82 +++++++++---------- .../output/create_table.out.dbml | 2 +- .../output/in_table_fk.out.dbml | 24 +++--- .../output/quoted_names.out.dbml | 2 +- .../output/cmd_create_table.out.dbml | 4 +- .../postgres_importer/output/db_dump.out.dbml | 32 ++++---- .../output/general_schema.out.dbml | 12 +-- .../output/multiple_schema.out.dbml | 8 +- .../output/shorthand_syntax.out.dbml | 2 +- .../snowflake_importer/output/alter.out.dbml | 2 +- .../output/create_table.out.dbml | 26 +++--- .../output/column_settings.out.dbml | 2 +- .../output/general_schema.out.dbml | 10 +-- .../output/referential_actions.out.dbml | 4 +- .../output/sticky_notes.out.dbml | 6 +- .../output/table_group_settings.out.dbml | 14 ++-- .../output/table_settings.out.dbml | 4 +- packages/dbml-core/src/model_structure/ref.js | 12 ++- 31 files changed, 181 insertions(+), 171 deletions(-) diff --git a/packages/dbml-core/__tests__/examples/importer/json_importer/output/general_schema.out.dbml b/packages/dbml-core/__tests__/examples/importer/json_importer/output/general_schema.out.dbml index 4ba4e7f91..35d6d09fb 100644 --- a/packages/dbml-core/__tests__/examples/importer/json_importer/output/general_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/importer/json_importer/output/general_schema.out.dbml @@ -61,14 +61,14 @@ Table "countries" { "continent_name" varchar } -Ref:"orders"."id" < "order_items"."order_id" +Ref:"orders"."id" "ecommerce"."products"."id" diff --git a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/general_schema.out.dbml b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/general_schema.out.dbml index 4ba4e7f91..35d6d09fb 100644 --- a/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/general_schema.out.dbml +++ b/packages/dbml-core/__tests__/examples/model_exporter/dbml_exporter/output/general_schema.out.dbml @@ -61,14 +61,14 @@ Table "countries" { "continent_name" varchar } -Ref:"orders"."id" < "order_items"."order_id" +Ref:"orders"."id" "ecommerce"."products"."id" -Ref:"ecommerce"."merchants".("id", "country_code") < "ecommerce"."merchant_periods".("merchant_id", "country_code") +Ref:"ecommerce"."merchants".("id", "country_code") ? 0 + && ep.fields.every((f) => !f.not_null && !f.pk); + if (allNullable) { + if (ep.relation === CARDINALITY_SOME) ep.relation = CARDINALITY_MANY; + else if (ep.relation === CARDINALITY_ONE) ep.relation = CARDINALITY_MAYBE; + } + } } /** From 90ae93af00769cf9b4faba4712b23065ee7ee7f0 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 16:11:46 +0700 Subject: [PATCH 22/35] test: add tests for optional ref importer to DBML --- .../examples/importer/importer.spec.ts | 90 +++++++++---------- 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/packages/dbml-core/__tests__/examples/importer/importer.spec.ts b/packages/dbml-core/__tests__/examples/importer/importer.spec.ts index 2b5fab5fe..937cc6461 100644 --- a/packages/dbml-core/__tests__/examples/importer/importer.spec.ts +++ b/packages/dbml-core/__tests__/examples/importer/importer.spec.ts @@ -6,58 +6,56 @@ import path from 'path'; import { test, expect, describe } from 'vitest'; describe('@dbml/core - importer optional refs', () => { - const POSTGRES_NULLABLE_FK = ` - CREATE TABLE users (id int PRIMARY KEY); - CREATE TABLE posts ( - id int PRIMARY KEY, - user_id int REFERENCES users(id) - ); - `; - - const POSTGRES_NOT_NULL_FK = ` - CREATE TABLE users (id int PRIMARY KEY); - CREATE TABLE posts ( - id int PRIMARY KEY, - user_id int NOT NULL REFERENCES users(id) - ); - `; - - const MYSQL_NULLABLE_FK = ` - CREATE TABLE users (id int PRIMARY KEY); - CREATE TABLE posts ( - id int PRIMARY KEY, - user_id int, - FOREIGN KEY (user_id) REFERENCES users(id) - ); - `; + describe('postgres', () => { + test('NOT NULL FK to PK', () => { + const sql = ` + CREATE TABLE a (id int PRIMARY KEY); + CREATE TABLE b (id int PRIMARY KEY, a_id int NOT NULL REFERENCES a(id)); + `; + expect(importer.import(sql, 'postgres')).toContain('Ref:"a"."id" < "b"."a_id"'); + }); - const MYSQL_NOT_NULL_FK = ` - CREATE TABLE users (id int PRIMARY KEY); - CREATE TABLE posts ( - id int PRIMARY KEY, - user_id int NOT NULL, - FOREIGN KEY (user_id) REFERENCES users(id) - ); - `; + test('nullable FK to PK', () => { + const sql = ` + CREATE TABLE a (id int PRIMARY KEY); + CREATE TABLE b (id int PRIMARY KEY, a_id int REFERENCES a(id)); + `; + expect(importer.import(sql, 'postgres')).toContain('Ref:"a"."id" { - const res = importer.import(POSTGRES_NULLABLE_FK, 'postgres'); - expect(res).toContain('Ref:"users"."id" { + const sql = ` + CREATE TABLE a (id int PRIMARY KEY, code int UNIQUE NOT NULL); + CREATE TABLE b (id int PRIMARY KEY, a_code int NOT NULL REFERENCES a(code)); + `; + expect(importer.import(sql, 'postgres')).toContain('Ref:"a"."code" < "b"."a_code"'); + }); - test('postgres: NOT NULL FK column produces required ref', () => { - const res = importer.import(POSTGRES_NOT_NULL_FK, 'postgres'); - expect(res).toContain('Ref:"users"."id" < "posts"."user_id"'); + test('nullable FK to UNIQUE', () => { + const sql = ` + CREATE TABLE a (id int PRIMARY KEY, code int UNIQUE NOT NULL); + CREATE TABLE b (id int PRIMARY KEY, a_code int REFERENCES a(code)); + `; + expect(importer.import(sql, 'postgres')).toContain('Ref:"a"."code" { - const res = importer.import(MYSQL_NULLABLE_FK, 'mysql'); - expect(res).toContain('Ref:"users"."id" { + test('NOT NULL FK to PK', () => { + const sql = ` + CREATE TABLE a (id int PRIMARY KEY); + CREATE TABLE b (id int PRIMARY KEY, a_id int NOT NULL, FOREIGN KEY (a_id) REFERENCES a(id)); + `; + expect(importer.import(sql, 'mysql')).toContain('Ref:"a"."id" < "b"."a_id"'); + }); - test('mysql: NOT NULL FK column produces required ref', () => { - const res = importer.import(MYSQL_NOT_NULL_FK, 'mysql'); - expect(res).toContain('Ref:"users"."id" < "posts"."user_id"'); + test('nullable FK to PK', () => { + const sql = ` + CREATE TABLE a (id int PRIMARY KEY); + CREATE TABLE b (id int PRIMARY KEY, a_id int, FOREIGN KEY (a_id) REFERENCES a(id)); + `; + expect(importer.import(sql, 'mysql')).toContain('Ref:"a"."id" Date: Wed, 1 Jul 2026 16:54:12 +0700 Subject: [PATCH 23/35] test: fix failed tests --- .../output/general_schema.out.json | 194 ++++++++++++++---- .../output/referential_actions.out.json | 179 ++++++++++------ .../model_structure/single_schema.spec.ts | 12 +- .../normalized_model/schema_def.out.json | 148 ++++++++++--- .../normalized_model/table_partial.out.json | 8 +- .../dbml-core/src/model_structure/endpoint.js | 1 + 6 files changed, 401 insertions(+), 141 deletions(-) diff --git a/packages/dbml-core/__tests__/examples/model_exporter/json_exporter/output/general_schema.out.json b/packages/dbml-core/__tests__/examples/model_exporter/json_exporter/output/general_schema.out.json index 95ae0019f..8bb3deea8 100644 --- a/packages/dbml-core/__tests__/examples/model_exporter/json_exporter/output/general_schema.out.json +++ b/packages/dbml-core/__tests__/examples/model_exporter/json_exporter/output/general_schema.out.json @@ -7,6 +7,9 @@ { "name": "orders", "alias": null, + "note": null, + "partials": [], + "recordIds": [], "fields": [ { "name": "id", @@ -15,7 +18,10 @@ "args": null }, "pk": true, - "increment": true + "note": null, + "increment": true, + "injectedPartialId": null, + "checkIds": [] }, { "name": "user_id", @@ -24,21 +30,30 @@ "args": null }, "unique": true, - "not_null": true + "not_null": true, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "status", "type": { "type_name": "orders_status", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "created_at", "type": { "type_name": "varchar", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] } ], "indexes": [] @@ -46,20 +61,29 @@ { "name": "order_items", "alias": null, + "note": null, + "partials": [], + "recordIds": [], "fields": [ { "name": "order_id", "type": { "type_name": "int", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "product_id", "type": { "type_name": "int", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "quantity", @@ -67,10 +91,13 @@ "type_name": "int", "args": null }, + "note": null, "dbdefault": { "type": "number", "value": 1 - } + }, + "injectedPartialId": null, + "checkIds": [] } ], "indexes": [] @@ -78,6 +105,9 @@ { "name": "products", "alias": null, + "note": null, + "partials": [], + "recordIds": [], "fields": [ { "name": "id", @@ -85,14 +115,20 @@ "type_name": "int", "args": null }, - "pk": true + "pk": true, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "name", "type": { "type_name": "varchar", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "merchant_id", @@ -100,21 +136,30 @@ "type_name": "int", "args": null }, - "not_null": true + "not_null": true, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "price", "type": { "type_name": "int", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "status", "type": { "type_name": "product status", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "created_at", @@ -122,15 +167,19 @@ "type_name": "datetime", "args": null }, + "note": null, "dbdefault": { "value": "now()", "type": "expression" - } + }, + "injectedPartialId": null, + "checkIds": [] } ], "indexes": [ { "name": "product_status", + "note": null, "columns": [ { "type": "column", @@ -145,6 +194,7 @@ { "type": "hash", "unique": true, + "note": null, "columns": [ { "type": "column", @@ -157,6 +207,9 @@ { "name": "users", "alias": null, + "note": null, + "partials": [], + "recordIds": [], "fields": [ { "name": "id", @@ -164,14 +217,20 @@ "type_name": "int", "args": null }, - "pk": true + "pk": true, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "full_name", "type": { "type_name": "varchar", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "email", @@ -179,35 +238,50 @@ "type_name": "varchar", "args": null }, - "unique": true + "unique": true, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "gender", "type": { "type_name": "varchar", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "date_of_birth", "type": { "type_name": "varchar", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "created_at", "type": { "type_name": "varchar", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "country_code", "type": { "type_name": "int", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] } ], "indexes": [] @@ -215,6 +289,9 @@ { "name": "merchants", "alias": null, + "note": null, + "partials": [], + "recordIds": [], "fields": [ { "name": "id", @@ -222,35 +299,50 @@ "type_name": "int", "args": null }, - "pk": true + "pk": true, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "merchant_name", "type": { "type_name": "varchar", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "country_code", "type": { "type_name": "int", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "created_at", "type": { "type_name": "varchar", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "admin_id", "type": { "type_name": "int", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] } ], "indexes": [] @@ -258,6 +350,9 @@ { "name": "countries", "alias": null, + "note": null, + "partials": [], + "recordIds": [], "fields": [ { "name": "code", @@ -265,21 +360,30 @@ "type_name": "int", "args": null }, - "pk": true + "pk": true, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "name", "type": { "type_name": "varchar", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "continent_name", "type": { "type_name": "varchar", "args": null - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] } ], "indexes": [] @@ -288,29 +392,37 @@ "enums": [ { "name": "orders_status", + "note": null, "values": [ { - "name": "created" + "name": "created", + "note": null }, { - "name": "running" + "name": "running", + "note": null }, { - "name": "done" + "name": "done", + "note": null }, { - "name": "failure" + "name": "failure", + "note": null } ] }, { "name": "product status", + "note": null, "values": [ { - "name": "Out of Stock" + "name": "Out of Stock", + "note": null }, { - "name": "In Stock" + "name": "In Stock", + "note": null } ] } @@ -332,7 +444,7 @@ "fieldNames": [ "order_id" ], - "relation": "*" + "relation": "0..*" } ] }, @@ -351,7 +463,7 @@ "fieldNames": [ "product_id" ], - "relation": "*" + "relation": "0..*" } ] }, @@ -370,7 +482,7 @@ "fieldNames": [ "country_code" ], - "relation": "*" + "relation": "0..*" } ] }, @@ -389,7 +501,7 @@ "fieldNames": [ "country_code" ], - "relation": "*" + "relation": "0..*" } ] }, @@ -427,11 +539,13 @@ "fieldNames": [ "admin_id" ], - "relation": "*" + "relation": "0..*" } ] } ] } - ] + ], + "notes": [], + "records": [] } \ No newline at end of file diff --git a/packages/dbml-core/__tests__/examples/model_exporter/json_exporter/output/referential_actions.out.json b/packages/dbml-core/__tests__/examples/model_exporter/json_exporter/output/referential_actions.out.json index ac78daada..cea547cbf 100644 --- a/packages/dbml-core/__tests__/examples/model_exporter/json_exporter/output/referential_actions.out.json +++ b/packages/dbml-core/__tests__/examples/model_exporter/json_exporter/output/referential_actions.out.json @@ -6,122 +6,157 @@ "tables": [ { "name": "orders", - "alias": null, + "note": null, + "partials": [], + "recordIds": [], "fields": [ { "name": "id", "type": { - "type_name": "int", - "args": null + "type_name": "int" }, "pk": true, - "increment": true + "note": null, + "increment": true, + "injectedPartialId": null, + "checkIds": [] }, { "name": "user_id", "type": { - "type_name": "int", - "args": null + "type_name": "int" }, "unique": true, - "not_null": true + "not_null": true, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "status", "type": { - "type_name": "orders_status_enum", - "args": null - } + "type_name": "orders_status_enum" + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "created_at", "type": { "type_name": "varchar(255)", "args": "255" - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] } ], "indexes": [] }, { "name": "order_items", - "alias": null, + "note": null, + "partials": [], + "recordIds": [], "fields": [ { "name": "order_id", "type": { - "type_name": "int", - "args": null - } + "type_name": "int" + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "product_id", "type": { - "type_name": "int", - "args": null - } + "type_name": "int" + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "product_name", "type": { "type_name": "varchar(255)", "args": "255" - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "quantity", "type": { - "type_name": "int", - "args": null + "type_name": "int" }, + "note": null, "dbdefault": { "type": "number", "value": 1 - } + }, + "injectedPartialId": null, + "checkIds": [] } ], "indexes": [] }, { "name": "products", - "alias": null, + "note": null, + "partials": [], + "recordIds": [], "fields": [ { "name": "id", "type": { - "type_name": "int", - "args": null - } + "type_name": "int" + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "name", "type": { "type_name": "varchar(255)", "args": "255" - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "price", "type": { "type_name": "decimal(10,4)", "args": "10,4" - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "created_at", "type": { - "type_name": "datetime", - "args": null + "type_name": "datetime" }, + "note": null, "dbdefault": { "value": "now()", "type": "expression" - } + }, + "injectedPartialId": null, + "checkIds": [] } ], "indexes": [ { "pk": true, + "note": null, "columns": [ { "type": "column", @@ -137,23 +172,30 @@ }, { "name": "users", - "alias": null, + "note": null, + "partials": [], + "recordIds": [], "fields": [ { "name": "id", "type": { - "type_name": "int", - "args": null + "type_name": "int" }, "pk": true, - "increment": true + "note": null, + "increment": true, + "injectedPartialId": null, + "checkIds": [] }, { "name": "name", "type": { "type_name": "varchar(255)", "args": "255" - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "email", @@ -161,62 +203,81 @@ "type_name": "varchar(255)", "args": "255" }, - "unique": true + "unique": true, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "date_of_birth", "type": { - "type_name": "datetime", - "args": null - } + "type_name": "datetime" + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "created_at", "type": { - "type_name": "datetime", - "args": null + "type_name": "datetime" }, + "note": null, "dbdefault": { "value": "now()", "type": "expression" - } + }, + "injectedPartialId": null, + "checkIds": [] }, { "name": "country_code", "type": { - "type_name": "int", - "args": null + "type_name": "int" }, - "not_null": true + "not_null": true, + "note": null, + "injectedPartialId": null, + "checkIds": [] } ], "indexes": [] }, { "name": "countries", - "alias": null, + "note": null, + "partials": [], + "recordIds": [], "fields": [ { "name": "code", "type": { - "type_name": "int", - "args": null + "type_name": "int" }, - "pk": true + "pk": true, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "name", "type": { "type_name": "varchar(255)", "args": "255" - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] }, { "name": "continent_name", "type": { "type_name": "varchar(255)", "args": "255" - } + }, + "note": null, + "injectedPartialId": null, + "checkIds": [] } ], "indexes": [] @@ -226,7 +287,6 @@ "tableGroups": [], "refs": [ { - "name": null, "onDelete": "restrict", "endpoints": [ { @@ -246,7 +306,6 @@ ] }, { - "name": null, "onDelete": "cascade", "endpoints": [ { @@ -261,12 +320,11 @@ "fieldNames": [ "order_id" ], - "relation": "*" + "relation": "0..*" } ] }, { - "name": null, "onDelete": "set null", "endpoints": [ { @@ -275,7 +333,7 @@ "id", "name" ], - "relation": "1" + "relation": "0..1" }, { "tableName": "order_items", @@ -283,12 +341,11 @@ "product_id", "product_name" ], - "relation": "*" + "relation": "0..*" } ] }, { - "name": null, "onDelete": "no action", "endpoints": [ { @@ -309,5 +366,7 @@ } ] } - ] + ], + "notes": [], + "records": [] } \ No newline at end of file diff --git a/packages/dbml-core/__tests__/examples/model_structure/single_schema.spec.ts b/packages/dbml-core/__tests__/examples/model_structure/single_schema.spec.ts index 6f1e4af1e..9c0f5335b 100644 --- a/packages/dbml-core/__tests__/examples/model_structure/single_schema.spec.ts +++ b/packages/dbml-core/__tests__/examples/model_structure/single_schema.spec.ts @@ -106,7 +106,7 @@ describe('@dbml/core - model_structure', () => { schemaName: DEFAULT_SCHEMA_NAME, tableName: 'users', fieldNames: ['country_code'], - relation: '*', + relation: '0..*', }, { schemaName: DEFAULT_SCHEMA_NAME, @@ -125,7 +125,7 @@ describe('@dbml/core - model_structure', () => { 'id', 'name', ], - relation: '1', + relation: '0..1', }, { schemaName: DEFAULT_SCHEMA_NAME, @@ -134,7 +134,7 @@ describe('@dbml/core - model_structure', () => { 'product_id', 'product_name', ], - relation: '*', + relation: '0..*', }, ]), }), @@ -473,7 +473,7 @@ describe('@dbml/core - model_structure', () => { schemaName: DEFAULT_SCHEMA_NAME, tableName: 'users', fieldNames: ['country_code'], - relation: '*', + relation: '0..*', }, { schemaName: DEFAULT_SCHEMA_NAME, @@ -492,7 +492,7 @@ describe('@dbml/core - model_structure', () => { 'id', 'name', ], - relation: '1', + relation: '0..1', }, { schemaName: DEFAULT_SCHEMA_NAME, @@ -501,7 +501,7 @@ describe('@dbml/core - model_structure', () => { 'product_id', 'product_name', ], - relation: '*', + relation: '0..*', }, ]), }), diff --git a/packages/dbml-core/__tests__/examples/normalized_model/schema_def.out.json b/packages/dbml-core/__tests__/examples/normalized_model/schema_def.out.json index 202c59354..7ac865c5f 100644 --- a/packages/dbml-core/__tests__/examples/normalized_model/schema_def.out.json +++ b/packages/dbml-core/__tests__/examples/normalized_model/schema_def.out.json @@ -99,7 +99,6 @@ "refs": { "1": { "id": 1, - "name": null, "endpointIds": [ 1, 2 @@ -108,7 +107,6 @@ }, "2": { "id": 2, - "name": null, "endpointIds": [ 3, 4 @@ -223,6 +221,7 @@ "alias": "EU", "note": null, "partials": [], + "recordIds": [], "fieldIds": [ 1, 2, @@ -232,6 +231,7 @@ 6 ], "indexIds": [], + "checkIds": [], "schemaId": 3, "groupId": 1 }, @@ -241,6 +241,7 @@ "alias": null, "note": null, "partials": [], + "recordIds": [], "fieldIds": [ 7, 8, @@ -250,6 +251,7 @@ 12 ], "indexIds": [], + "checkIds": [], "schemaId": 1, "groupId": 1 }, @@ -259,11 +261,13 @@ "alias": null, "note": null, "partials": [], + "recordIds": [], "fieldIds": [ 13, 14 ], "indexIds": [], + "checkIds": [], "schemaId": 1, "groupId": 1 }, @@ -273,11 +277,13 @@ "alias": "A", "note": null, "partials": [], + "recordIds": [], "fieldIds": [ 15, 16 ], "indexIds": [], + "checkIds": [], "schemaId": 4, "groupId": 1 }, @@ -287,11 +293,13 @@ "alias": null, "note": null, "partials": [], + "recordIds": [], "fieldIds": [ 17, 18 ], "indexIds": [], + "checkIds": [], "schemaId": 4, "groupId": null }, @@ -302,11 +310,13 @@ "note": null, "headerColor": "#aaaaaa", "partials": [], + "recordIds": [], "fieldIds": [ 19, 20 ], "indexIds": [], + "checkIds": [], "schemaId": 1, "groupId": null }, @@ -316,11 +326,13 @@ "alias": null, "note": null, "partials": [], + "recordIds": [], "fieldIds": [ 21, 22 ], "indexIds": [], + "checkIds": [], "schemaId": 1, "groupId": null }, @@ -330,12 +342,14 @@ "alias": null, "note": null, "partials": [], + "recordIds": [], "fieldIds": [ 23, 24, 25 ], "indexIds": [], + "checkIds": [], "schemaId": 1, "groupId": null }, @@ -357,18 +371,23 @@ "offset": 1539, "line": 108, "column": 22 + }, + "filepath": { + "path": "/main.dbml" } }, "name": "P_unary_expression", "id": 1 } ], + "recordIds": [], "fieldIds": [ 26, 27, 28 ], "indexIds": [], + "checkIds": [], "schemaId": 1, "groupId": null } @@ -394,7 +413,7 @@ "fieldNames": [ "name" ], - "relation": "*", + "relation": "0..*", "refId": 1, "fieldIds": [ 16 @@ -420,7 +439,7 @@ "fieldNames": [ "name" ], - "relation": "*", + "relation": "0..*", "refId": 2, "fieldIds": [ 18 @@ -459,7 +478,7 @@ "fieldNames": [ "name" ], - "relation": "1", + "relation": "0..1", "refId": 4, "fieldIds": [ 8 @@ -485,7 +504,7 @@ "fieldNames": [ "id" ], - "relation": "1", + "relation": "0..1", "refId": 5, "fieldIds": [ 19 @@ -498,7 +517,7 @@ "fieldNames": [ "id" ], - "relation": "*", + "relation": "0..*", "refId": 5, "fieldIds": [ 21 @@ -511,7 +530,7 @@ "fieldNames": [ "id" ], - "relation": "1", + "relation": "0..1", "refId": 6, "fieldIds": [ 21 @@ -524,7 +543,7 @@ "fieldNames": [ "c_id" ], - "relation": "*", + "relation": "0..*", "refId": 6, "fieldIds": [ 20 @@ -583,6 +602,7 @@ }, "indexes": {}, "indexColumns": {}, + "checks": {}, "fields": { "1": { "id": 1, @@ -595,6 +615,8 @@ "unique": false, "pk": true, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [ 1, 6, @@ -614,6 +636,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 1, "enumId": null @@ -629,6 +653,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 1, "enumId": 1 @@ -644,6 +670,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 1, "enumId": 1 @@ -659,6 +687,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 1, "enumId": 2 @@ -674,6 +704,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 1, "enumId": 3 @@ -689,6 +721,8 @@ "unique": false, "pk": true, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [ 3, 5 @@ -707,6 +741,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [ 7 ], @@ -724,6 +760,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 2, "enumId": 1 @@ -739,6 +777,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 2, "enumId": 1 @@ -754,6 +794,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 2, "enumId": 2 @@ -769,6 +811,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 2, "enumId": 3 @@ -784,6 +828,8 @@ "unique": false, "pk": true, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 3, "enumId": null @@ -799,6 +845,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 3, "enumId": null @@ -814,6 +862,8 @@ "unique": false, "pk": true, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 4, "enumId": null @@ -829,6 +879,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [ 2 ], @@ -846,6 +898,8 @@ "unique": false, "pk": true, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 5, "enumId": null @@ -861,6 +915,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [ 4 ], @@ -878,6 +934,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [ 9 ], @@ -895,6 +953,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [ 12 ], @@ -912,6 +972,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [ 10, 11 @@ -930,6 +992,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 7, "enumId": null @@ -949,6 +1013,8 @@ "type": "number", "value": -2 }, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 8, "enumId": null @@ -968,6 +1034,8 @@ "type": "number", "value": -2 }, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 8, "enumId": null @@ -987,6 +1055,8 @@ "type": "number", "value": 7.2225 }, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 8, "enumId": null @@ -1002,6 +1072,8 @@ "unique": false, "pk": false, "note": null, + "injectedPartialId": null, + "checkIds": [], "endpointIds": [], "tableId": 9, "enumId": null @@ -1020,6 +1092,7 @@ "value": -2 }, "injectedPartialId": 1, + "checkIds": [], "endpointIds": [], "tableId": 9, "enumId": null @@ -1038,6 +1111,7 @@ "value": -7.2225 }, "injectedPartialId": 1, + "checkIds": [], "endpointIds": [], "tableId": 9, "enumId": null @@ -1052,11 +1126,6 @@ "fields": [ { "name": "id", - "type": { - "schemaName": null, - "type_name": "int(-1)", - "args": "-1" - }, "token": { "start": { "offset": 1356, @@ -1067,21 +1136,25 @@ "offset": 1380, "line": 101, "column": 27 + }, + "filepath": { + "path": "/main.dbml" } }, - "inline_refs": [], + "type": { + "schemaName": null, + "type_name": "int(-1)", + "args": "-1" + }, "dbdefault": { "type": "number", "value": -2 - } + }, + "inline_refs": [], + "checks": [] }, { "name": "id2", - "type": { - "schemaName": null, - "type_name": "int(--1)", - "args": "--1" - }, "token": { "start": { "offset": 1383, @@ -1092,21 +1165,25 @@ "offset": 1410, "line": 102, "column": 30 + }, + "filepath": { + "path": "/main.dbml" } }, - "inline_refs": [], + "type": { + "schemaName": null, + "type_name": "int(--1)", + "args": "--1" + }, "dbdefault": { "type": "number", "value": -2 - } + }, + "inline_refs": [], + "checks": [] }, { "name": "id3", - "type": { - "schemaName": null, - "type_name": "int(+-+---+0.1)", - "args": "+-+---+0.1" - }, "token": { "start": { "offset": 1413, @@ -1117,16 +1194,25 @@ "offset": 1459, "line": 103, "column": 49 + }, + "filepath": { + "path": "/main.dbml" } }, - "inline_refs": [], + "type": { + "schemaName": null, + "type_name": "int(+-+---+0.1)", + "args": "+-+---+0.1" + }, "dbdefault": { "type": "number", "value": -7.2225 - } + }, + "inline_refs": [], + "checks": [] } ], "indexes": [] } } -} +} \ No newline at end of file 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 ff045a25f..aecca6674 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 @@ -333,7 +333,7 @@ "fieldNames": [ "id2" ], - "relation": "1", + "relation": "0..1", "refId": 1, "fieldIds": [ 2 @@ -346,7 +346,7 @@ "fieldNames": [ "refColumn1" ], - "relation": "*", + "relation": "0..*", "refId": 1, "fieldIds": [ 6 @@ -359,7 +359,7 @@ "fieldNames": [ "refColumn2" ], - "relation": "*", + "relation": "0..*", "refId": 2, "fieldIds": [ 7 @@ -372,7 +372,7 @@ "fieldNames": [ "id" ], - "relation": "1", + "relation": "0..1", "refId": 2, "fieldIds": [ 3 diff --git a/packages/dbml-core/src/model_structure/endpoint.js b/packages/dbml-core/src/model_structure/endpoint.js index 915544f5d..6a849b957 100644 --- a/packages/dbml-core/src/model_structure/endpoint.js +++ b/packages/dbml-core/src/model_structure/endpoint.js @@ -40,6 +40,7 @@ class Endpoint extends Element { this.setFields(fieldNames, table); // Adjust cardinality based on field nullability + // Unspecified not_null means nullable (SQL default) if (this.fields.length > 0 && this.fields.every((f) => !f.not_null && !f.pk)) { if (this.relation === CARDINALITY_SOME) this.relation = CARDINALITY_MANY; else if (this.relation === CARDINALITY_ONE) this.relation = CARDINALITY_MAYBE; From 87323b9a4bd05f5706d30fd40aaaf402aebe3947 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 17:19:35 +0700 Subject: [PATCH 24/35] feat: add warnings for logical and physical ref mismatch --- .../examples/interpreter/record/fk.test.ts | 2 +- .../snapshots/binder/output/ref.out.json | 28 ++++++++- .../ref_name_and_color_setting.out.json | 52 ++++++++++++++- .../binder/output/ref_setting.out.json | 52 ++++++++++++++- .../ref_name_and_color_setting.out.json | 52 ++++++++++++++- .../interpreter/output/ref_settings.out.json | 52 ++++++++++++++- .../interpreter/output/table_group.out.json | 28 ++++++++- ...tablepartial_causing_circular_ref.out.json | 28 ++++++++- .../src/core/global_modules/ref/interpret.ts | 63 ++++++++++++++++++- .../src/core/types/symbol/metadata.ts | 22 ++++++- 10 files changed, 368 insertions(+), 11 deletions(-) diff --git a/packages/dbml-parse/__tests__/examples/interpreter/record/fk.test.ts b/packages/dbml-parse/__tests__/examples/interpreter/record/fk.test.ts index 89f0941d2..7317ec7dc 100644 --- a/packages/dbml-parse/__tests__/examples/interpreter/record/fk.test.ts +++ b/packages/dbml-parse/__tests__/examples/interpreter/record/fk.test.ts @@ -501,7 +501,7 @@ describe('[example - record] simple foreign key constraints', () => { } Table user_profiles { id int [pk] - user_id int + user_id int [unique] bio text } Table departments { diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/ref.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/ref.out.json index 30e19408e..60b48a77e 100644 --- a/packages/dbml-parse/__tests__/snapshots/binder/output/ref.out.json +++ b/packages/dbml-parse/__tests__/snapshots/binder/output/ref.out.json @@ -178,5 +178,31 @@ ] } ] - } + }, + "warnings": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'referrer_id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@referrer_id@[L2:C4, L2:C23]", + "snippet": "referrer_id integer" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'referrer_id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L6:C15, L6:C32]", + "snippet": "Users.referrer_id" + } + } + } + ] } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/ref_name_and_color_setting.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/ref_name_and_color_setting.out.json index 7c576a3e0..277a31024 100644 --- a/packages/dbml-parse/__tests__/snapshots/binder/output/ref_name_and_color_setting.out.json +++ b/packages/dbml-parse/__tests__/snapshots/binder/output/ref_name_and_color_setting.out.json @@ -352,5 +352,55 @@ ] } ] - } + }, + "warnings": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@id@[L1:C2, L1:C8]", + "snippet": "id int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@id@[L6:C2, L6:C8]", + "snippet": "id int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L11:C15, L11:C19]", + "snippet": "b.id" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L15:C2, L15:C6]", + "snippet": "c.id" + } + } + } + ] } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/ref_setting.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/ref_setting.out.json index 90764371f..4f663d746 100644 --- a/packages/dbml-parse/__tests__/snapshots/binder/output/ref_setting.out.json +++ b/packages/dbml-parse/__tests__/snapshots/binder/output/ref_setting.out.json @@ -159,5 +159,55 @@ ] } ] - } + }, + "warnings": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@id@[L1:C1, L1:C11]", + "snippet": "id integer" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'referrer' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@referrer@[L4:C4, L4:C31]", + "snippet": "referrer i...[ref: -id]" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'referrer' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@referrer@[L4:C4, L4:C31]", + "snippet": "referrer i...[ref: -id]" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L4:C22, L4:C30]", + "snippet": "ref: -id" + } + } + } + ] } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_name_and_color_setting.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_name_and_color_setting.out.json index c54ce7e58..72bc7bc72 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_name_and_color_setting.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_name_and_color_setting.out.json @@ -256,5 +256,55 @@ "offset": 0 } } - } + }, + "warnings": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@id@[L1:C2, L1:C8]", + "snippet": "id int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@id@[L6:C2, L6:C8]", + "snippet": "id int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L11:C15, L11:C19]", + "snippet": "b.id" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L15:C2, L15:C6]", + "snippet": "c.id" + } + } + } + ] } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_settings.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_settings.out.json index a9ff067dc..c4a9e5858 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_settings.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_settings.out.json @@ -255,5 +255,55 @@ "offset": 0 } } - } + }, + "warnings": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@id@[L6:C4, L6:C14]", + "snippet": "id integer" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'code' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@code@[L7:C4, L7:C15]", + "snippet": "code number" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L10:C12, L10:C16]", + "snippet": "B.id" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'code' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L11:C14, L11:C20]", + "snippet": "B.code" + } + } + } + ] } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_group.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_group.out.json index 85fa04c64..931b288d2 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_group.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_group.out.json @@ -357,5 +357,31 @@ "offset": 0 } } - } + }, + "warnings": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@id@[L1:C2, L1:C8]", + "snippet": "id int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L12:C16, L12:C27]", + "snippet": "ref: > U.id" + } + } + } + ] } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/tablepartial_causing_circular_ref.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/tablepartial_causing_circular_ref.out.json index f5df3db28..2defd837c 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/tablepartial_causing_circular_ref.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/tablepartial_causing_circular_ref.out.json @@ -256,5 +256,31 @@ "offset": 0 } } - } + }, + "warnings": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'col3' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@col3@[L2:C2, L2:C27]", + "snippet": "col3 type ... > T.col2]" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'col3' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "error", + "node": { + "context": { + "id": "node@@@[L7:C13, L7:C24]", + "snippet": "ref: > col3" + } + } + } + ] } \ No newline at end of file diff --git a/packages/dbml-parse/src/core/global_modules/ref/interpret.ts b/packages/dbml-parse/src/core/global_modules/ref/interpret.ts index eb13667bf..a412727c1 100644 --- a/packages/dbml-parse/src/core/global_modules/ref/interpret.ts +++ b/packages/dbml-parse/src/core/global_modules/ref/interpret.ts @@ -4,16 +4,18 @@ import { } from '@/core/utils/expression'; import { aggregateSettingList } from '@/core/utils/validate'; import { extractStringFromIdentifierStream } from '@/core/utils/expression'; -import { CompileError, CompileErrorCode } from '@/core/types/errors'; +import { CompileError, CompileErrorCode, CompileWarning } from '@/core/types/errors'; import { ElementDeclarationNode, FunctionApplicationNode, IdentifierStreamNode, type ListExpressionNode, AttributeNode, + SyntaxNode, } from '@/core/types/nodes'; import type { Ref } from '@/core/types/schemaJson'; import { + ColumnSymbol, RefMetadata, type Filepath, } from '@/core/types'; @@ -47,7 +49,8 @@ export class RefInterpreter { ...this.interpretName(), ...this.interpretBody(), ]; - return Report.create(this.ref as Ref, errors); + const warnings = this.validateRefConstraints(); + return Report.create(this.ref as Ref, errors, warnings); } private interpretName (): CompileError[] { @@ -153,4 +156,60 @@ export class RefInterpreter { return []; } + + private validateRefConstraints (): CompileWarning[] { + const leftCard = this.metadata.leftCardinality(this.compiler); + const rightCard = this.metadata.rightCardinality(this.compiler); + if (!leftCard || !rightCard) return []; + + const leftColumns = this.metadata.leftColumns(this.compiler); + const rightColumns = this.metadata.rightColumns(this.compiler); + + return [ + // rightCard: min constrains leftColumns (nullability), max constrains rightColumns (uniqueness) + ...this.validateCardinalityConstraints(rightCard, leftColumns, rightColumns, this.metadata.leftToken(), this.metadata.rightToken()), + // leftCard: min constrains rightColumns (nullability), max constrains leftColumns (uniqueness) + ...this.validateCardinalityConstraints(leftCard, rightColumns, leftColumns, this.metadata.rightToken(), this.metadata.leftToken()), + ]; + } + + // A cardinality constrains: + // min >= 1 → otherColumns must be NOT NULL + // max = 1 → ownColumns must be unique/pk + private validateCardinalityConstraints ( + cardinality: { min: number; max: number | '*' }, + otherColumns: ColumnSymbol[], + ownColumns: ColumnSymbol[], + otherNode: SyntaxNode, + ownNode: SyntaxNode, + ): CompileWarning[] { + const warnings: CompileWarning[] = []; + + // min >= 1 -> other side must be NOT NULL + if (cardinality.min >= 1 && otherColumns.length > 0) { + for (const col of otherColumns) { + if (col.nullable(this.compiler) === true) { + const msg = `Column '${col.name}' is nullable but relationship requires it to be NOT NULL`; + warnings.push(new CompileWarning(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, otherNode)); + if (col.declaration) { + warnings.push(new CompileWarning(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, col.declaration)); + } + } + } + } + + // max = 1 -> own side must be unique/pk + if (cardinality.max === 1 && ownColumns.length === 1) { + const col = ownColumns[0]; + if (!col.unique(this.compiler) && !col.pk(this.compiler)) { + const msg = `Column '${col.name}' should be unique or primary key for a one-side relationship`; + warnings.push(new CompileWarning(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, ownNode)); + if (col.declaration) { + warnings.push(new CompileWarning(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, col.declaration)); + } + } + } + + return warnings; + } } diff --git a/packages/dbml-parse/src/core/types/symbol/metadata.ts b/packages/dbml-parse/src/core/types/symbol/metadata.ts index 42740b807..d004ce8b2 100644 --- a/packages/dbml-parse/src/core/types/symbol/metadata.ts +++ b/packages/dbml-parse/src/core/types/symbol/metadata.ts @@ -17,7 +17,7 @@ import type { } from '../symbol'; import type { Internable } from '../internable'; import type { RelationshipOp, RelationCardinality } from '../relation'; -import { getMultiplicities } from '../relation'; +import { getMultiplicities, parseCardinality } from '../relation'; import { UNHANDLED } from '../module'; import { ElementKind, SettingName } from '../keywords'; import { @@ -168,6 +168,16 @@ export class RefMetadata extends NodeMetadata { return op ? getMultiplicities(op) : undefined; } + leftCardinality (compiler: Compiler): { min: number; max: number | '*' } | undefined { + const c = this.cardinalities(compiler); + return c ? parseCardinality(c[0]) : undefined; + } + + rightCardinality (compiler: Compiler): { min: number; max: number | '*' } | undefined { + const c = this.cardinalities(compiler); + return c ? parseCardinality(c[1]) : undefined; + } + active (compiler: Compiler): boolean { if (!(this.declaration instanceof ElementDeclarationNode)) return true; const field = getBody(this.declaration)[0]; @@ -290,6 +300,16 @@ export class PartialRefMetadata extends NodeMetadata { return op ? getMultiplicities(op) : undefined; } + leftCardinality (compiler: Compiler): { min: number; max: number | '*' } | undefined { + const c = this.cardinalities(compiler); + return c ? parseCardinality(c[0]) : undefined; + } + + rightCardinality (compiler: Compiler): { min: number; max: number | '*' } | undefined { + const c = this.cardinalities(compiler); + return c ? parseCardinality(c[1]) : undefined; + } + leftToken (): SyntaxNode { if (this.declaration instanceof AttributeNode) { const columnField = this.declaration.parentOfKind(FunctionApplicationNode); From 42481b64214c2317c24cce100f7cd8487879ca9c Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 1 Jul 2026 17:51:54 +0700 Subject: [PATCH 25/35] test: update cli test --- .../expect-out-files/schema.dbml | 4 +-- .../expect-out-files/schema.dbml | 10 +++---- .../filename --mysql stdout/stdout.txt | 10 +++---- .../expect-out-files/schema.dbml | 24 +++++++-------- .../expect-out-files/schema.dbml | 10 +++---- .../expect-out-files/schema.dbml | 10 +++---- .../filename --postgres stdout/stdout.txt | 10 +++---- .../filename --snowflake stdout/stdout.txt | 26 ++++++++-------- .../sql2dbml/filename stdout/stdout.txt | 10 +++---- .../expect-out-files/schema.dbml | 30 +++++++++---------- .../filenames --mysql stdout/stdout.txt | 30 +++++++++---------- .../expect-out-files/schema.dbml | 30 +++++++++---------- .../expect-out-files/schema.dbml | 30 +++++++++---------- .../filenames --postgres stdout/stdout.txt | 30 +++++++++---------- .../sql2dbml/filenames stdout/stdout.txt | 30 +++++++++---------- .../expect-out-files/multiple_schema.out.dbml | 10 +++---- .../expect-out-files/multiple_schema.out.dbml | 14 ++++----- .../expect-out-files/multiple_schema.out.dbml | 10 +++---- 18 files changed, 164 insertions(+), 164 deletions(-) diff --git a/packages/dbml-cli/__tests__/sql2dbml/filename --mssql --out-file/expect-out-files/schema.dbml b/packages/dbml-cli/__tests__/sql2dbml/filename --mssql --out-file/expect-out-files/schema.dbml index 74928b6c9..c29e57eee 100644 --- a/packages/dbml-cli/__tests__/sql2dbml/filename --mssql --out-file/expect-out-files/schema.dbml +++ b/packages/dbml-cli/__tests__/sql2dbml/filename --mssql --out-file/expect-out-files/schema.dbml @@ -20,6 +20,6 @@ Table "CodeDef" { } } -Ref "fk__CodeDef__ParentCdKey__CodeDef__CdKey":"CodeDef"."CdKey" < "CodeDef"."ParentCdKey" +Ref "fk__CodeDef__ParentCdKey__CodeDef__CdKey":"CodeDef"."CdKey" Date: Fri, 3 Jul 2026 09:47:24 +0700 Subject: [PATCH 26/35] v8.3.2-9.0.0-optional-ref.0 --- dbml-playground/package.json | 6 +++--- lerna.json | 2 +- packages/dbml-cli/package.json | 8 ++++---- packages/dbml-connector/package.json | 2 +- packages/dbml-core/package.json | 4 ++-- packages/dbml-parse/package.json | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dbml-playground/package.json b/dbml-playground/package.json index 062895d15..9f4a24fa4 100644 --- a/dbml-playground/package.json +++ b/dbml-playground/package.json @@ -1,6 +1,6 @@ { "name": "@dbml/playground", - "version": "8.3.1", + "version": "8.3.2-9.0.0-optional-ref.0", "description": "Interactive playground for debugging and visualizing the DBML parser pipeline", "author": "Holistics ", "license": "Apache-2.0", @@ -25,8 +25,8 @@ "format": "prettier --write src/" }, "dependencies": { - "@dbml/core": "^8.3.1", - "@dbml/parse": "^8.3.1", + "@dbml/core": "^8.3.2-9.0.0-optional-ref.0", + "@dbml/parse": "^8.3.2-9.0.0-optional-ref.0", "@phosphor-icons/vue": "^2.2.0", "floating-vue": "^5.2.2", "lodash-es": "^4.17.21", diff --git a/lerna.json b/lerna.json index 9ed5a08fc..a73c4a185 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "8.3.1", + "version": "8.3.2-9.0.0-optional-ref.0", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } diff --git a/packages/dbml-cli/package.json b/packages/dbml-cli/package.json index d3f3a2dda..60d421c1e 100644 --- a/packages/dbml-cli/package.json +++ b/packages/dbml-cli/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package", "name": "@dbml/cli", - "version": "8.3.1", + "version": "8.3.2-9.0.0-optional-ref.0", "description": "", "main": "lib/index.js", "license": "Apache-2.0", @@ -32,9 +32,9 @@ ], "dependencies": { "@babel/cli": "^7.21.0", - "@dbml/connector": "^8.3.1", - "@dbml/core": "^8.3.1", - "@dbml/parse": "^8.3.1", + "@dbml/connector": "^8.3.2-9.0.0-optional-ref.0", + "@dbml/core": "^8.3.2-9.0.0-optional-ref.0", + "@dbml/parse": "^8.3.2-9.0.0-optional-ref.0", "bluebird": "^3.5.5", "chalk": "^2.4.2", "commander": "^2.20.0", diff --git a/packages/dbml-connector/package.json b/packages/dbml-connector/package.json index 5599665db..972c9e35f 100644 --- a/packages/dbml-connector/package.json +++ b/packages/dbml-connector/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package", "name": "@dbml/connector", - "version": "8.3.1", + "version": "8.3.2-9.0.0-optional-ref.0", "description": "This package was created to fetch the schema JSON from many kind of databases.", "author": "huy.phung.sw@gmail.com", "license": "MIT", diff --git a/packages/dbml-core/package.json b/packages/dbml-core/package.json index 1b571fcc4..3947bba92 100644 --- a/packages/dbml-core/package.json +++ b/packages/dbml-core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package", "name": "@dbml/core", - "version": "8.3.1", + "version": "8.3.2-9.0.0-optional-ref.0", "description": "> TODO: description", "author": "Holistics ", "license": "Apache-2.0", @@ -46,7 +46,7 @@ "lint:fix": "eslint --fix ." }, "dependencies": { - "@dbml/parse": "^8.3.1", + "@dbml/parse": "^8.3.2-9.0.0-optional-ref.0", "antlr4": "^4.13.1", "lodash": "^4.18.1", "lodash-es": "^4.18.1", diff --git a/packages/dbml-parse/package.json b/packages/dbml-parse/package.json index 806e5dcc4..532d67fb6 100644 --- a/packages/dbml-parse/package.json +++ b/packages/dbml-parse/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package", "name": "@dbml/parse", - "version": "8.3.1", + "version": "8.3.2-9.0.0-optional-ref.0", "description": "DBML parser v2", "author": "Holistics ", "license": "Apache-2.0", From 01feeb4d524ff11d6cfaad16730a27c73e0d3b1f Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Fri, 3 Jul 2026 09:49:47 +0700 Subject: [PATCH 27/35] v9.0.0-optional-ref.0 --- dbml-playground/package.json | 6 +++--- lerna.json | 2 +- packages/dbml-cli/package.json | 8 ++++---- packages/dbml-connector/package.json | 2 +- packages/dbml-core/package.json | 4 ++-- packages/dbml-parse/package.json | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dbml-playground/package.json b/dbml-playground/package.json index 9f4a24fa4..274eb101e 100644 --- a/dbml-playground/package.json +++ b/dbml-playground/package.json @@ -1,6 +1,6 @@ { "name": "@dbml/playground", - "version": "8.3.2-9.0.0-optional-ref.0", + "version": "9.0.0-optional-ref.0", "description": "Interactive playground for debugging and visualizing the DBML parser pipeline", "author": "Holistics ", "license": "Apache-2.0", @@ -25,8 +25,8 @@ "format": "prettier --write src/" }, "dependencies": { - "@dbml/core": "^8.3.2-9.0.0-optional-ref.0", - "@dbml/parse": "^8.3.2-9.0.0-optional-ref.0", + "@dbml/core": "^9.0.0-optional-ref.0", + "@dbml/parse": "^9.0.0-optional-ref.0", "@phosphor-icons/vue": "^2.2.0", "floating-vue": "^5.2.2", "lodash-es": "^4.17.21", diff --git a/lerna.json b/lerna.json index a73c4a185..c5bb5105e 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "8.3.2-9.0.0-optional-ref.0", + "version": "9.0.0-optional-ref.0", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } diff --git a/packages/dbml-cli/package.json b/packages/dbml-cli/package.json index 60d421c1e..b4ede8608 100644 --- a/packages/dbml-cli/package.json +++ b/packages/dbml-cli/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package", "name": "@dbml/cli", - "version": "8.3.2-9.0.0-optional-ref.0", + "version": "9.0.0-optional-ref.0", "description": "", "main": "lib/index.js", "license": "Apache-2.0", @@ -32,9 +32,9 @@ ], "dependencies": { "@babel/cli": "^7.21.0", - "@dbml/connector": "^8.3.2-9.0.0-optional-ref.0", - "@dbml/core": "^8.3.2-9.0.0-optional-ref.0", - "@dbml/parse": "^8.3.2-9.0.0-optional-ref.0", + "@dbml/connector": "^9.0.0-optional-ref.0", + "@dbml/core": "^9.0.0-optional-ref.0", + "@dbml/parse": "^9.0.0-optional-ref.0", "bluebird": "^3.5.5", "chalk": "^2.4.2", "commander": "^2.20.0", diff --git a/packages/dbml-connector/package.json b/packages/dbml-connector/package.json index 972c9e35f..e5c4b6778 100644 --- a/packages/dbml-connector/package.json +++ b/packages/dbml-connector/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package", "name": "@dbml/connector", - "version": "8.3.2-9.0.0-optional-ref.0", + "version": "9.0.0-optional-ref.0", "description": "This package was created to fetch the schema JSON from many kind of databases.", "author": "huy.phung.sw@gmail.com", "license": "MIT", diff --git a/packages/dbml-core/package.json b/packages/dbml-core/package.json index 3947bba92..18af44faf 100644 --- a/packages/dbml-core/package.json +++ b/packages/dbml-core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package", "name": "@dbml/core", - "version": "8.3.2-9.0.0-optional-ref.0", + "version": "9.0.0-optional-ref.0", "description": "> TODO: description", "author": "Holistics ", "license": "Apache-2.0", @@ -46,7 +46,7 @@ "lint:fix": "eslint --fix ." }, "dependencies": { - "@dbml/parse": "^8.3.2-9.0.0-optional-ref.0", + "@dbml/parse": "^9.0.0-optional-ref.0", "antlr4": "^4.13.1", "lodash": "^4.18.1", "lodash-es": "^4.18.1", diff --git a/packages/dbml-parse/package.json b/packages/dbml-parse/package.json index 532d67fb6..21e190874 100644 --- a/packages/dbml-parse/package.json +++ b/packages/dbml-parse/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package", "name": "@dbml/parse", - "version": "8.3.2-9.0.0-optional-ref.0", + "version": "9.0.0-optional-ref.0", "description": "DBML parser v2", "author": "Holistics ", "license": "Apache-2.0", From a73fd23c556aafbdd87bd098e09c8b316ab857f7 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Fri, 3 Jul 2026 10:34:53 +0700 Subject: [PATCH 28/35] feat: re-export som cardinalities and helper functions --- packages/dbml-core/src/index.ts | 9 +++++++++ packages/dbml-core/types/index.d.ts | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/packages/dbml-core/src/index.ts b/packages/dbml-core/src/index.ts index 4ed75e9a2..1ececf44c 100644 --- a/packages/dbml-core/src/index.ts +++ b/packages/dbml-core/src/index.ts @@ -44,6 +44,13 @@ export { dbmlMonarchTokensProvider, DEFAULT_ENTRY, Filepath, + // Relationship cardinality constants and utilities + CARDINALITY_ONE, + CARDINALITY_MAYBE, + CARDINALITY_SOME, + CARDINALITY_MANY, + getMultiplicities, + getRelationshipOp, } from '@dbml/parse'; // Re-export types @@ -54,4 +61,6 @@ export type { DiagramViewSyncOperation, DiagramViewBlock, TextEdit, + RelationCardinality, + RelationshipOp, } from '@dbml/parse'; diff --git a/packages/dbml-core/types/index.d.ts b/packages/dbml-core/types/index.d.ts index c380ff1ea..e08dcef54 100644 --- a/packages/dbml-core/types/index.d.ts +++ b/packages/dbml-core/types/index.d.ts @@ -42,6 +42,12 @@ export { formatRecordValue, DEFAULT_ENTRY, Filepath, + CARDINALITY_ONE, + CARDINALITY_MAYBE, + CARDINALITY_SOME, + CARDINALITY_MANY, + getMultiplicities, + getRelationshipOp, } from '@dbml/parse'; // Re-export types @@ -52,4 +58,6 @@ export type { DiagramViewSyncOperation, DiagramViewBlock, TextEdit, + RelationCardinality, + RelationshipOp, } from '@dbml/parse'; From 95687af04506257d9aea15a593845364eae8dd72 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Fri, 3 Jul 2026 10:36:22 +0700 Subject: [PATCH 29/35] v9.0.0-optional-ref.1 --- dbml-playground/package.json | 4 ++-- lerna.json | 2 +- packages/dbml-cli/package.json | 4 ++-- packages/dbml-core/package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dbml-playground/package.json b/dbml-playground/package.json index 274eb101e..d7cc28b1d 100644 --- a/dbml-playground/package.json +++ b/dbml-playground/package.json @@ -1,6 +1,6 @@ { "name": "@dbml/playground", - "version": "9.0.0-optional-ref.0", + "version": "9.0.0-optional-ref.1", "description": "Interactive playground for debugging and visualizing the DBML parser pipeline", "author": "Holistics ", "license": "Apache-2.0", @@ -25,7 +25,7 @@ "format": "prettier --write src/" }, "dependencies": { - "@dbml/core": "^9.0.0-optional-ref.0", + "@dbml/core": "^9.0.0-optional-ref.1", "@dbml/parse": "^9.0.0-optional-ref.0", "@phosphor-icons/vue": "^2.2.0", "floating-vue": "^5.2.2", diff --git a/lerna.json b/lerna.json index c5bb5105e..2b382eda9 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "9.0.0-optional-ref.0", + "version": "9.0.0-optional-ref.1", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } diff --git a/packages/dbml-cli/package.json b/packages/dbml-cli/package.json index b4ede8608..db6986708 100644 --- a/packages/dbml-cli/package.json +++ b/packages/dbml-cli/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package", "name": "@dbml/cli", - "version": "9.0.0-optional-ref.0", + "version": "9.0.0-optional-ref.1", "description": "", "main": "lib/index.js", "license": "Apache-2.0", @@ -33,7 +33,7 @@ "dependencies": { "@babel/cli": "^7.21.0", "@dbml/connector": "^9.0.0-optional-ref.0", - "@dbml/core": "^9.0.0-optional-ref.0", + "@dbml/core": "^9.0.0-optional-ref.1", "@dbml/parse": "^9.0.0-optional-ref.0", "bluebird": "^3.5.5", "chalk": "^2.4.2", diff --git a/packages/dbml-core/package.json b/packages/dbml-core/package.json index 18af44faf..d8f076941 100644 --- a/packages/dbml-core/package.json +++ b/packages/dbml-core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package", "name": "@dbml/core", - "version": "9.0.0-optional-ref.0", + "version": "9.0.0-optional-ref.1", "description": "> TODO: description", "author": "Holistics ", "license": "Apache-2.0", From 0835f7a15830c9d22ac735f86319f6549fe1e0c6 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Fri, 3 Jul 2026 12:11:15 +0700 Subject: [PATCH 30/35] feat: add CompileInfo --- .../mssql/expect-out-files/schema.dbml | 2 +- .../mysql/expect-out-files/schema.dbml | 2 +- .../oracle/expect-out-files/schema.dbml | 16 +-- .../postgres/expect-out-files/schema.dbml | 2 +- .../__tests__/snapshots/binder/binder.test.ts | 7 +- .../snapshots/binder/output/ref.out.json | 54 +++++----- .../ref_name_and_color_setting.out.json | 102 +++++++++--------- .../binder/output/ref_setting.out.json | 102 +++++++++--------- .../snapshots/interpreter/interpreter.test.ts | 2 + .../ref_name_and_color_setting.out.json | 10 +- .../interpreter/output/ref_settings.out.json | 10 +- .../interpreter/output/table_group.out.json | 6 +- ...tablepartial_causing_circular_ref.out.json | 6 +- .../dbml-parse/__tests__/utils/compiler.ts | 1 + .../dbml-parse/__tests__/utils/testHelpers.ts | 57 +++++++++- .../compiler/queries/pipeline/interpret.ts | 8 +- .../core/global_modules/program/interpret.ts | 8 +- .../src/core/global_modules/ref/interpret.ts | 55 ++++++---- .../src/core/global_modules/utils.ts | 5 + packages/dbml-parse/src/core/types/errors.ts | 27 +++++ packages/dbml-parse/src/core/types/report.ts | 47 +++++--- .../src/services/diagnostics/provider.ts | 28 +++-- 22 files changed, 347 insertions(+), 210 deletions(-) diff --git a/packages/dbml-cli/__tests__/db2dbml/mssql/expect-out-files/schema.dbml b/packages/dbml-cli/__tests__/db2dbml/mssql/expect-out-files/schema.dbml index 19e300178..6292324cc 100644 --- a/packages/dbml-cli/__tests__/db2dbml/mssql/expect-out-files/schema.dbml +++ b/packages/dbml-cli/__tests__/db2dbml/mssql/expect-out-files/schema.dbml @@ -223,4 +223,4 @@ Ref "fk_order":"dbo"."orders"."order_id" < "dbo"."order_items"."order_id" [updat Ref "fk_user":"dbo"."users"."user_id" < "dbo"."orders"."user_id" [update: cascade, delete: cascade] -Ref "fk_gender":"dbo"."gender_reference"."value" < "dbo"."user_define_data_types"."gender" +Ref "fk_gender":"dbo"."gender_reference"."value" @referrer_id@[L2:C4, L2:C23]", + "snippet": "referrer_id integer" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'referrer_id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L6:C15, L6:C32]", + "snippet": "Users.referrer_id" + } + } + } + ], "nodeReferees": [ { "context": { @@ -178,31 +204,5 @@ ] } ] - }, - "warnings": [ - { - "code": "INVALID_REF_RELATIONSHIP", - "diagnostic": "Column 'referrer_id' should be unique or primary key for a one-side relationship", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@referrer_id@[L2:C4, L2:C23]", - "snippet": "referrer_id integer" - } - } - }, - { - "code": "INVALID_REF_RELATIONSHIP", - "diagnostic": "Column 'referrer_id' should be unique or primary key for a one-side relationship", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L6:C15, L6:C32]", - "snippet": "Users.referrer_id" - } - } - } - ] + } } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/ref_name_and_color_setting.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/ref_name_and_color_setting.out.json index 277a31024..4663ec6bc 100644 --- a/packages/dbml-parse/__tests__/snapshots/binder/output/ref_name_and_color_setting.out.json +++ b/packages/dbml-parse/__tests__/snapshots/binder/output/ref_name_and_color_setting.out.json @@ -1,4 +1,54 @@ { + "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L1:C2, L1:C8]", + "snippet": "id int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L6:C2, L6:C8]", + "snippet": "id int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L11:C15, L11:C19]", + "snippet": "b.id" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L15:C2, L15:C6]", + "snippet": "c.id" + } + } + } + ], "nodeReferees": [ { "context": { @@ -352,55 +402,5 @@ ] } ] - }, - "warnings": [ - { - "code": "INVALID_REF_RELATIONSHIP", - "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@id@[L1:C2, L1:C8]", - "snippet": "id int" - } - } - }, - { - "code": "INVALID_REF_RELATIONSHIP", - "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@id@[L6:C2, L6:C8]", - "snippet": "id int" - } - } - }, - { - "code": "INVALID_REF_RELATIONSHIP", - "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L11:C15, L11:C19]", - "snippet": "b.id" - } - } - }, - { - "code": "INVALID_REF_RELATIONSHIP", - "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L15:C2, L15:C6]", - "snippet": "c.id" - } - } - } - ] + } } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/ref_setting.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/ref_setting.out.json index 4f663d746..a4bf9cdc6 100644 --- a/packages/dbml-parse/__tests__/snapshots/binder/output/ref_setting.out.json +++ b/packages/dbml-parse/__tests__/snapshots/binder/output/ref_setting.out.json @@ -1,4 +1,54 @@ { + "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L1:C1, L1:C11]", + "snippet": "id integer" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'referrer' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@referrer@[L4:C4, L4:C31]", + "snippet": "referrer i...[ref: -id]" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'referrer' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@referrer@[L4:C4, L4:C31]", + "snippet": "referrer i...[ref: -id]" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L4:C22, L4:C30]", + "snippet": "ref: -id" + } + } + } + ], "nodeReferees": [ { "context": { @@ -159,55 +209,5 @@ ] } ] - }, - "warnings": [ - { - "code": "INVALID_REF_RELATIONSHIP", - "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@id@[L1:C1, L1:C11]", - "snippet": "id integer" - } - } - }, - { - "code": "INVALID_REF_RELATIONSHIP", - "diagnostic": "Column 'referrer' should be unique or primary key for a one-side relationship", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@referrer@[L4:C4, L4:C31]", - "snippet": "referrer i...[ref: -id]" - } - } - }, - { - "code": "INVALID_REF_RELATIONSHIP", - "diagnostic": "Column 'referrer' should be unique or primary key for a one-side relationship", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@referrer@[L4:C4, L4:C31]", - "snippet": "referrer i...[ref: -id]" - } - } - }, - { - "code": "INVALID_REF_RELATIONSHIP", - "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", - "filepath": "/main.dbml", - "level": "error", - "node": { - "context": { - "id": "node@@@[L4:C22, L4:C30]", - "snippet": "ref: -id" - } - } - } - ] + } } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/interpreter.test.ts b/packages/dbml-parse/__tests__/snapshots/interpreter/interpreter.test.ts index ffa38e476..a6c3b36aa 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/interpreter.test.ts +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/interpreter.test.ts @@ -17,11 +17,13 @@ import type Report from '@/core/types/report'; function serializeInterpreterResult (compiler: Compiler, report: Report): string { const errors = report.getErrors(); const warnings = report.getWarnings(); + const infos = report.getInfos(); const value = errors.length > 0 ? undefined : report.getValue(); return JSON.stringify(toSnapshot(compiler, { database: value as any, errors, warnings, + infos, }), null, 2); } diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_name_and_color_setting.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_name_and_color_setting.out.json index 72bc7bc72..fb0a66f90 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_name_and_color_setting.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_name_and_color_setting.out.json @@ -257,12 +257,12 @@ } } }, - "warnings": [ + "infos": [ { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", "filepath": "/main.dbml", - "level": "error", + "level": "info", "node": { "context": { "id": "node@@id@[L1:C2, L1:C8]", @@ -274,7 +274,7 @@ "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", "filepath": "/main.dbml", - "level": "error", + "level": "info", "node": { "context": { "id": "node@@id@[L6:C2, L6:C8]", @@ -286,7 +286,7 @@ "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", "filepath": "/main.dbml", - "level": "error", + "level": "info", "node": { "context": { "id": "node@@@[L11:C15, L11:C19]", @@ -298,7 +298,7 @@ "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", "filepath": "/main.dbml", - "level": "error", + "level": "info", "node": { "context": { "id": "node@@@[L15:C2, L15:C6]", diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_settings.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_settings.out.json index c4a9e5858..0a73d1239 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_settings.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_settings.out.json @@ -256,12 +256,12 @@ } } }, - "warnings": [ + "infos": [ { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", "filepath": "/main.dbml", - "level": "error", + "level": "info", "node": { "context": { "id": "node@@id@[L6:C4, L6:C14]", @@ -273,7 +273,7 @@ "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'code' should be unique or primary key for a one-side relationship", "filepath": "/main.dbml", - "level": "error", + "level": "info", "node": { "context": { "id": "node@@code@[L7:C4, L7:C15]", @@ -285,7 +285,7 @@ "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", "filepath": "/main.dbml", - "level": "error", + "level": "info", "node": { "context": { "id": "node@@@[L10:C12, L10:C16]", @@ -297,7 +297,7 @@ "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'code' should be unique or primary key for a one-side relationship", "filepath": "/main.dbml", - "level": "error", + "level": "info", "node": { "context": { "id": "node@@@[L11:C14, L11:C20]", diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_group.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_group.out.json index 931b288d2..327f5ce93 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_group.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_group.out.json @@ -358,12 +358,12 @@ } } }, - "warnings": [ + "infos": [ { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", "filepath": "/main.dbml", - "level": "error", + "level": "info", "node": { "context": { "id": "node@@id@[L1:C2, L1:C8]", @@ -375,7 +375,7 @@ "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", "filepath": "/main.dbml", - "level": "error", + "level": "info", "node": { "context": { "id": "node@@@[L12:C16, L12:C27]", diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/tablepartial_causing_circular_ref.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/tablepartial_causing_circular_ref.out.json index 2defd837c..8de4511dc 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/tablepartial_causing_circular_ref.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/tablepartial_causing_circular_ref.out.json @@ -257,12 +257,12 @@ } } }, - "warnings": [ + "infos": [ { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'col3' should be unique or primary key for a one-side relationship", "filepath": "/main.dbml", - "level": "error", + "level": "info", "node": { "context": { "id": "node@@col3@[L2:C2, L2:C27]", @@ -274,7 +274,7 @@ "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'col3' should be unique or primary key for a one-side relationship", "filepath": "/main.dbml", - "level": "error", + "level": "info", "node": { "context": { "id": "node@@@[L7:C13, L7:C24]", diff --git a/packages/dbml-parse/__tests__/utils/compiler.ts b/packages/dbml-parse/__tests__/utils/compiler.ts index 5f3fba168..1a42a49b1 100644 --- a/packages/dbml-parse/__tests__/utils/compiler.ts +++ b/packages/dbml-parse/__tests__/utils/compiler.ts @@ -103,6 +103,7 @@ export function interpret (source: string): Report | undefine db ? db as Database : undefined, [...parseResult.getErrors(), ...bindResult.getErrors(), ...interpretResult.getErrors()], [...parseResult.getWarnings(), ...bindResult.getWarnings(), ...interpretResult.getWarnings()], + [...parseResult.getInfos(), ...bindResult.getInfos(), ...interpretResult.getInfos()], ); } diff --git a/packages/dbml-parse/__tests__/utils/testHelpers.ts b/packages/dbml-parse/__tests__/utils/testHelpers.ts index ac5fc22cd..10eba8782 100644 --- a/packages/dbml-parse/__tests__/utils/testHelpers.ts +++ b/packages/dbml-parse/__tests__/utils/testHelpers.ts @@ -3,7 +3,7 @@ import { NodeSymbol, SchemaSymbol, SymbolKind } from '@/core/types/symbol'; import { SyntaxToken } from '@/core/types/tokens'; import { ElementDeclarationNode, FunctionApplicationNode, FunctionExpressionNode, LiteralNode, PrimaryExpressionNode, ProgramNode, SyntaxNode, VariableNode } from '@/core/types/nodes'; import { getElementNameString } from '@/core/utils/expression'; -import { CompileError, CompileErrorCode, CompileWarning } from '@/core/types/errors'; +import { CompileError, CompileErrorCode, CompileWarning, CompileInfo } from '@/core/types/errors'; import type Compiler from '@/compiler'; import { UNHANDLED } from '@/core/types/module'; import { Filepath, SchemaElement, TokenPosition } from '@/core/types'; @@ -78,6 +78,7 @@ export type Snappable = | string | number | null | undefined | boolean | bigint | symbol | CompileWarning | CompileError + | CompileInfo | SyntaxNode | SyntaxToken | NodeSymbol @@ -112,8 +113,9 @@ function sortArray (array: unknown[]): unknown[] { if (typeof s === 'boolean') return 2; if (typeof s === 'bigint') return 3; if (typeof s === 'symbol') return 4; - if (s instanceof CompileWarning) return 5; - if (s instanceof CompileError) return 6; + if (s instanceof CompileInfo) return 5; + if (s instanceof CompileWarning) return 6; + if (s instanceof CompileError) return 7; if (s instanceof SyntaxNode) return 7; if (s instanceof SyntaxToken) return 8; if ((s as any)?.token) return 9; // possibly a schema element @@ -127,7 +129,7 @@ function sortArray (array: unknown[]): unknown[] { if (typeof s === 'boolean') return Number(s); if (typeof s === 'bigint') return Number(s); if (typeof s === 'symbol') return s.toString(); - if (s instanceof CompileWarning || s instanceof CompileError) return (s as any).nodeOrToken?.start ?? 0; + if (s instanceof CompileInfo || s instanceof CompileWarning || s instanceof CompileError) return (s as any).nodeOrToken?.start ?? 0; if (s instanceof SyntaxNode) return s.start; if (s instanceof SyntaxToken) return s.start; if ((s as any)?.declaration) return getIntraKindRank((s as any).declaration); @@ -144,7 +146,7 @@ function sortArray (array: unknown[]): unknown[] { // Secondary tiebreaker when primary rank is equal function getTiebreakerRank (s: unknown): number | string { - if (s instanceof CompileWarning || s instanceof CompileError) return s.diagnostic; + if (s instanceof CompileInfo || s instanceof CompileWarning || s instanceof CompileError) return s.diagnostic; if (s instanceof SyntaxNode) return s.id; if (s instanceof SyntaxToken) return s.value ?? ''; if ((s as any)?.id !== undefined) return (s as any).id; @@ -171,6 +173,11 @@ export function toSnapshot ( if (Array.isArray(value)) { return sortArray([...value]).map((v) => toSnapshot(compiler, v as Snappable, { simple, includeReferences, includeSymbols, includeReferee })); } + if (value instanceof CompileInfo) { + return infoToSnapshot(compiler, value, { + simple, + }); + } if (value instanceof CompileWarning) { return warningToSnapshot(compiler, value, { simple, @@ -251,6 +258,46 @@ export function errorToSnapshot ( }); } +export function infoToSnapshot ( + compiler: Compiler, + info: CompileInfo, + { + simple = false, + }: { simple?: boolean } = {}, +): unknown { + const { + code, + diagnostic, + nodeOrToken, + filepath, + } = info; + if (simple) { + return sortObject({ + level: 'info', + code: CompileErrorCode[code], + diagnostic, + filepath: filepath.toString(), + }); + } + return sortObject({ + level: 'info', + code: CompileErrorCode[code], + diagnostic, + filepath: filepath.toString(), + ...(nodeOrToken instanceof SyntaxNode + ? { + node: syntaxNodeToSnapshot(compiler, nodeOrToken, { + simple: true, + }), + } + : { + token: syntaxTokenToSnapshot(compiler, nodeOrToken as SyntaxToken, { + simple: true, + }), + }), + }); +} + export function warningToSnapshot ( compiler: Compiler, warning: CompileWarning, diff --git a/packages/dbml-parse/src/compiler/queries/pipeline/interpret.ts b/packages/dbml-parse/src/compiler/queries/pipeline/interpret.ts index db25f642a..9d709a800 100644 --- a/packages/dbml-parse/src/compiler/queries/pipeline/interpret.ts +++ b/packages/dbml-parse/src/compiler/queries/pipeline/interpret.ts @@ -1,6 +1,6 @@ import type Compiler from '@/compiler'; import type { Database, MasterDatabase } from '@/core/types/schemaJson'; -import type { CompileError, CompileWarning } from '@/core/types/errors'; +import type { CompileError, CompileWarning, CompileInfo } from '@/core/types/errors'; import { Filepath, type FilepathId } from '@/core/types/filepath'; import { UNHANDLED } from '@/core/types/module'; import Report from '@/core/types/report'; @@ -18,6 +18,7 @@ export function interpretFile (this: Compiler, filepath: Filepath): Report { const errors: CompileError[] = []; const warnings: CompileWarning[] = []; + const infos: CompileInfo[] = []; // Collect all reachable files from all entry points const visited = new Set(); @@ -37,10 +38,12 @@ export function interpretProject (this: Compiler): Report { const parseResult = this.parseFile(file); errors.push(...parseResult.getErrors()); warnings.push(...parseResult.getWarnings()); + infos.push(...parseResult.getInfos()); const bindResult = this.bindFile(file); errors.push(...bindResult.getErrors()); warnings.push(...bindResult.getWarnings()); + infos.push(...bindResult.getInfos()); const { ast, @@ -53,6 +56,7 @@ export function interpretProject (this: Compiler): Report { } errors.push(...result.getErrors()); warnings.push(...result.getWarnings()); + infos.push(...result.getInfos()); } const files: Record = {}; @@ -66,5 +70,5 @@ export function interpretProject (this: Compiler): Report { return new Report({ files, - }, errors, warnings); + }, errors, warnings, infos); } diff --git a/packages/dbml-parse/src/core/global_modules/program/interpret.ts b/packages/dbml-parse/src/core/global_modules/program/interpret.ts index e7d9f32ca..bf23a5ae0 100644 --- a/packages/dbml-parse/src/core/global_modules/program/interpret.ts +++ b/packages/dbml-parse/src/core/global_modules/program/interpret.ts @@ -1,6 +1,6 @@ import Compiler from '@/compiler/index'; import { CompileError, CompileErrorCode } from '@/core/types/errors'; -import type { CompileWarning } from '@/core/types/errors'; +import type { CompileWarning, CompileInfo } from '@/core/types/errors'; import type { Filepath } from '@/core/types/filepath'; import { UNHANDLED } from '@/core/types/module'; import { ProgramNode } from '@/core/types/nodes'; @@ -38,6 +38,7 @@ export default class ProgramInterpreter { private filepath: Filepath; private errors: CompileError[] = []; private warnings: CompileWarning[] = []; + private infos: CompileInfo[] = []; private db: Database; constructor (compiler: Compiler, symbol: ProgramSymbol, filepath: Filepath) { @@ -72,7 +73,7 @@ export default class ProgramInterpreter { this.interpretAllMetadata(); this.interpretAllAliases(); this.warnings.push(...this.validateRecords()); - return new Report(this.db, this.errors, this.warnings); + return new Report(this.db, this.errors, this.warnings, this.infos); } private interpretAllSymbols () { @@ -85,6 +86,7 @@ export default class ProgramInterpreter { if (result.hasValue(UNHANDLED)) continue; this.errors.push(...result.getErrors()); this.warnings.push(...result.getWarnings()); + this.infos.push(...result.getInfos()); const value = result.getValue(); if (value) this.pushElement(symbol, value); } @@ -123,6 +125,7 @@ export default class ProgramInterpreter { if (!result.hasValue(UNHANDLED)) { this.errors.push(...result.getErrors()); this.warnings.push(...result.getWarnings()); + this.infos.push(...result.getInfos()); const value = result.getValue(); if (value) this.pushElement(use, value); } @@ -181,6 +184,7 @@ export default class ProgramInterpreter { if (result.hasValue(UNHANDLED)) continue; this.errors.push(...result.getErrors()); this.warnings.push(...result.getWarnings()); + this.infos.push(...result.getInfos()); const value = result.getValue(); if (value === undefined) continue; switch (meta.kind) { diff --git a/packages/dbml-parse/src/core/global_modules/ref/interpret.ts b/packages/dbml-parse/src/core/global_modules/ref/interpret.ts index a412727c1..a81964f1a 100644 --- a/packages/dbml-parse/src/core/global_modules/ref/interpret.ts +++ b/packages/dbml-parse/src/core/global_modules/ref/interpret.ts @@ -4,7 +4,7 @@ import { } from '@/core/utils/expression'; import { aggregateSettingList } from '@/core/utils/validate'; import { extractStringFromIdentifierStream } from '@/core/utils/expression'; -import { CompileError, CompileErrorCode, CompileWarning } from '@/core/types/errors'; +import { CompileError, CompileErrorCode, CompileInfo } from '@/core/types/errors'; import { ElementDeclarationNode, FunctionApplicationNode, @@ -49,8 +49,8 @@ export class RefInterpreter { ...this.interpretName(), ...this.interpretBody(), ]; - const warnings = this.validateRefConstraints(); - return Report.create(this.ref as Ref, errors, warnings); + const { infos } = this.validateRefConstraints(); + return Report.create(this.ref as Ref, errors, undefined, infos); } private interpretName (): CompileError[] { @@ -157,24 +157,28 @@ export class RefInterpreter { return []; } - private validateRefConstraints (): CompileWarning[] { + private validateRefConstraints (): { infos: CompileInfo[] } { const leftCard = this.metadata.leftCardinality(this.compiler); const rightCard = this.metadata.rightCardinality(this.compiler); - if (!leftCard || !rightCard) return []; + if (!leftCard || !rightCard) return { infos: [] }; const leftColumns = this.metadata.leftColumns(this.compiler); const rightColumns = this.metadata.rightColumns(this.compiler); - return [ - // rightCard: min constrains leftColumns (nullability), max constrains rightColumns (uniqueness) - ...this.validateCardinalityConstraints(rightCard, leftColumns, rightColumns, this.metadata.leftToken(), this.metadata.rightToken()), - // leftCard: min constrains rightColumns (nullability), max constrains leftColumns (uniqueness) - ...this.validateCardinalityConstraints(leftCard, rightColumns, leftColumns, this.metadata.rightToken(), this.metadata.leftToken()), - ]; + const r1 = this.validateCardinalityConstraints(rightCard, leftColumns, rightColumns, this.metadata.leftToken(), this.metadata.rightToken()); + const r2 = this.validateCardinalityConstraints(leftCard, rightColumns, leftColumns, this.metadata.rightToken(), this.metadata.leftToken()); + + return { + infos: [ + ...r1, + ...r2, + ], + }; } // A cardinality constrains: // min >= 1 → otherColumns must be NOT NULL + // min = 0 → otherColumns may be nullable; info if NOT NULL (operator is more permissive) // max = 1 → ownColumns must be unique/pk private validateCardinalityConstraints ( cardinality: { min: number; max: number | '*' }, @@ -182,17 +186,30 @@ export class RefInterpreter { ownColumns: ColumnSymbol[], otherNode: SyntaxNode, ownNode: SyntaxNode, - ): CompileWarning[] { - const warnings: CompileWarning[] = []; + ): CompileInfo[] { + const infos: CompileInfo[] = []; // min >= 1 -> other side must be NOT NULL if (cardinality.min >= 1 && otherColumns.length > 0) { for (const col of otherColumns) { if (col.nullable(this.compiler) === true) { - const msg = `Column '${col.name}' is nullable but relationship requires it to be NOT NULL`; - warnings.push(new CompileWarning(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, otherNode)); + const msg = `Column '${col.name}' is nullable but operator implies mandatory`; + infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, otherNode)); + if (col.declaration) { + infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, col.declaration)); + } + } + } + } + + // min = 0 -> other side may be nullable; info if NOT NULL + if (cardinality.min === 0 && otherColumns.length > 0) { + for (const col of otherColumns) { + if (col.nullable(this.compiler) === false) { + const msg = `Column '${col.name}' is NOT NULL but operator marks it optional`; + infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, otherNode)); if (col.declaration) { - warnings.push(new CompileWarning(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, col.declaration)); + infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, col.declaration)); } } } @@ -203,13 +220,13 @@ export class RefInterpreter { const col = ownColumns[0]; if (!col.unique(this.compiler) && !col.pk(this.compiler)) { const msg = `Column '${col.name}' should be unique or primary key for a one-side relationship`; - warnings.push(new CompileWarning(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, ownNode)); + infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, ownNode)); if (col.declaration) { - warnings.push(new CompileWarning(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, col.declaration)); + infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, col.declaration)); } } } - return warnings; + return infos; } } diff --git a/packages/dbml-parse/src/core/global_modules/utils.ts b/packages/dbml-parse/src/core/global_modules/utils.ts index 973f16da1..a316f2343 100644 --- a/packages/dbml-parse/src/core/global_modules/utils.ts +++ b/packages/dbml-parse/src/core/global_modules/utils.ts @@ -41,6 +41,11 @@ export function getNodeMemberSymbols (compiler: Compiler, node: SyntaxNode): Rep ...(symbol.hasValue(UNHANDLED) ? [] : symbol.getWarnings()), ...(nestedSymbols.hasValue(UNHANDLED) ? [] : nestedSymbols.getWarnings()), ], + [ + ...report.getInfos(), + ...(symbol.hasValue(UNHANDLED) ? [] : symbol.getInfos()), + ...(nestedSymbols.hasValue(UNHANDLED) ? [] : nestedSymbols.getInfos()), + ], ); }, new Report([]), diff --git a/packages/dbml-parse/src/core/types/errors.ts b/packages/dbml-parse/src/core/types/errors.ts index 3e3f24872..0ad933253 100644 --- a/packages/dbml-parse/src/core/types/errors.ts +++ b/packages/dbml-parse/src/core/types/errors.ts @@ -200,3 +200,30 @@ export class CompileWarning extends Error { return this.nodeOrToken.filepath; } } + +export class CompileInfo extends Error { + code: Readonly; + + diagnostic: Readonly; + + nodeOrToken: Readonly; + + start: Readonly; + + end: Readonly; + + constructor (code: number, message: string, nodeOrToken: SyntaxNode | SyntaxToken) { + super(message); + this.code = code; + this.diagnostic = message; + this.nodeOrToken = nodeOrToken; + this.start = nodeOrToken.start; + this.end = nodeOrToken.end; + this.name = this.constructor.name; + Object.setPrototypeOf(this, CompileInfo.prototype); + } + + get filepath (): Filepath { + return this.nodeOrToken.filepath; + } +} diff --git a/packages/dbml-parse/src/core/types/report.ts b/packages/dbml-parse/src/core/types/report.ts index c9ed20ad7..7575eedc8 100644 --- a/packages/dbml-parse/src/core/types/report.ts +++ b/packages/dbml-parse/src/core/types/report.ts @@ -1,6 +1,6 @@ -import { CompileError, CompileWarning } from './errors'; +import { CompileError, CompileWarning, CompileInfo } from './errors'; -// Used to hold the result of a computation and any errors/warnings along the way +// Used to hold the result of a computation and any errors/warnings/infos along the way export default class Report { private value: T; @@ -8,20 +8,21 @@ export default class Report { private warnings?: CompileWarning[]; - static create (value: T, errors?: CompileError[], warnings?: CompileWarning[]) { - return new Report(value, errors, warnings); + private infos?: CompileInfo[]; + + static create (value: T, errors?: CompileError[], warnings?: CompileWarning[], infos?: CompileInfo[]) { + return new Report(value, errors, warnings, infos); } - constructor (value: T, errors?: CompileError[], warnings?: CompileWarning[]) { + constructor (value: T, errors?: CompileError[], warnings?: CompileWarning[], infos?: CompileInfo[]) { this.value = value; - this.errors = errors === undefined ? [] : errors; - if (warnings?.length) { - this.warnings = warnings; - } + this.errors = errors ?? []; + this.warnings = warnings; + this.infos = infos; } filter (filteredValue: S): Report> { - if (this.value as any === filteredValue) return new Report(undefined, this.errors, this.warnings); + if (this.value as any === filteredValue) return new Report(undefined, this.errors, this.warnings, this.infos); return this as Report>; } @@ -44,7 +45,11 @@ export default class Report { } getWarnings (): CompileWarning[] { - return this.warnings || []; + return this.warnings ?? []; + } + + getInfos (): CompileInfo[] { + return this.infos ?? []; } // Chain the reported value @@ -52,7 +57,7 @@ export default class Report { // 2. If `fn` produces further warnings or errors, accumulate // If the reported value is filteredValue, return undefined chainFiltered(fn: (_: Exclude) => Report, filteredValue: S): Report { - if (this.value as any === filteredValue) return new Report(undefined, this.errors, this.warnings); + if (this.value as any === filteredValue) return new Report(undefined, this.errors, this.warnings, this.infos); const res = fn(this.value as Exclude); const errors = [ ...this.errors, @@ -62,8 +67,12 @@ export default class Report { ...this.getWarnings(), ...res.getWarnings(), ]; + const infos = [ + ...this.getInfos(), + ...res.getInfos(), + ]; - return new Report(res.value, errors, warnings); + return new Report(res.value, errors, warnings, infos); } chain(fn: (_: T) => Report): Report { @@ -76,8 +85,12 @@ export default class Report { ...this.getWarnings(), ...res.getWarnings(), ]; + const infos = [ + ...this.getInfos(), + ...res.getInfos(), + ]; - return new Report(res.value, errors, warnings); + return new Report(res.value, errors, warnings, infos); } // Map the reported value @@ -85,11 +98,11 @@ export default class Report { // 2. `fn` cannot produce further warnings or errors // If the reported value is filteredValue, return undefined mapFiltered(fn: (_: Exclude) => U, filteredValue: S): Report { - if (this.value as any === filteredValue) return new Report(undefined, this.errors, this.warnings); - return new Report(fn(this.value as Exclude), this.errors, this.warnings); + if (this.value as any === filteredValue) return new Report(undefined, this.errors, this.warnings, this.infos); + return new Report(fn(this.value as Exclude), this.errors, this.warnings, this.infos); } map(fn: (_: T) => U): Report { - return new Report(fn(this.value), this.errors, this.warnings); + return new Report(fn(this.value), this.errors, this.warnings, this.infos); } } diff --git a/packages/dbml-parse/src/services/diagnostics/provider.ts b/packages/dbml-parse/src/services/diagnostics/provider.ts index 7f73c57e0..901fd1d0d 100644 --- a/packages/dbml-parse/src/services/diagnostics/provider.ts +++ b/packages/dbml-parse/src/services/diagnostics/provider.ts @@ -1,13 +1,13 @@ import type Compiler from '@/compiler'; import { Filepath } from '@/core/types/filepath'; -import type { CompileError, CompileWarning } from '@/core/types/errors'; +import type { CompileError, CompileWarning, CompileInfo } from '@/core/types/errors'; import type { SyntaxNode } from '@/core/types/nodes'; import type { SyntaxToken } from '@/core/types/tokens'; import { MarkerData, MarkerSeverity } from '@/services/types'; // This is the same format that dbdiagram-frontend uses export interface Diagnostic { - type: 'error' | 'warning'; + type: 'error' | 'warning' | 'info'; text: string; startRow: number; startColumn: number; @@ -31,6 +31,7 @@ export default class DBMLDiagnosticsProvider { return [ ...this.provideErrors(filepath), ...this.provideWarnings(filepath), + ...this.provideInfos(filepath), ]; } @@ -58,6 +59,18 @@ export default class DBMLDiagnosticsProvider { return warnings.map((warning) => this.createDiagnostic(warning, 'warning')); } + /** + * Get only infos from the current compilation + */ + provideInfos (filepath?: Filepath): Diagnostic[] { + if (!filepath) { + const infosResult = this.compiler.interpretProject().getInfos(); + return infosResult.map((info) => this.createDiagnostic(info, 'info')); + } + const infosResult = this.compiler.interpretFile(filepath).getInfos(); + return infosResult.map((info) => this.createDiagnostic(info, 'info')); + } + /** * Convert Monaco markers format (for editor integration) */ @@ -78,8 +91,8 @@ export default class DBMLDiagnosticsProvider { } private createDiagnostic ( - errorOrWarning: CompileError | CompileWarning, - severity: 'error' | 'warning', + errorOrWarning: CompileError | CompileWarning | CompileInfo, + severity: 'error' | 'warning' | 'info', ): Diagnostic { const nodeOrToken = errorOrWarning.nodeOrToken; @@ -101,8 +114,9 @@ export default class DBMLDiagnosticsProvider { }; } - private getSeverityValue (severity: 'error' | 'warning'): MarkerSeverity { - // Monaco marker severity values - return severity === 'error' ? MarkerSeverity.Error : MarkerSeverity.Warning; + private getSeverityValue (severity: 'error' | 'warning' | 'info'): MarkerSeverity { + if (severity === 'error') return MarkerSeverity.Error; + if (severity === 'warning') return MarkerSeverity.Warning; + return MarkerSeverity.Info; } } From c2ed0409da6fad24ac6e9fac01bdda9b2d0deb30 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Fri, 3 Jul 2026 15:47:57 +0700 Subject: [PATCH 31/35] fix: split nullable() and isNotNullSet() --- .../snapshots/binder/output/ref.out.json | 48 +++++++ .../ref_name_and_color_setting.out.json | 96 ++++++++++++++ .../binder/output/ref_setting.out.json | 48 +++++++ .../output/general_schema.out.json | 124 +++++++++++++++++- .../output/note_normalize.out.json | 76 ++++++++++- ...te_normalize_with_top_empty_lines.out.json | 76 ++++++++++- .../interpreter/output/project.out.json | 124 +++++++++++++++++- .../ref_name_and_color_setting.out.json | 96 ++++++++++++++ .../interpreter/output/ref_settings.out.json | 96 ++++++++++++++ .../output/referential_actions.out.json | 124 +++++++++++++++++- .../interpreter/output/table_group.out.json | 48 +++++++ .../output/table_settings.out.json | 52 +++++++- ...tablepartial_causing_circular_ref.out.json | 48 +++++++ .../core/global_modules/records/interpret.ts | 2 +- .../records/utils/constraints/helper.ts | 2 +- .../core/global_modules/table/interpret.ts | 3 +- .../global_modules/tablePartial/interpret.ts | 3 +- .../src/core/types/symbol/symbols.ts | 23 +++- 18 files changed, 1073 insertions(+), 16 deletions(-) diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/ref.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/ref.out.json index afc846e0c..220cabe16 100644 --- a/packages/dbml-parse/__tests__/snapshots/binder/output/ref.out.json +++ b/packages/dbml-parse/__tests__/snapshots/binder/output/ref.out.json @@ -1,5 +1,29 @@ { "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L1:C4, L1:C14]", + "snippet": "id integer" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'referrer_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@referrer_id@[L2:C4, L2:C23]", + "snippet": "referrer_id integer" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'referrer_id' should be unique or primary key for a one-side relationship", @@ -12,6 +36,30 @@ } } }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L6:C4, L6:C12]", + "snippet": "Users.id" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'referrer_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L6:C15, L6:C32]", + "snippet": "Users.referrer_id" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'referrer_id' should be unique or primary key for a one-side relationship", diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/ref_name_and_color_setting.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/ref_name_and_color_setting.out.json index 4663ec6bc..8b4637f6f 100644 --- a/packages/dbml-parse/__tests__/snapshots/binder/output/ref_name_and_color_setting.out.json +++ b/packages/dbml-parse/__tests__/snapshots/binder/output/ref_name_and_color_setting.out.json @@ -1,5 +1,17 @@ { "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L1:C2, L1:C8]", + "snippet": "id int" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", @@ -12,6 +24,42 @@ } } }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'c_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@c_id@[L2:C2, L2:C10]", + "snippet": "c_id int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L6:C2, L6:C8]", + "snippet": "id int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L6:C2, L6:C8]", + "snippet": "id int" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", @@ -24,6 +72,18 @@ } } }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L11:C15, L11:C19]", + "snippet": "b.id" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", @@ -36,6 +96,30 @@ } } }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L11:C22, L11:C26]", + "snippet": "c.id" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L15:C2, L15:C6]", + "snippet": "c.id" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", @@ -47,6 +131,18 @@ "snippet": "c.id" } } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'c_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L15:C9, L15:C15]", + "snippet": "b.c_id" + } + } } ], "nodeReferees": [ diff --git a/packages/dbml-parse/__tests__/snapshots/binder/output/ref_setting.out.json b/packages/dbml-parse/__tests__/snapshots/binder/output/ref_setting.out.json index a4bf9cdc6..1f18e28a3 100644 --- a/packages/dbml-parse/__tests__/snapshots/binder/output/ref_setting.out.json +++ b/packages/dbml-parse/__tests__/snapshots/binder/output/ref_setting.out.json @@ -1,5 +1,17 @@ { "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L1:C1, L1:C11]", + "snippet": "id integer" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", @@ -12,6 +24,30 @@ } } }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'referrer' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@referrer@[L4:C4, L4:C31]", + "snippet": "referrer i...[ref: -id]" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'referrer' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@referrer@[L4:C4, L4:C31]", + "snippet": "referrer i...[ref: -id]" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'referrer' should be unique or primary key for a one-side relationship", @@ -36,6 +72,18 @@ } } }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L4:C22, L4:C30]", + "snippet": "ref: -id" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/general_schema.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/general_schema.out.json index 4a480a447..6d763e490 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/general_schema.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/general_schema.out.json @@ -1341,5 +1341,127 @@ "offset": 0 } } - } + }, + "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'order_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@order_id@[L20:C2, L20:C16]", + "snippet": "\"order_id\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'product_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@product_id@[L21:C2, L21:C18]", + "snippet": "\"product_id\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'country_code' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@country_code@[L47:C2, L47:C20]", + "snippet": "\"country_code\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'country_code' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@country_code@[L63:C2, L63:C20]", + "snippet": "\"country_code\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'admin_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@admin_id@[L65:C2, L65:C16]", + "snippet": "\"admin_id\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'order_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L74:C20, L74:C44]", + "snippet": "\"order_ite...\"order_id\"" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'product_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L76:C22, L76:C48]", + "snippet": "\"order_ite...roduct_id\"" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'country_code' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L78:C25, L78:C47]", + "snippet": "\"users\".\"c...ntry_code\"" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'country_code' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L80:C25, L80:C51]", + "snippet": "\"merchants...ntry_code\"" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'admin_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L84:C19, L84:C41]", + "snippet": "\"merchants...\"admin_id\"" + } + } + } + ] } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/note_normalize.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/note_normalize.out.json index eef8e4f8f..43524d38b 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/note_normalize.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/note_normalize.out.json @@ -579,5 +579,79 @@ "offset": 87 } } - } + }, + "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'following_user_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@following_user_id@[L4:C2, L4:C27]", + "snippet": "following_...id integer" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'followed_user_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@followed_user_id@[L5:C2, L5:C26]", + "snippet": "followed_u...id integer" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'user_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@user_id@[L45:C2, L45:C17]", + "snippet": "user_id integer" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'user_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L60:C5, L60:C18]", + "snippet": "posts.user_id" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'following_user_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L62:C16, L62:C41]", + "snippet": "follows.fo...ng_user_id" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'followed_user_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L64:C16, L64:C40]", + "snippet": "follows.fo...ed_user_id" + } + } + } + ] } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/note_normalize_with_top_empty_lines.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/note_normalize_with_top_empty_lines.out.json index b88a3db0e..39c81f2c5 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/note_normalize_with_top_empty_lines.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/note_normalize_with_top_empty_lines.out.json @@ -579,5 +579,79 @@ "offset": 87 } } - } + }, + "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'following_user_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@following_user_id@[L4:C2, L4:C27]", + "snippet": "following_...id integer" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'followed_user_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@followed_user_id@[L5:C2, L5:C26]", + "snippet": "followed_u...id integer" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'user_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@user_id@[L50:C2, L50:C17]", + "snippet": "user_id integer" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'user_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L65:C5, L65:C18]", + "snippet": "posts.user_id" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'following_user_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L67:C16, L67:C41]", + "snippet": "follows.fo...ng_user_id" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'followed_user_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L69:C16, L69:C40]", + "snippet": "follows.fo...ed_user_id" + } + } + } + ] } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/project.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/project.out.json index ecd2278c2..cc13de31d 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/project.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/project.out.json @@ -1374,5 +1374,127 @@ "offset": 0 } } - } + }, + "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'order_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@order_id@[L31:C2, L31:C16]", + "snippet": "\"order_id\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'product_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@product_id@[L32:C2, L32:C18]", + "snippet": "\"product_id\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'country_code' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@country_code@[L58:C2, L58:C20]", + "snippet": "\"country_code\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'country_code' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@country_code@[L74:C2, L74:C20]", + "snippet": "\"country_code\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'admin_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@admin_id@[L76:C2, L76:C16]", + "snippet": "\"admin_id\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'order_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L85:C20, L85:C44]", + "snippet": "\"order_ite...\"order_id\"" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'product_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L87:C22, L87:C48]", + "snippet": "\"order_ite...roduct_id\"" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'country_code' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L89:C25, L89:C47]", + "snippet": "\"users\".\"c...ntry_code\"" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'country_code' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L91:C25, L91:C51]", + "snippet": "\"merchants...ntry_code\"" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'admin_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L95:C19, L95:C41]", + "snippet": "\"merchants...\"admin_id\"" + } + } + } + ] } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_name_and_color_setting.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_name_and_color_setting.out.json index fb0a66f90..a34645e70 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_name_and_color_setting.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_name_and_color_setting.out.json @@ -258,6 +258,18 @@ } }, "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L1:C2, L1:C8]", + "snippet": "id int" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", @@ -270,6 +282,42 @@ } } }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'c_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@c_id@[L2:C2, L2:C10]", + "snippet": "c_id int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L6:C2, L6:C8]", + "snippet": "id int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L6:C2, L6:C8]", + "snippet": "id int" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", @@ -282,6 +330,18 @@ } } }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L11:C15, L11:C19]", + "snippet": "b.id" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", @@ -294,6 +354,30 @@ } } }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L11:C22, L11:C26]", + "snippet": "c.id" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L15:C2, L15:C6]", + "snippet": "c.id" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", @@ -305,6 +389,18 @@ "snippet": "c.id" } } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'c_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L15:C9, L15:C15]", + "snippet": "b.c_id" + } + } } ] } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_settings.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_settings.out.json index 0a73d1239..d53fe4f78 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_settings.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/ref_settings.out.json @@ -257,6 +257,42 @@ } }, "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L1:C4, L1:C14]", + "snippet": "id integer" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'code' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@code@[L2:C4, L2:C15]", + "snippet": "code number" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L6:C4, L6:C14]", + "snippet": "id integer" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", @@ -269,6 +305,18 @@ } } }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'code' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@code@[L7:C4, L7:C15]", + "snippet": "code number" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'code' should be unique or primary key for a one-side relationship", @@ -281,6 +329,30 @@ } } }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L10:C5, L10:C9]", + "snippet": "A.id" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L10:C12, L10:C16]", + "snippet": "B.id" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", @@ -293,6 +365,30 @@ } } }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'code' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L11:C5, L11:C11]", + "snippet": "A.code" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'code' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L11:C14, L11:C20]", + "snippet": "B.code" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'code' should be unique or primary key for a one-side relationship", diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/referential_actions.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/referential_actions.out.json index be795b8a3..aa2f38902 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/referential_actions.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/referential_actions.out.json @@ -922,5 +922,127 @@ "offset": 0 } } - } + }, + "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'name' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@name@[L5:C2, L5:C21]", + "snippet": "\"name\" varchar(255)" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'order_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@order_id@[L16:C2, L16:C16]", + "snippet": "\"order_id\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'product_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@product_id@[L17:C2, L17:C18]", + "snippet": "\"product_id\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'product_name' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@product_name@[L18:C2, L18:C29]", + "snippet": "\"product_n...rchar(255)" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L23:C2, L23:C10]", + "snippet": "\"id\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'order_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L50:C20, L50:C44]", + "snippet": "\"order_ite...\"order_id\"" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L52:C4, L52:C29]", + "snippet": "\"products\"...\", \"name\")" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'name' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L52:C4, L52:C29]", + "snippet": "\"products\"...\", \"name\")" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'product_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L52:C32, L52:C76]", + "snippet": "\"order_ite...uct_name\")" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'product_name' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L52:C32, L52:C76]", + "snippet": "\"order_ite...uct_name\")" + } + } + } + ] } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_group.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_group.out.json index 327f5ce93..fc315f819 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_group.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_group.out.json @@ -359,6 +359,18 @@ } }, "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@id@[L1:C2, L1:C8]", + "snippet": "id int" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", @@ -371,6 +383,42 @@ } } }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'admin_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@admin_id@[L12:C2, L12:C28]", + "snippet": "admin_id i...f: > U.id]" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'admin_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@admin_id@[L12:C2, L12:C28]", + "snippet": "admin_id i...f: > U.id]" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L12:C16, L12:C27]", + "snippet": "ref: > U.id" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'id' should be unique or primary key for a one-side relationship", diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_settings.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_settings.out.json index a9e5a6c58..9b5fd8c1a 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_settings.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/table_settings.out.json @@ -487,5 +487,55 @@ "offset": 0 } } - } + }, + "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'user_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@user_id@[L18:C2, L18:C15]", + "snippet": "\"user_id\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'product_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@product_id@[L19:C2, L19:C18]", + "snippet": "\"product_id\" int" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'user_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L23:C18, L23:C38]", + "snippet": "\"merchant\".\"user_id\"" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'product_id' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L25:C21, L25:C44]", + "snippet": "\"merchant\"...roduct_id\"" + } + } + } + ] } \ No newline at end of file diff --git a/packages/dbml-parse/__tests__/snapshots/interpreter/output/tablepartial_causing_circular_ref.out.json b/packages/dbml-parse/__tests__/snapshots/interpreter/output/tablepartial_causing_circular_ref.out.json index 8de4511dc..e45ea93d7 100644 --- a/packages/dbml-parse/__tests__/snapshots/interpreter/output/tablepartial_causing_circular_ref.out.json +++ b/packages/dbml-parse/__tests__/snapshots/interpreter/output/tablepartial_causing_circular_ref.out.json @@ -258,6 +258,18 @@ } }, "infos": [ + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'col3' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@col3@[L2:C2, L2:C27]", + "snippet": "col3 type ... > T.col2]" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'col3' should be unique or primary key for a one-side relationship", @@ -270,6 +282,42 @@ } } }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'col2' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@col2@[L7:C2, L7:C25]", + "snippet": "col2 type ...f: > col3]" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'col2' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@col2@[L7:C2, L7:C25]", + "snippet": "col2 type ...f: > col3]" + } + } + }, + { + "code": "INVALID_REF_RELATIONSHIP", + "diagnostic": "Column 'col3' is nullable but operator implies mandatory", + "filepath": "/main.dbml", + "level": "info", + "node": { + "context": { + "id": "node@@@[L7:C13, L7:C24]", + "snippet": "ref: > col3" + } + } + }, { "code": "INVALID_REF_RELATIONSHIP", "diagnostic": "Column 'col3' should be unique or primary key for a one-side relationship", diff --git a/packages/dbml-parse/src/core/global_modules/records/interpret.ts b/packages/dbml-parse/src/core/global_modules/records/interpret.ts index d4ccbc809..f572c0b3e 100644 --- a/packages/dbml-parse/src/core/global_modules/records/interpret.ts +++ b/packages/dbml-parse/src/core/global_modules/records/interpret.ts @@ -233,7 +233,7 @@ function extractValue ( const typeName = (typeInfo?.name ?? '').split('(')[0]; const isEnum = !!typeInfo?.enumSymbol; const increment = colSymbol.increment(compiler); - const notNull = colSymbol.nullable(compiler) === false; + const notNull = colSymbol.isNotNullSet(compiler) === true; const dbdefault = colSymbol.default(compiler); const colName = colSymbol.name ?? ''; const valueType = getRecordValueType(typeName, isEnum); diff --git a/packages/dbml-parse/src/core/global_modules/records/utils/constraints/helper.ts b/packages/dbml-parse/src/core/global_modules/records/utils/constraints/helper.ts index f5bc0f005..7bd2615ae 100644 --- a/packages/dbml-parse/src/core/global_modules/records/utils/constraints/helper.ts +++ b/packages/dbml-parse/src/core/global_modules/records/utils/constraints/helper.ts @@ -25,7 +25,7 @@ export function columnInfoFromSymbol (col: ColumnSymbol, compiler: Compiler): Co pk: col.pk(compiler), unique: col.unique(compiler), increment: col.increment(compiler), - notNull: col.nullable(compiler) === false, + notNull: col.isNotNullSet(compiler) === true, dbdefault: def, typeName: type?.name ?? '', }; diff --git a/packages/dbml-parse/src/core/global_modules/table/interpret.ts b/packages/dbml-parse/src/core/global_modules/table/interpret.ts index 2274b3cd8..457ddd83b 100644 --- a/packages/dbml-parse/src/core/global_modules/table/interpret.ts +++ b/packages/dbml-parse/src/core/global_modules/table/interpret.ts @@ -272,8 +272,7 @@ export class TableInterpreter { column.pk = columnSymbol?.pk(this.compiler) || false; column.unique = columnSymbol?.unique(this.compiler) || false; column.increment = columnSymbol?.increment(this.compiler) || undefined; - const nullable = columnSymbol?.nullable(this.compiler); - column.not_null = nullable === undefined ? undefined : !nullable; + column.not_null = columnSymbol?.isNotNullSet(this.compiler); column.dbdefault = columnSymbol?.default(this.compiler); column.note = columnSymbol?.note(this.compiler); diff --git a/packages/dbml-parse/src/core/global_modules/tablePartial/interpret.ts b/packages/dbml-parse/src/core/global_modules/tablePartial/interpret.ts index 11a845e14..a2d631f12 100644 --- a/packages/dbml-parse/src/core/global_modules/tablePartial/interpret.ts +++ b/packages/dbml-parse/src/core/global_modules/tablePartial/interpret.ts @@ -185,8 +185,7 @@ export class TablePartialInterpreter { column.pk = columnSymbol?.pk(this.compiler) || undefined; column.unique = columnSymbol?.unique(this.compiler) || undefined; column.increment = columnSymbol?.increment(this.compiler) || undefined; - const nullable = columnSymbol?.nullable(this.compiler); - column.not_null = nullable === undefined ? undefined : !nullable; + column.not_null = columnSymbol?.isNotNullSet(this.compiler); column.dbdefault = columnSymbol?.default(this.compiler); column.note = columnSymbol?.note(this.compiler); diff --git a/packages/dbml-parse/src/core/types/symbol/symbols.ts b/packages/dbml-parse/src/core/types/symbol/symbols.ts index e5fd324b0..160ea445a 100644 --- a/packages/dbml-parse/src/core/types/symbol/symbols.ts +++ b/packages/dbml-parse/src/core/types/symbol/symbols.ts @@ -505,12 +505,27 @@ export class ColumnSymbol extends NodeSymbol { return !!s?.[SettingName.Unique]?.length; } - nullable (compiler: Compiler): boolean | undefined { - if (!this.declaration) return undefined; + // Returns whether the column is nullable, considering all constraints: + // pk, increment -> not nullable + // [not null] -> not nullable + // [null] -> nullable + // unspecified -> nullable (SQL default) + nullable (compiler: Compiler): boolean { + if (!this.declaration) return true; + if (this.pk(compiler)) return false; + if (this.increment(compiler)) return false; const s = compiler.nodeSettings(this.declaration).getFiltered(UNHANDLED); - if (s?.[SettingName.NotNull]?.length) return false; - if (s?.[SettingName.Null]?.length) return true; + return true; + } + + // Returns whether [not null] or [null] is explicitly set + // true if [not null], false if [null], undefined if unspecified + isNotNullSet (compiler: Compiler): boolean | undefined { + if (!this.declaration) return undefined; + const s = compiler.nodeSettings(this.declaration).getFiltered(UNHANDLED); + if (s?.[SettingName.NotNull]?.length) return true; + if (s?.[SettingName.Null]?.length) return false; return undefined; } From 7d15f1de5244491ef1955e6e0a854bd94e1b358a Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Fri, 3 Jul 2026 15:58:22 +0700 Subject: [PATCH 32/35] feat: add code action provider --- .../src/services/code_actions/provider.ts | 22 +++++++++++++++++++ packages/dbml-parse/src/services/index.ts | 2 ++ packages/dbml-parse/src/services/types.ts | 9 +++++++- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 packages/dbml-parse/src/services/code_actions/provider.ts diff --git a/packages/dbml-parse/src/services/code_actions/provider.ts b/packages/dbml-parse/src/services/code_actions/provider.ts new file mode 100644 index 000000000..97cd4659c --- /dev/null +++ b/packages/dbml-parse/src/services/code_actions/provider.ts @@ -0,0 +1,22 @@ +import type Compiler from '@/compiler'; +import type { + CodeActionProvider, CodeActionList, CodeActionContext, + TextModel, Range, CancellationToken, +} from '../types'; + +export default class DBMLCodeActionProvider implements CodeActionProvider { + private compiler: Compiler; + + constructor (compiler: Compiler) { + this.compiler = compiler; + } + + provideCodeActions ( + model: TextModel, + range: Range, + context: CodeActionContext, + token: CancellationToken, + ): CodeActionList | undefined { + return undefined; + } +} diff --git a/packages/dbml-parse/src/services/index.ts b/packages/dbml-parse/src/services/index.ts index b6e60fa07..1b44bd432 100644 --- a/packages/dbml-parse/src/services/index.ts +++ b/packages/dbml-parse/src/services/index.ts @@ -1,3 +1,4 @@ +import DBMLCodeActionProvider from './code_actions/provider'; import DBMLDefinitionProvider from './definition/provider'; import DBMLDiagnosticsProvider from './diagnostics/provider'; export type { Diagnostic } from './diagnostics/provider'; @@ -7,6 +8,7 @@ import DBMLCompletionItemProvider from './suggestions/provider'; export * from '@/services/types'; export { + DBMLCodeActionProvider, DBMLCompletionItemProvider, DBMLDefinitionProvider, DBMLReferencesProvider, diff --git a/packages/dbml-parse/src/services/types.ts b/packages/dbml-parse/src/services/types.ts index 8a41d75f4..2382f7999 100644 --- a/packages/dbml-parse/src/services/types.ts +++ b/packages/dbml-parse/src/services/types.ts @@ -82,9 +82,16 @@ export type Color = languages.IColor; // Go to definition export type DefinitionProvider = languages.DefinitionProvider; export type Definition = languages.Definition; -export type CodeActionList = languages.CodeActionList; export type SignatureHelpResult = languages.SignatureHelpResult; +// Code actions +export type CodeActionProvider = languages.CodeActionProvider; +export type CodeActionList = languages.CodeActionList; +export type CodeAction = languages.CodeAction; +export type CodeActionContext = languages.CodeActionContext; +export type WorkspaceEdit = languages.WorkspaceEdit; +export type WorkspaceTextEdit = languages.IWorkspaceTextEdit; + // Show references export type ReferenceProvider = languages.ReferenceProvider; From f41fb3b03bb374a283e039bb050e2a278889a6b1 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Fri, 3 Jul 2026 16:58:11 +0700 Subject: [PATCH 33/35] feat: implement code actions --- .../src/components/editor/dbml_services.ts | 1 + .../code_actions/code_actions.test.ts | 95 +++++++++++++ packages/dbml-parse/src/compiler/index.ts | 3 +- .../src/core/global_modules/ref/interpret.ts | 127 ++++++++++++------ packages/dbml-parse/src/core/types/errors.ts | 17 ++- .../dbml-parse/src/core/types/relation.ts | 24 ++++ .../src/services/code_actions/provider.ts | 41 +++++- packages/dbml-parse/src/services/types.ts | 1 - 8 files changed, 257 insertions(+), 52 deletions(-) create mode 100644 packages/dbml-parse/__tests__/examples/services/code_actions/code_actions.test.ts diff --git a/dbml-playground/src/components/editor/dbml_services.ts b/dbml-playground/src/components/editor/dbml_services.ts index 2cce39cf4..d99e086a5 100644 --- a/dbml-playground/src/components/editor/dbml_services.ts +++ b/dbml-playground/src/components/editor/dbml_services.ts @@ -17,6 +17,7 @@ export async function setupDbmlServices (compiler: Compiler): Promise { monaco.languages.registerDefinitionProvider(languageId, services.definitionProvider as any); monaco.languages.registerReferenceProvider(languageId, services.referenceProvider as any); monaco.languages.registerCompletionItemProvider(languageId, services.autocompletionProvider as any); + monaco.languages.registerCodeActionProvider(languageId, services.codeActionProvider as any); } export function updateDiagnosticMarkers (model: monaco.editor.ITextModel): void { diff --git a/packages/dbml-parse/__tests__/examples/services/code_actions/code_actions.test.ts b/packages/dbml-parse/__tests__/examples/services/code_actions/code_actions.test.ts new file mode 100644 index 000000000..b00ad1c83 --- /dev/null +++ b/packages/dbml-parse/__tests__/examples/services/code_actions/code_actions.test.ts @@ -0,0 +1,95 @@ +import { + describe, expect, test, +} from 'vitest'; +import { interpret } from '@tests/utils'; + +function getQuickFixes (source: string) { + const result = interpret(source); + const infos = result.getInfos(); + return infos.flatMap((info) => info.quickFixes ?? []); +} + +describe('[example] code actions - ref constraint quick fixes', () => { + describe('nullability fixes', () => { + test('nullable column with required ref suggests changing op or adding [not null]', () => { + const source = ` + Table users { id int [pk] } + Table posts { user_id int [null] } + Ref: posts.user_id > users.id + `; + const fixes = getQuickFixes(source); + expect(fixes.some((f) => f.title.includes('Change operator'))).toBe(true); + expect(fixes.some((f) => f.title.includes('[not null]'))).toBe(true); + }); + + test('NOT NULL column with optional ref suggests changing op', () => { + const source = ` + Table users { id int [pk] } + Table posts { user_id int [not null] } + Ref: posts.user_id >? users.id + `; + const fixes = getQuickFixes(source); + expect(fixes.some((f) => f.title.includes('Change operator'))).toBe(true); + }); + + test('no fixes when nullability matches operator', () => { + const source = ` + Table users { id int [pk] } + Table posts { user_id int [not null] } + Ref: posts.user_id > users.id + `; + const fixes = getQuickFixes(source); + expect(fixes).toHaveLength(0); + }); + }); + + describe('uniqueness fixes', () => { + test('non-unique column in one-to-one ref suggests changing op or adding [unique]', () => { + const source = ` + Table users { id int [pk] } + Table profiles { user_id int } + Ref: profiles.user_id - users.id + `; + const fixes = getQuickFixes(source); + expect(fixes.some((f) => f.title.includes('Change operator'))).toBe(true); + expect(fixes.some((f) => f.title.includes('[unique]'))).toBe(true); + }); + + test('no uniqueness fix when column is pk', () => { + const source = ` + Table users { id int [pk] } + Table profiles { user_id int [pk] } + Ref: profiles.user_id - users.id + `; + const fixes = getQuickFixes(source); + const uniqueFixes = fixes.filter((f) => f.title.includes('[unique]')); + expect(uniqueFixes).toHaveLength(0); + }); + }); + + describe('operator change produces correct new op', () => { + test('> with nullable column suggests ?<', () => { + const source = ` + Table users { id int [pk] } + Table posts { user_id int [null] } + Ref: posts.user_id > users.id + `; + const fixes = getQuickFixes(source); + const opFix = fixes.find((f) => f.title.includes('Change operator')); + // right card (1,1) becomes (0,1), getRelationshipOp(0..1, *) = ?< + expect(opFix?.title).toContain('?<'); + }); + + test('- with non-unique column suggests >', () => { + const source = ` + Table users { id int [pk] } + Table profiles { user_id int [not null] } + Ref: profiles.user_id - users.id + `; + const fixes = getQuickFixes(source); + const opFix = fixes.find((f) => f.title.includes('Change operator')); + // left card (1,1) max becomes *, getRelationshipOp(*, 1) = > + expect(opFix?.title).toContain('>'); + }); + }); +}); diff --git a/packages/dbml-parse/src/compiler/index.ts b/packages/dbml-parse/src/compiler/index.ts index df2dfead3..8d81df77e 100644 --- a/packages/dbml-parse/src/compiler/index.ts +++ b/packages/dbml-parse/src/compiler/index.ts @@ -18,7 +18,7 @@ import { type Internable, type Primitive, intern } from '@/core/types/internable import { SyntaxNode, SyntaxNodeIdGenerator } from '@/core/types/nodes'; import { NodeSymbolIdGenerator, SymbolFactory } from '@/core/types/symbol'; import { - DBMLCompletionItemProvider, DBMLDefinitionProvider, DBMLDiagnosticsProvider, DBMLReferencesProvider, + DBMLCodeActionProvider, DBMLCompletionItemProvider, DBMLDefinitionProvider, DBMLDiagnosticsProvider, DBMLReferencesProvider, } from '@/services/index'; import { type DbmlProjectLayout } from './projectLayout'; import { @@ -432,6 +432,7 @@ export default class Compiler { triggerCharacters, }), diagnosticsProvider: new DBMLDiagnosticsProvider(this), + codeActionProvider: new DBMLCodeActionProvider(this), }; } } diff --git a/packages/dbml-parse/src/core/global_modules/ref/interpret.ts b/packages/dbml-parse/src/core/global_modules/ref/interpret.ts index a81964f1a..0c0ff692f 100644 --- a/packages/dbml-parse/src/core/global_modules/ref/interpret.ts +++ b/packages/dbml-parse/src/core/global_modules/ref/interpret.ts @@ -5,13 +5,15 @@ import { import { aggregateSettingList } from '@/core/utils/validate'; import { extractStringFromIdentifierStream } from '@/core/utils/expression'; import { CompileError, CompileErrorCode, CompileInfo } from '@/core/types/errors'; +import type { SyntaxToken } from '@/core/types/tokens'; import { ElementDeclarationNode, FunctionApplicationNode, + InfixExpressionNode, IdentifierStreamNode, type ListExpressionNode, AttributeNode, - SyntaxNode, + PrefixExpressionNode, } from '@/core/types/nodes'; import type { Ref } from '@/core/types/schemaJson'; import { @@ -25,7 +27,12 @@ import { getTokenPosition, } from '@/core/utils/interpret'; import Report from '@/core/types/report'; -import { getMultiplicities } from '@/core/types/relation'; +import { + getMultiplicities, getRelationshipOp, parseCardinality, + makeCardinalityOptional, makeCardinalityRequired, makeCardinalityMany, +} from '@/core/types/relation'; +import type { RelationCardinality } from '@/core/types/relation'; +import type { QuickFix } from '@/core/types/errors'; import { zip } from 'lodash-es'; export class RefInterpreter { @@ -158,75 +165,109 @@ export class RefInterpreter { } private validateRefConstraints (): { infos: CompileInfo[] } { - const leftCard = this.metadata.leftCardinality(this.compiler); - const rightCard = this.metadata.rightCardinality(this.compiler); - if (!leftCard || !rightCard) return { infos: [] }; - - const leftColumns = this.metadata.leftColumns(this.compiler); - const rightColumns = this.metadata.rightColumns(this.compiler); - - const r1 = this.validateCardinalityConstraints(rightCard, leftColumns, rightColumns, this.metadata.leftToken(), this.metadata.rightToken()); - const r2 = this.validateCardinalityConstraints(leftCard, rightColumns, leftColumns, this.metadata.rightToken(), this.metadata.leftToken()); - + if (!this.metadata.cardinalities(this.compiler)) return { infos: [] }; return { infos: [ - ...r1, - ...r2, + ...this.validateCardinality('right'), + ...this.validateCardinality('left'), ], }; } + private getOpToken (): SyntaxToken | undefined { + if (this.declarationNode instanceof ElementDeclarationNode) { + const field = getBody(this.declarationNode)[0]; + if (!(field instanceof FunctionApplicationNode)) return undefined; + const infix = field.callee; + if (!(infix instanceof InfixExpressionNode)) return undefined; + return infix.op; + } + if (this.declarationNode instanceof AttributeNode) { + const prefix = this.declarationNode.value; + if (!(prefix instanceof PrefixExpressionNode)) return undefined; + return prefix.op; + } + return undefined; + } + // A cardinality constrains: - // min >= 1 → otherColumns must be NOT NULL - // min = 0 → otherColumns may be nullable; info if NOT NULL (operator is more permissive) - // max = 1 → ownColumns must be unique/pk - private validateCardinalityConstraints ( - cardinality: { min: number; max: number | '*' }, - otherColumns: ColumnSymbol[], - ownColumns: ColumnSymbol[], - otherNode: SyntaxNode, - ownNode: SyntaxNode, - ): CompileInfo[] { + // min >= 1 -> otherColumns must be NOT NULL + // min = 0 -> otherColumns may be nullable; info if NOT NULL + // max = 1 -> ownColumns must be unique/pk + private validateCardinality (side: 'left' | 'right'): CompileInfo[] { + const cardinalities = this.metadata.cardinalities(this.compiler)!; + const thisRel = side === 'left' ? cardinalities[0] : cardinalities[1]; + const otherRel = side === 'left' ? cardinalities[1] : cardinalities[0]; + const card = parseCardinality(thisRel); + + const otherColumns = side === 'left' ? this.metadata.rightColumns(this.compiler) : this.metadata.leftColumns(this.compiler); + const ownColumns = side === 'left' ? this.metadata.leftColumns(this.compiler) : this.metadata.rightColumns(this.compiler); + const otherNode = side === 'left' ? this.metadata.rightToken() : this.metadata.leftToken(); + const ownNode = side === 'left' ? this.metadata.leftToken() : this.metadata.rightToken(); + const opToken = this.getOpToken(); + const infos: CompileInfo[] = []; - // min >= 1 -> other side must be NOT NULL - if (cardinality.min >= 1 && otherColumns.length > 0) { + if (card.min >= 1) { for (const col of otherColumns) { - if (col.nullable(this.compiler) === true) { + if (col.nullable(this.compiler)) { const msg = `Column '${col.name}' is nullable but operator implies mandatory`; - infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, otherNode)); - if (col.declaration) { - infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, col.declaration)); - } + const fixes = [ + opToken && suggestChangeOp(opToken, makeCardinalityOptional(thisRel), otherRel), + suggestAddSetting(col, 'not null'), + ].filter((f): f is QuickFix => !!f); + infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, otherNode, fixes)); + if (col.declaration) infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, col.declaration, fixes)); } } } - // min = 0 -> other side may be nullable; info if NOT NULL - if (cardinality.min === 0 && otherColumns.length > 0) { + if (card.min === 0) { for (const col of otherColumns) { if (col.nullable(this.compiler) === false) { const msg = `Column '${col.name}' is NOT NULL but operator marks it optional`; - infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, otherNode)); - if (col.declaration) { - infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, col.declaration)); - } + const fixes = [ + opToken && suggestChangeOp(opToken, makeCardinalityRequired(thisRel), otherRel), + ].filter((f): f is QuickFix => !!f); + infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, otherNode, fixes)); + if (col.declaration) infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, col.declaration, fixes)); } } } - // max = 1 -> own side must be unique/pk - if (cardinality.max === 1 && ownColumns.length === 1) { + if (card.max === 1 && ownColumns.length === 1) { const col = ownColumns[0]; if (!col.unique(this.compiler) && !col.pk(this.compiler)) { const msg = `Column '${col.name}' should be unique or primary key for a one-side relationship`; - infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, ownNode)); - if (col.declaration) { - infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, col.declaration)); - } + const fixes = [ + opToken && suggestChangeOp(opToken, makeCardinalityMany(thisRel), otherRel), + suggestAddSetting(col, 'unique'), + ].filter((f): f is QuickFix => !!f); + infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, ownNode, fixes)); + if (col.declaration) infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, col.declaration, fixes)); } } return infos; } } + +function suggestChangeOp (opToken: SyntaxToken, newThisRel: RelationCardinality, otherRel: RelationCardinality): QuickFix { + const newOp = getRelationshipOp(newThisRel, otherRel); + return { + title: `Change operator to '${newOp}'`, + edits: [ + { range: { start: opToken.startPos, end: opToken.endPos }, newText: newOp, filepath: opToken.filepath }, + ], + }; +} + +function suggestAddSetting (col: ColumnSymbol, setting: string): QuickFix | undefined { + if (!col.declaration) return undefined; + return { + title: `Add [${setting}] to '${col.name}'`, + edits: [ + { range: { start: col.declaration.endPos, end: col.declaration.endPos }, newText: ` [${setting}]`, filepath: col.declaration.filepath }, + ], + }; +} diff --git a/packages/dbml-parse/src/core/types/errors.ts b/packages/dbml-parse/src/core/types/errors.ts index 0ad933253..6580ed4ab 100644 --- a/packages/dbml-parse/src/core/types/errors.ts +++ b/packages/dbml-parse/src/core/types/errors.ts @@ -1,5 +1,6 @@ import { Filepath } from './filepath'; import { SyntaxNode } from '@/core/types/nodes'; +import type { Position } from './position'; import { SyntaxToken } from '@/core/types/tokens'; export enum CompileErrorCode { @@ -201,6 +202,17 @@ export class CompileWarning extends Error { } } +export interface QuickFix { + title: string; + edits: TextEdit[]; +} + +export interface TextEdit { + range: { start: Position; end: Position }; + newText: string; + filepath: Filepath; +} + export class CompileInfo extends Error { code: Readonly; @@ -212,13 +224,16 @@ export class CompileInfo extends Error { end: Readonly; - constructor (code: number, message: string, nodeOrToken: SyntaxNode | SyntaxToken) { + quickFixes?: QuickFix[]; + + constructor (code: number, message: string, nodeOrToken: SyntaxNode | SyntaxToken, quickFixes?: QuickFix[]) { super(message); this.code = code; this.diagnostic = message; this.nodeOrToken = nodeOrToken; this.start = nodeOrToken.start; this.end = nodeOrToken.end; + this.quickFixes = quickFixes; this.name = this.constructor.name; Object.setPrototypeOf(this, CompileInfo.prototype); } diff --git a/packages/dbml-parse/src/core/types/relation.ts b/packages/dbml-parse/src/core/types/relation.ts index 3452353d6..5846b0e52 100644 --- a/packages/dbml-parse/src/core/types/relation.ts +++ b/packages/dbml-parse/src/core/types/relation.ts @@ -191,3 +191,27 @@ export function getRelationshipOp ( return '<>'; } + +// Cardinality transforms: adjust min or max while preserving the other. +// Used by code actions to suggest operator changes. + +// Set min to 0 (allow null): 1 -> 0..1, * -> 0..* +export function makeCardinalityOptional (rel: RelationCardinality): RelationCardinality { + const { min, max } = parseCardinality(rel); + if (min === 0) return rel; + return max === '*' ? CARDINALITY_MANY : CARDINALITY_MAYBE; +} + +// Set min to 1 (require not null): 0..1 -> 1, 0..* -> * +export function makeCardinalityRequired (rel: RelationCardinality): RelationCardinality { + const { min, max } = parseCardinality(rel); + if (min >= 1) return rel; + return max === '*' ? CARDINALITY_SOME : CARDINALITY_ONE; +} + +// Set max to * (allow many): 1 -> *, 0..1 -> 0..* +export function makeCardinalityMany (rel: RelationCardinality): RelationCardinality { + const { min, max } = parseCardinality(rel); + if (max === '*') return rel; + return min === 0 ? CARDINALITY_MANY : CARDINALITY_SOME; +} diff --git a/packages/dbml-parse/src/services/code_actions/provider.ts b/packages/dbml-parse/src/services/code_actions/provider.ts index 97cd4659c..39f1422f6 100644 --- a/packages/dbml-parse/src/services/code_actions/provider.ts +++ b/packages/dbml-parse/src/services/code_actions/provider.ts @@ -1,7 +1,8 @@ import type Compiler from '@/compiler'; +import type { QuickFix } from '@/core/types/errors'; import type { - CodeActionProvider, CodeActionList, CodeActionContext, - TextModel, Range, CancellationToken, + CodeActionProvider, CodeActionList, CodeAction, + TextModel, WorkspaceEdit, } from '../types'; export default class DBMLCodeActionProvider implements CodeActionProvider { @@ -13,10 +14,38 @@ export default class DBMLCodeActionProvider implements CodeActionProvider { provideCodeActions ( model: TextModel, - range: Range, - context: CodeActionContext, - token: CancellationToken, ): CodeActionList | undefined { - return undefined; + const infos = this.compiler.interpretProject().getInfos(); + const withFixes = infos.filter((info) => info.quickFixes?.length); + if (!withFixes.length) return undefined; + + const actions: CodeAction[] = []; + for (const info of withFixes) { + for (const fix of info.quickFixes ?? []) { + actions.push(this.quickFixToCodeAction(fix, model)); + } + } + + if (!actions.length) return undefined; + return { actions, dispose () {} }; + } + + private quickFixToCodeAction (fix: QuickFix, model: TextModel): CodeAction { + const edit: WorkspaceEdit = { + edits: fix.edits.map((e) => ({ + resource: model.uri, + textEdit: { + range: { + startLineNumber: e.range.start.line + 1, + startColumn: e.range.start.column + 1, + endLineNumber: e.range.end.line + 1, + endColumn: e.range.end.column + 1, + }, + text: e.newText, + }, + versionId: model.getVersionId(), + })), + }; + return { title: fix.title, edit }; } } diff --git a/packages/dbml-parse/src/services/types.ts b/packages/dbml-parse/src/services/types.ts index 2382f7999..f26a773af 100644 --- a/packages/dbml-parse/src/services/types.ts +++ b/packages/dbml-parse/src/services/types.ts @@ -90,7 +90,6 @@ export type CodeActionList = languages.CodeActionList; export type CodeAction = languages.CodeAction; export type CodeActionContext = languages.CodeActionContext; export type WorkspaceEdit = languages.WorkspaceEdit; -export type WorkspaceTextEdit = languages.IWorkspaceTextEdit; // Show references export type ReferenceProvider = languages.ReferenceProvider; From 80c0de1791736c9a3c3aded667960b0de320febe Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Fri, 3 Jul 2026 17:14:59 +0700 Subject: [PATCH 34/35] fix: filter out relevant code actions --- .../src/services/code_actions/provider.ts | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/packages/dbml-parse/src/services/code_actions/provider.ts b/packages/dbml-parse/src/services/code_actions/provider.ts index 39f1422f6..e530ca2d8 100644 --- a/packages/dbml-parse/src/services/code_actions/provider.ts +++ b/packages/dbml-parse/src/services/code_actions/provider.ts @@ -1,8 +1,9 @@ import type Compiler from '@/compiler'; -import type { QuickFix } from '@/core/types/errors'; +import type { CompileInfo, QuickFix } from '@/core/types/errors'; +import { CompileErrorCode } from '@/core/types/errors'; import type { - CodeActionProvider, CodeActionList, CodeAction, - TextModel, WorkspaceEdit, + CodeActionProvider, CodeActionList, CodeAction, CodeActionContext, + TextModel, Range, CancellationToken, WorkspaceEdit, MarkerData, } from '../types'; export default class DBMLCodeActionProvider implements CodeActionProvider { @@ -14,15 +15,22 @@ export default class DBMLCodeActionProvider implements CodeActionProvider { provideCodeActions ( model: TextModel, + _range: Range, + context: CodeActionContext, + _token: CancellationToken, ): CodeActionList | undefined { - const infos = this.compiler.interpretProject().getInfos(); - const withFixes = infos.filter((info) => info.quickFixes?.length); - if (!withFixes.length) return undefined; + const markers = context.markers; + if (!markers.length) return undefined; + const infos = this.compiler.interpretProject().getInfos(); const actions: CodeAction[] = []; - for (const info of withFixes) { - for (const fix of info.quickFixes ?? []) { - actions.push(this.quickFixToCodeAction(fix, model)); + + for (const marker of markers) { + const matchingInfos = this.findInfosForMarker(infos, marker); + for (const info of matchingInfos) { + for (const fix of info.quickFixes ?? []) { + actions.push(this.quickFixToCodeAction(fix, model, marker)); + } } } @@ -30,7 +38,18 @@ export default class DBMLCodeActionProvider implements CodeActionProvider { return { actions, dispose () {} }; } - private quickFixToCodeAction (fix: QuickFix, model: TextModel): CodeAction { + private findInfosForMarker (infos: CompileInfo[], marker: MarkerData): CompileInfo[] { + return infos.filter((info) => { + const node = info.nodeOrToken; + return info.quickFixes?.length + && node.startPos.line + 1 === marker.startLineNumber + && node.startPos.column + 1 === marker.startColumn + && node.endPos.line + 1 === marker.endLineNumber + && node.endPos.column + 1 === marker.endColumn; + }); + } + + private quickFixToCodeAction (fix: QuickFix, model: TextModel, marker: MarkerData): CodeAction { const edit: WorkspaceEdit = { edits: fix.edits.map((e) => ({ resource: model.uri, @@ -46,6 +65,12 @@ export default class DBMLCodeActionProvider implements CodeActionProvider { versionId: model.getVersionId(), })), }; - return { title: fix.title, edit }; + return { + title: fix.title, + edit, + diagnostics: [ + marker, + ], + }; } } From 5924d61ba493a40500288018cc3eb2df2c05191e Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Fri, 3 Jul 2026 17:31:31 +0700 Subject: [PATCH 35/35] fix: make code action provider multifile aware --- .../code_actions/code_actions.test.ts | 11 +++--- packages/dbml-parse/src/compiler/index.ts | 3 +- .../src/compiler/queries/transform/index.ts | 1 + .../src/core/global_modules/ref/interpret.ts | 34 +++++++++++++------ packages/dbml-parse/src/core/types/errors.ts | 8 +---- .../src/services/code_actions/provider.ts | 32 ++++++++++------- 6 files changed, 52 insertions(+), 37 deletions(-) diff --git a/packages/dbml-parse/__tests__/examples/services/code_actions/code_actions.test.ts b/packages/dbml-parse/__tests__/examples/services/code_actions/code_actions.test.ts index b00ad1c83..c33a6ea92 100644 --- a/packages/dbml-parse/__tests__/examples/services/code_actions/code_actions.test.ts +++ b/packages/dbml-parse/__tests__/examples/services/code_actions/code_actions.test.ts @@ -19,7 +19,7 @@ describe('[example] code actions - ref constraint quick fixes', () => { `; const fixes = getQuickFixes(source); expect(fixes.some((f) => f.title.includes('Change operator'))).toBe(true); - expect(fixes.some((f) => f.title.includes('[not null]'))).toBe(true); + expect(fixes.some((f) => f.title.includes('not null'))).toBe(true); }); test('NOT NULL column with optional ref suggests changing op', () => { @@ -52,7 +52,7 @@ describe('[example] code actions - ref constraint quick fixes', () => { `; const fixes = getQuickFixes(source); expect(fixes.some((f) => f.title.includes('Change operator'))).toBe(true); - expect(fixes.some((f) => f.title.includes('[unique]'))).toBe(true); + expect(fixes.some((f) => f.title.includes('unique'))).toBe(true); }); test('no uniqueness fix when column is pk', () => { @@ -62,13 +62,13 @@ describe('[example] code actions - ref constraint quick fixes', () => { Ref: profiles.user_id - users.id `; const fixes = getQuickFixes(source); - const uniqueFixes = fixes.filter((f) => f.title.includes('[unique]')); + const uniqueFixes = fixes.filter((f) => f.title.includes('unique')); expect(uniqueFixes).toHaveLength(0); }); }); describe('operator change produces correct new op', () => { - test('> with nullable column suggests ?<', () => { + test('> with nullable column suggests >?', () => { const source = ` Table users { id int [pk] } Table posts { user_id int [null] } @@ -76,8 +76,7 @@ describe('[example] code actions - ref constraint quick fixes', () => { `; const fixes = getQuickFixes(source); const opFix = fixes.find((f) => f.title.includes('Change operator')); - // right card (1,1) becomes (0,1), getRelationshipOp(0..1, *) = ?< - expect(opFix?.title).toContain('?<'); + expect(opFix?.title).toContain('>?'); }); test('- with non-unique column suggests >', () => { diff --git a/packages/dbml-parse/src/compiler/index.ts b/packages/dbml-parse/src/compiler/index.ts index 8d81df77e..24fbba417 100644 --- a/packages/dbml-parse/src/compiler/index.ts +++ b/packages/dbml-parse/src/compiler/index.ts @@ -46,7 +46,7 @@ import { symbolUses } from './queries/symbol/symbolUses'; import { type DiagramViewBlock, findDiagramViewBlocks, - renameTable, syncDiagramView, + renameTable, syncDiagramView, addSetting, } from './queries/transform'; import { addDoubleQuoteIfNeeded, escapeString, formatRecordValue, isValidIdentifier, splitQualifiedIdentifier, unescapeString, @@ -388,6 +388,7 @@ export default class Compiler { // transform queries renameTable = renameTable.bind(this); syncDiagramView = syncDiagramView.bind(this); + addSetting = addSetting.bind(this); findDiagramViewBlocks (filepath: Filepath): DiagramViewBlock[] { return findDiagramViewBlocks(this.getSource(filepath) ?? ''); } diff --git a/packages/dbml-parse/src/compiler/queries/transform/index.ts b/packages/dbml-parse/src/compiler/queries/transform/index.ts index bf41f40a3..96e83fa4e 100644 --- a/packages/dbml-parse/src/compiler/queries/transform/index.ts +++ b/packages/dbml-parse/src/compiler/queries/transform/index.ts @@ -6,4 +6,5 @@ export { type DiagramViewBlock, } from './syncDiagramView'; export { applyTextEdits, type TextEdit } from './applyTextEdits'; +export { addSetting, addSettingEdit } from './addSetting'; export { type TableNameInput } from './utils'; diff --git a/packages/dbml-parse/src/core/global_modules/ref/interpret.ts b/packages/dbml-parse/src/core/global_modules/ref/interpret.ts index 0c0ff692f..3cad0cc71 100644 --- a/packages/dbml-parse/src/core/global_modules/ref/interpret.ts +++ b/packages/dbml-parse/src/core/global_modules/ref/interpret.ts @@ -11,7 +11,7 @@ import { FunctionApplicationNode, InfixExpressionNode, IdentifierStreamNode, - type ListExpressionNode, + ListExpressionNode, AttributeNode, PrefixExpressionNode, } from '@/core/types/nodes'; @@ -27,6 +27,7 @@ import { getTokenPosition, } from '@/core/utils/interpret'; import Report from '@/core/types/report'; +import { addSettingEdit } from '@/compiler/queries/transform/addSetting'; import { getMultiplicities, getRelationshipOp, parseCardinality, makeCardinalityOptional, makeCardinalityRequired, makeCardinalityMany, @@ -213,7 +214,7 @@ export class RefInterpreter { if (col.nullable(this.compiler)) { const msg = `Column '${col.name}' is nullable but operator implies mandatory`; const fixes = [ - opToken && suggestChangeOp(opToken, makeCardinalityOptional(thisRel), otherRel), + opToken && suggestChangeOp(opToken, makeCardinalityOptional(thisRel), otherRel, side, `make '${col.name}' optional`), suggestAddSetting(col, 'not null'), ].filter((f): f is QuickFix => !!f); infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, otherNode, fixes)); @@ -227,7 +228,7 @@ export class RefInterpreter { if (col.nullable(this.compiler) === false) { const msg = `Column '${col.name}' is NOT NULL but operator marks it optional`; const fixes = [ - opToken && suggestChangeOp(opToken, makeCardinalityRequired(thisRel), otherRel), + opToken && suggestChangeOp(opToken, makeCardinalityRequired(thisRel), otherRel, side, `make '${col.name}' required`), ].filter((f): f is QuickFix => !!f); infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, otherNode, fixes)); if (col.declaration) infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, col.declaration, fixes)); @@ -240,7 +241,7 @@ export class RefInterpreter { if (!col.unique(this.compiler) && !col.pk(this.compiler)) { const msg = `Column '${col.name}' should be unique or primary key for a one-side relationship`; const fixes = [ - opToken && suggestChangeOp(opToken, makeCardinalityMany(thisRel), otherRel), + opToken && suggestChangeOp(opToken, makeCardinalityMany(thisRel), otherRel, side, `make '${col.name}' many`), suggestAddSetting(col, 'unique'), ].filter((f): f is QuickFix => !!f); infos.push(new CompileInfo(CompileErrorCode.INVALID_REF_RELATIONSHIP, msg, ownNode, fixes)); @@ -252,22 +253,35 @@ export class RefInterpreter { } } -function suggestChangeOp (opToken: SyntaxToken, newThisRel: RelationCardinality, otherRel: RelationCardinality): QuickFix { - const newOp = getRelationshipOp(newThisRel, otherRel); +function suggestChangeOp ( + opToken: SyntaxToken, + newThisRel: RelationCardinality, + otherRel: RelationCardinality, + side: 'left' | 'right', + description: string, +): QuickFix { + const newLeft = side === 'left' ? newThisRel : otherRel; + const newRight = side === 'right' ? newThisRel : otherRel; + const newOp = getRelationshipOp(newLeft, newRight); return { - title: `Change operator to '${newOp}'`, + title: `Change operator to '${newOp}' (${description})`, + filepath: opToken.filepath, edits: [ - { range: { start: opToken.startPos, end: opToken.endPos }, newText: newOp, filepath: opToken.filepath }, + { start: opToken.start, end: opToken.end, newText: newOp }, ], }; } function suggestAddSetting (col: ColumnSymbol, setting: string): QuickFix | undefined { if (!col.declaration) return undefined; + const edit = addSettingEdit(col.declaration, setting); + if (!edit) return undefined; + return { - title: `Add [${setting}] to '${col.name}'`, + title: `Make '${col.name}' ${setting}`, + filepath: col.declaration.filepath, edits: [ - { range: { start: col.declaration.endPos, end: col.declaration.endPos }, newText: ` [${setting}]`, filepath: col.declaration.filepath }, + edit, ], }; } diff --git a/packages/dbml-parse/src/core/types/errors.ts b/packages/dbml-parse/src/core/types/errors.ts index 6580ed4ab..0eddd1bbb 100644 --- a/packages/dbml-parse/src/core/types/errors.ts +++ b/packages/dbml-parse/src/core/types/errors.ts @@ -1,6 +1,5 @@ import { Filepath } from './filepath'; import { SyntaxNode } from '@/core/types/nodes'; -import type { Position } from './position'; import { SyntaxToken } from '@/core/types/tokens'; export enum CompileErrorCode { @@ -204,13 +203,8 @@ export class CompileWarning extends Error { export interface QuickFix { title: string; - edits: TextEdit[]; -} - -export interface TextEdit { - range: { start: Position; end: Position }; - newText: string; filepath: Filepath; + edits: import('@/compiler/queries/transform/applyTextEdits').TextEdit[]; } export class CompileInfo extends Error { diff --git a/packages/dbml-parse/src/services/code_actions/provider.ts b/packages/dbml-parse/src/services/code_actions/provider.ts index e530ca2d8..bda233c63 100644 --- a/packages/dbml-parse/src/services/code_actions/provider.ts +++ b/packages/dbml-parse/src/services/code_actions/provider.ts @@ -1,10 +1,10 @@ import type Compiler from '@/compiler'; import type { CompileInfo, QuickFix } from '@/core/types/errors'; -import { CompileErrorCode } from '@/core/types/errors'; import type { CodeActionProvider, CodeActionList, CodeAction, CodeActionContext, TextModel, Range, CancellationToken, WorkspaceEdit, MarkerData, } from '../types'; +import { Uri } from '../types'; export default class DBMLCodeActionProvider implements CodeActionProvider { private compiler: Compiler; @@ -50,20 +50,26 @@ export default class DBMLCodeActionProvider implements CodeActionProvider { } private quickFixToCodeAction (fix: QuickFix, model: TextModel, marker: MarkerData): CodeAction { + const uri = model.uri; + const resource = Uri.parse(fix.filepath.toUri({ protocol: uri.scheme })); const edit: WorkspaceEdit = { - edits: fix.edits.map((e) => ({ - resource: model.uri, - textEdit: { - range: { - startLineNumber: e.range.start.line + 1, - startColumn: e.range.start.column + 1, - endLineNumber: e.range.end.line + 1, - endColumn: e.range.end.column + 1, + edits: fix.edits.map((e) => { + const startPos = model.getPositionAt(e.start); + const endPos = model.getPositionAt(e.end); + return { + resource, + textEdit: { + range: { + startLineNumber: startPos.lineNumber, + startColumn: startPos.column, + endLineNumber: endPos.lineNumber, + endColumn: endPos.column, + }, + text: e.newText, }, - text: e.newText, - }, - versionId: model.getVersionId(), - })), + versionId: model.getVersionId(), + }; + }), }; return { title: fix.title,