Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions packages/dbml-core/__tests__/examples/exporter/exporter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,46 @@ Ref:"posts"."user_id" ?<? "users"."id"`);
});
});

describe('table partial refs', () => {
test('should preserve optional ref from table partial (nullable FK)', () => {
const input = `
TablePartial auditable {
created_by int [ref: >? users.id]
}
Table users {
id int [pk]
}
Table posts {
id int [pk]
~auditable
}
`.trim();
const res = exporter.export(input, 'dbml');
// >? should export as ?< (flipped direction, optional on the one side)
expect(res.trim()).toContain('?<');
// Should NOT become many-to-many (<>)
expect(res.trim()).not.toContain('<>');
});

test('should preserve required ref from table partial', () => {
const input = `
TablePartial auditable {
created_by int [ref: > users.id]
}
Table users {
id int [pk]
}
Table posts {
id int [pk]
~auditable
}
`.trim();
const res = exporter.export(input, 'dbml');
// > should export as < (flipped direction, required many-to-one)
expect(res.trim()).toMatch(/"users"\."id" < "posts"\."created_by"/);
});
});

describe('sql exporters', () => {
test('mysql exporter should produce FK constraint for optional ref', () => {
const input = `
Expand Down
4 changes: 3 additions & 1 deletion packages/dbml-core/src/export/OracleExporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import {
isNumericType,
isStringType,
} from '@dbml/parse';
import { concat, flatten, forEach, isEmpty } from 'lodash-es';
import {
concat, flatten, forEach, isEmpty,
} from 'lodash-es';
import {
buildJunctionFields1,
buildJunctionFields2,
Expand Down
6 changes: 3 additions & 3 deletions packages/dbml-core/src/export/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { JsonExporterOptions } from './JsonExporter';
export type ExportFormat = 'dbml' | 'mysql' | 'postgres' | 'json' | 'mssql' | 'oracle';

export type ExportOptions =
Partial<DbmlExporterOptions> &
Partial<JsonExporterOptions>;
Partial<DbmlExporterOptions>
& Partial<JsonExporterOptions>;

/**
* @deprecated Passing a boolean as the third argument is deprecated. Use `ExportOptions` instead.
Expand All @@ -29,7 +29,7 @@ function _export (
format: ExportFormat,
options: ExportOptions | boolean = {
isNormalized: true,
includeRecords: true
includeRecords: true,
},
): string {
const resolvedFlags = normalizeExportOptions(options);
Expand Down
5 changes: 3 additions & 2 deletions packages/dbml-core/src/model_structure/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import StickyNote from './stickyNote';
import Table from './table';
import TableGroup from './tableGroup';
import TablePartial from './tablePartial';
import type { RawDatabase, TableRecord, RawTableRecord, NormalizedModel } from '../../types/model_structure/database';
import type {
RawDatabase, TableRecord, RawTableRecord, NormalizedModel,
} from '../../types/model_structure/database';
import type { Token } from '../../types/model_structure/element';
import type { DiagramView } from '@dbml/parse';

Expand Down Expand Up @@ -83,7 +85,6 @@ class Database extends Element {
if (schema.refs.some((r) => r.equals(ref as any))) return;
schema.pushRef(ref);
});

}

private generateId (): void {
Expand Down
6 changes: 4 additions & 2 deletions packages/dbml-core/src/model_structure/table.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { get, isNil } from 'lodash-es';
import { getMultiplicities } from '@dbml/parse';
import Check from './check';
import { DEFAULT_SCHEMA_NAME } from './config';
import Element from './element';
Expand Down Expand Up @@ -188,19 +189,20 @@ class Table extends Element {
// convert inline_refs from injected fields to refs
if (rawField.inline_refs) {
rawField.inline_refs.forEach((iref: any) => {
const multiplicities = getMultiplicities(iref.relation) || ['*', '*'];
const ref = {
token: rawField.token,
endpoints: [{
tableName: this.name,
schemaName: this.schema?.name,
fieldNames: [rawField.name],
relation: ['-', '<'].includes(iref.relation) ? '1' : '*',
relation: multiplicities[0],
token: rawField.token,
}, {
tableName: iref.tableName,
schemaName: iref.schemaName,
fieldNames: iref.fieldNames,
relation: ['-', '>'].includes(iref.relation) ? '1' : '*',
relation: multiplicities[1],
token: iref.token,
}],
injectedPartial: tablePartial,
Expand Down
4 changes: 1 addition & 3 deletions packages/dbml-core/src/parse/ANTLR/ASTGeneration/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,4 @@ function parse (input, format) {
return database;
}

export {
parse,
};
export { parse };
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { flatten, flattenDepth, isEmpty } from 'lodash-es';
import SnowflakeParserVisitor from '../../parsers/snowflake/SnowflakeParserVisitor';
import { Enum, Field, Index, Table, TableRecord } from '../AST';
import { COLUMN_CONSTRAINT_KIND, CONSTRAINT_TYPE, DATA_TYPE, TABLE_CONSTRAINT_KIND } from '../constants';
import {
Enum, Field, Index, Table, TableRecord,
} from '../AST';
import {
COLUMN_CONSTRAINT_KIND, CONSTRAINT_TYPE, DATA_TYPE, TABLE_CONSTRAINT_KIND,
} from '../constants';
import { getOriginalText } from '../helpers';
import { CARDINALITY_MANY, CARDINALITY_MAYBE } from '@dbml/parse';

Expand Down
4 changes: 3 additions & 1 deletion packages/dbml-core/src/parse/Parser.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Compiler, DEFAULT_ENTRY, Filepath, MemoryProjectLayout } from '@dbml/parse';
import {
Compiler, DEFAULT_ENTRY, Filepath, MemoryProjectLayout,
} from '@dbml/parse';
import Database from '../model_structure/database';
import { parse } from './ANTLR/ASTGeneration';
import dbmlParser from './deprecated/dbmlParser.cjs';
Expand Down
4 changes: 1 addition & 3 deletions packages/dbml-core/src/parse/databaseGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,4 @@ const generateDatabase = (schemaJson) => {
}
};

export {
generateDatabase,
};
export { generateDatabase };
6 changes: 4 additions & 2 deletions packages/dbml-parse/__tests__/examples/binder/binder.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { describe, expect } from 'vitest';
import { SyntaxNodeKind, ElementDeclarationNode, BlockExpressionNode, ProgramNode } from '@/core/types/nodes';
import {
SyntaxNodeKind, ElementDeclarationNode, BlockExpressionNode, ProgramNode,
} from '@/core/types/nodes';
import { NodeSymbol, SymbolKind } from '@/core/types/symbol';
import { UNHANDLED } from '@/core/types/module';
import { CompileErrorCode } from '@/core/types/errors';
Expand Down Expand Up @@ -31,7 +33,7 @@ describe('[example] binder', () => {
expect(findMember(compiler, tableSymbol!, SymbolKind.Column, 'id')).toSatisfy((s: any) => s?.isKind(SymbolKind.Column));

// Verify column symbol properties
const columnSymbol = findMember(compiler, tableSymbol!, SymbolKind.Column, 'id')
const columnSymbol = findMember(compiler, tableSymbol!, SymbolKind.Column, 'id');
const tableBody = tableNode.body as BlockExpressionNode;
const columnNode = tableBody.body[0];
expect(columnSymbol!.declaration).toBe(columnNode);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import {
isValidIdentifier, addDoubleQuoteIfNeeded,
} from '@/compiler/index';
import { isValidIdentifier, addDoubleQuoteIfNeeded } from '@/compiler/index';

describe('isValidIdentifier', () => {
test('should return true for simple alphanumeric identifier', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ Table users {
// sentinel must have been cleared on failure
expect(() => {
layout.setSource(fileA, 'Table users { id int }');

compiler.bindProject();
}).not.toThrow();
});
Expand Down
37 changes: 16 additions & 21 deletions packages/dbml-parse/__tests__/examples/compiler/renameTable.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import {
describe, expect, test,
} from 'vitest';
import { describe, expect, test } from 'vitest';
import { DEFAULT_ENTRY } from '@/constants';
import Compiler from '@/compiler/index';
import { MemoryProjectLayout } from '@/compiler/projectLayout/layout';
Expand Down Expand Up @@ -1904,9 +1902,7 @@ Table Users {
});
});


describe('[example] renameTable cross-file', () => {

test('renaming a table updates declaration and same-file references', () => {
// The table 'users' is defined in base.dbml and has a self-referencing FK
const base = `
Expand All @@ -1922,7 +1918,7 @@ Table users {

const result = compiler.renameTable(fp, 'users', 'accounts').get(fp.absolute)!;
expect(result).toContain('Table accounts');
expect(result).toContain('accounts.id'); // ref updated
expect(result).toContain('accounts.id'); // ref updated
expect(result).not.toContain('users');
});

Expand All @@ -1932,7 +1928,7 @@ Table users {
// resolves to the original declaration and rewrites every file that touches it.
const { compiler } = setupCompiler({
'/base.dbml': 'Table users { id int [pk] }',
'/consumer.dbml': `use { table users } from './base.dbml'\nTable orders { user_id int [ref: > users.id] }`,
'/consumer.dbml': 'use { table users } from \'./base.dbml\'\nTable orders { user_id int [ref: > users.id] }',
});

const changes = compiler.renameTable(fp('/consumer.dbml'), 'users', 'accounts');
Expand All @@ -1957,15 +1953,15 @@ Table posts {
user_id int [ref: > users.id]
}
`,
'/main.dbml': `use { table users } from './base.dbml'\nTable orders { user_id int [ref: > users.id] }`,
'/main.dbml': 'use { table users } from \'./base.dbml\'\nTable orders { user_id int [ref: > users.id] }',
});

const changes = compiler.renameTable(fp('/base.dbml'), 'users', 'accounts');
const baseAfter = changes.get(fp('/base.dbml').absolute)!;
const mainAfter = changes.get(fp('/main.dbml').absolute)!;

expect(baseAfter).toContain('Table accounts');
expect(baseAfter).toContain('accounts.id'); // ref in same file updated
expect(baseAfter).toContain('accounts.id'); // ref in same file updated
expect(baseAfter).not.toContain('Table users');

// Cascade reaches the importer: both the use specifier and the inline ref are rewritten.
Expand Down Expand Up @@ -1996,7 +1992,7 @@ Table accounts { id int [pk] }
const compiler = new Compiler(layout);

const changes = compiler.renameTable(fp, 'users', 'accounts');
expect(changes.size).toBe(0); // unchanged - collision detected
expect(changes.size).toBe(0); // unchanged - collision detected
});

test('renaming with schema qualification updates schema-qualified references', () => {
Expand All @@ -2023,7 +2019,7 @@ Table posts {
test('renaming an alias only rewrites the alias-introducing file', () => {
const { compiler } = setupCompiler({
'/base.dbml': 'Table users { id int [pk] }',
'/main.dbml': `use { table users as u } from './base.dbml'\nTable orders { user_id int [ref: > u.id] }`,
'/main.dbml': 'use { table users as u } from \'./base.dbml\'\nTable orders { user_id int [ref: > u.id] }',
});

const changes = compiler.renameTable(fp('/main.dbml'), 'u', 'member');
Expand All @@ -2041,7 +2037,7 @@ Table posts {
// Only the alias 'u' is visible in main.dbml - 'users' is not in scope there.
const { compiler } = setupCompiler({
'/base.dbml': 'Table users { id int [pk] }',
'/main.dbml': `use { table users as u } from './base.dbml'\nTable orders { user_id int [ref: > u.id] }`,
'/main.dbml': 'use { table users as u } from \'./base.dbml\'\nTable orders { user_id int [ref: > u.id] }',
});

const changes = compiler.renameTable(fp('/main.dbml'), 'users', 'accounts');
Expand All @@ -2052,7 +2048,7 @@ Table posts {
test('cross-file rename with alias: source-name token in the use specifier flips, alias stays', () => {
const { compiler } = setupCompiler({
'/base.dbml': 'Table users { id int [pk] }',
'/main.dbml': `use { table users as u } from './base.dbml'\nTable orders { user_id int [ref: > u.id] }`,
'/main.dbml': 'use { table users as u } from \'./base.dbml\'\nTable orders { user_id int [ref: > u.id] }',
});

const changes = compiler.renameTable(fp('/base.dbml'), 'users', 'accounts');
Expand All @@ -2066,7 +2062,6 @@ Table posts {
});

describe('[example] renameTable - alias/use renameability rules', () => {

describe('inline alias (Table users as U) - rename is ignored', () => {
test('renaming by alias single-file is a no-op', () => {
const input = `
Expand Down Expand Up @@ -2112,7 +2107,7 @@ Ref: U.id < U.id
test('rename from the importing file cascades to base + importer', () => {
const { compiler } = setupCompiler({
'/base.dbml': 'Table users { id int [pk] }',
'/main.dbml': `use { table users } from './base.dbml'\nTable orders { user_id int [ref: > users.id] }`,
'/main.dbml': 'use { table users } from \'./base.dbml\'\nTable orders { user_id int [ref: > users.id] }',
});

const changes = compiler.renameTable(fp('/main.dbml'), 'users', 'accounts');
Expand All @@ -2129,8 +2124,8 @@ Ref: U.id < U.id
test('rename from the declaring file cascades to all unaliased importers', () => {
const { compiler } = setupCompiler({
'/base.dbml': 'Table users { id int [pk] }',
'/a.dbml': `use { table users } from './base.dbml'\nTable orders { user_id int [ref: > users.id] }`,
'/b.dbml': `use { table users } from './base.dbml'\nTable carts { user_id int [ref: > users.id] }`,
'/a.dbml': 'use { table users } from \'./base.dbml\'\nTable orders { user_id int [ref: > users.id] }',
'/b.dbml': 'use { table users } from \'./base.dbml\'\nTable carts { user_id int [ref: > users.id] }',
});

const changes = compiler.renameTable(fp('/base.dbml'), 'users', 'accounts');
Expand All @@ -2146,7 +2141,7 @@ Ref: U.id < U.id
test('renaming by the alias only rewrites the alias-introducing file', () => {
const { compiler } = setupCompiler({
'/base.dbml': 'Table users { id int [pk] }',
'/main.dbml': `use { table users as u } from './base.dbml'\nTable orders { user_id int [ref: > u.id] }`,
'/main.dbml': 'use { table users as u } from \'./base.dbml\'\nTable orders { user_id int [ref: > u.id] }',
});

const changes = compiler.renameTable(fp('/main.dbml'), 'u', 'member');
Expand All @@ -2162,8 +2157,8 @@ Ref: U.id < U.id
test('aliased importer is insulated when renaming the original declaration', () => {
const { compiler } = setupCompiler({
'/base.dbml': 'Table users { id int [pk] }',
'/aliased.dbml': `use { table users as u } from './base.dbml'\nTable orders { user_id int [ref: > u.id] }`,
'/unaliased.dbml': `use { table users } from './base.dbml'\nTable carts { user_id int [ref: > users.id] }`,
'/aliased.dbml': 'use { table users as u } from \'./base.dbml\'\nTable orders { user_id int [ref: > u.id] }',
'/unaliased.dbml': 'use { table users } from \'./base.dbml\'\nTable carts { user_id int [ref: > users.id] }',
});

const changes = compiler.renameTable(fp('/base.dbml'), 'users', 'accounts');
Expand All @@ -2182,7 +2177,7 @@ Ref: U.id < U.id
// Only `u` is visible in main - `users` is not in scope.
const { compiler } = setupCompiler({
'/base.dbml': 'Table users { id int [pk] }',
'/main.dbml': `use { table users as u } from './base.dbml'\nTable orders { user_id int [ref: > u.id] }`,
'/main.dbml': 'use { table users as u } from \'./base.dbml\'\nTable orders { user_id int [ref: > u.id] }',
});

const changes = compiler.renameTable(fp('/main.dbml'), 'users', 'accounts');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import {
splitQualifiedIdentifier,
} from '@/compiler/queries/utils';
import { splitQualifiedIdentifier } from '@/compiler/queries/utils';

describe('splitQualifiedIdentifier', () => {
it('should split simple unquoted identifiers', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import {
unescapeString, escapeString,
} from '@/compiler/queries/utils';
import { unescapeString, escapeString } from '@/compiler/queries/utils';

describe('unescapeString', () => {
it('should handle escaped quotes', () => {
Expand Down
Loading
Loading