mirror of
https://github.com/chartdb/chartdb.git
synced 2026-02-12 14:39:55 -06:00
fix(dbml-import): use defaultSchemas instead of hardcoded schema values (#1012)
This commit is contained in:
@@ -672,9 +672,9 @@ Table projects {
|
||||
expect(diagram.customTypes).toBeDefined();
|
||||
expect(diagram.customTypes).toHaveLength(3); // job_status, hr.employee_type, grade
|
||||
|
||||
// Check job_status enum
|
||||
// Check job_status enum (PostgreSQL default schema is 'public')
|
||||
const jobStatusEnum = diagram.customTypes?.find(
|
||||
(ct) => ct.name === 'job_status' && !ct.schema
|
||||
(ct) => ct.name === 'job_status' && ct.schema === 'public'
|
||||
);
|
||||
expect(jobStatusEnum).toBeDefined();
|
||||
expect(jobStatusEnum?.kind).toBe(DBCustomTypeKind.enum);
|
||||
@@ -698,9 +698,9 @@ Table projects {
|
||||
'intern',
|
||||
]);
|
||||
|
||||
// Check grade enum with quoted values
|
||||
// Check grade enum with quoted values (PostgreSQL default schema is 'public')
|
||||
const gradeEnum = diagram.customTypes?.find(
|
||||
(ct) => ct.name === 'grade' && !ct.schema
|
||||
(ct) => ct.name === 'grade' && ct.schema === 'public'
|
||||
);
|
||||
expect(gradeEnum).toBeDefined();
|
||||
expect(gradeEnum?.kind).toBe(DBCustomTypeKind.enum);
|
||||
@@ -806,9 +806,9 @@ Table admin.users {
|
||||
// Verify both enums are created
|
||||
expect(diagram.customTypes).toHaveLength(2);
|
||||
|
||||
// Check public.status enum
|
||||
// Check public.status enum (PostgreSQL default schema is 'public')
|
||||
const publicStatusEnum = diagram.customTypes?.find(
|
||||
(ct) => ct.name === 'status' && !ct.schema
|
||||
(ct) => ct.name === 'status' && ct.schema === 'public'
|
||||
);
|
||||
expect(publicStatusEnum).toBeDefined();
|
||||
expect(publicStatusEnum?.values).toEqual([
|
||||
@@ -830,9 +830,9 @@ Table admin.users {
|
||||
]);
|
||||
|
||||
// Verify fields reference correct enums
|
||||
// Note: 'public' schema is converted to empty string
|
||||
// Note: 'public' schema is the default for PostgreSQL
|
||||
const publicUsersTable = diagram.tables?.find(
|
||||
(t) => t.name === 'users' && t.schema === ''
|
||||
(t) => t.name === 'users' && t.schema === 'public'
|
||||
);
|
||||
const adminUsersTable = diagram.tables?.find(
|
||||
(t) => t.name === 'users' && t.schema === 'admin'
|
||||
@@ -1103,7 +1103,7 @@ Table "public_3"."comments" {
|
||||
|
||||
// Note: 'public' schema is converted to empty string
|
||||
const usersTable = diagram.tables?.find(
|
||||
(t) => t.name === 'users' && t.schema === ''
|
||||
(t) => t.name === 'users' && t.schema === 'public'
|
||||
);
|
||||
const postsTable = diagram.tables?.find(
|
||||
(t) => t.name === 'posts' && t.schema === 'public_2'
|
||||
|
||||
@@ -242,4 +242,62 @@ Note note_1750185617764 {
|
||||
getPreferredSynonymSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Schema Handling with defaultSchemas', () => {
|
||||
it('should use defaultSchema when table schema is empty for PostgreSQL', async () => {
|
||||
const dbml = `
|
||||
Table users {
|
||||
id int [pk]
|
||||
}
|
||||
`;
|
||||
|
||||
const diagram = await importDBMLToDiagram(dbml, {
|
||||
databaseType: DatabaseType.POSTGRESQL,
|
||||
});
|
||||
|
||||
expect(diagram.tables?.[0]?.schema).toBe('public');
|
||||
});
|
||||
|
||||
it('should use defaultSchema when table schema is empty for SQL Server', async () => {
|
||||
const dbml = `
|
||||
Table users {
|
||||
id int [pk]
|
||||
}
|
||||
`;
|
||||
|
||||
const diagram = await importDBMLToDiagram(dbml, {
|
||||
databaseType: DatabaseType.SQL_SERVER,
|
||||
});
|
||||
|
||||
expect(diagram.tables?.[0]?.schema).toBe('dbo');
|
||||
});
|
||||
|
||||
it('should have undefined schema for database types without defaultSchema', async () => {
|
||||
const dbml = `
|
||||
Table users {
|
||||
id int [pk]
|
||||
}
|
||||
`;
|
||||
|
||||
const diagram = await importDBMLToDiagram(dbml, {
|
||||
databaseType: DatabaseType.SQLITE,
|
||||
});
|
||||
|
||||
expect(diagram.tables?.[0]?.schema).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should preserve explicit schema even when different from default', async () => {
|
||||
const dbml = `
|
||||
Table "custom_schema"."users" {
|
||||
id int [pk]
|
||||
}
|
||||
`;
|
||||
|
||||
const diagram = await importDBMLToDiagram(dbml, {
|
||||
databaseType: DatabaseType.POSTGRESQL,
|
||||
});
|
||||
|
||||
expect(diagram.tables?.[0]?.schema).toBe('custom_schema');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,10 +39,10 @@ describe('DBML Schema Handling - Fantasy Realm Database', () => {
|
||||
databaseType: DatabaseType.MYSQL,
|
||||
});
|
||||
|
||||
// Verify no 'public' schema was added
|
||||
// Verify schema is undefined for MySQL (no default schema)
|
||||
expect(diagram.tables).toBeDefined();
|
||||
diagram.tables?.forEach((table) => {
|
||||
expect(table.schema).toBe('');
|
||||
expect(table.schema).toBeUndefined();
|
||||
});
|
||||
|
||||
// Check specific tables
|
||||
@@ -50,7 +50,7 @@ describe('DBML Schema Handling - Fantasy Realm Database', () => {
|
||||
(t) => t.name === 'wizards'
|
||||
);
|
||||
expect(wizardsTable).toBeDefined();
|
||||
expect(wizardsTable?.schema).toBe('');
|
||||
expect(wizardsTable?.schema).toBeUndefined();
|
||||
|
||||
// Check that reserved keywords are preserved as field names
|
||||
const yesField = wizardsTable?.fields.find((f) => f.name === 'Yes');
|
||||
@@ -162,7 +162,7 @@ describe('DBML Schema Handling - Fantasy Realm Database', () => {
|
||||
const heroesTable = diagram.tables?.find(
|
||||
(t) => t.name === 'heroes'
|
||||
);
|
||||
expect(heroesTable?.schema).toBe(''); // 'public' should be converted to empty
|
||||
expect(heroesTable?.schema).toBe('public'); // PostgreSQL default schema
|
||||
|
||||
const secretQuestsTable = diagram.tables?.find(
|
||||
(t) => t.name === 'secret_quests'
|
||||
@@ -172,7 +172,7 @@ describe('DBML Schema Handling - Fantasy Realm Database', () => {
|
||||
const artifactsTable = diagram.tables?.find(
|
||||
(t) => t.name === 'artifacts'
|
||||
);
|
||||
expect(artifactsTable?.schema).toBe(''); // No schema = empty string
|
||||
expect(artifactsTable?.schema).toBe('public'); // No schema = default schema
|
||||
});
|
||||
|
||||
it('should handle reserved keywords for PostgreSQL', async () => {
|
||||
@@ -222,18 +222,18 @@ describe('DBML Schema Handling - Fantasy Realm Database', () => {
|
||||
}
|
||||
);
|
||||
|
||||
// For MySQL, 'public' schema should be stripped
|
||||
// For MySQL, 'public' schema should become undefined (no default schema)
|
||||
mysqlDiagram.tables?.forEach((table) => {
|
||||
expect(table.schema).toBe('');
|
||||
expect(table.schema).toBeUndefined();
|
||||
});
|
||||
|
||||
// Now test with PostgreSQL - public should also be stripped (it's the default)
|
||||
// For PostgreSQL, 'public' is the default schema
|
||||
const pgDiagram = await importDBMLToDiagram(dbmlWithPublicSchema, {
|
||||
databaseType: DatabaseType.POSTGRESQL,
|
||||
});
|
||||
|
||||
pgDiagram.tables?.forEach((table) => {
|
||||
expect(table.schema).toBe('');
|
||||
expect(table.schema).toBe('public');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Parser } from '@dbml/core';
|
||||
import type { Diagram } from '@/lib/domain/diagram';
|
||||
import { generateDiagramId, generateId } from '@/lib/utils';
|
||||
import { generateDiagramId, generateId, isStringEmpty } from '@/lib/utils';
|
||||
import type { DBTable } from '@/lib/domain/db-table';
|
||||
import { defaultSchemas } from '@/lib/data/default-schemas';
|
||||
import type { Cardinality, DBRelationship } from '@/lib/domain/db-relationship';
|
||||
import type { DBField } from '@/lib/domain/db-field';
|
||||
import type { DataTypeData } from '@/lib/data/data-types/data-types';
|
||||
@@ -502,14 +503,22 @@ export const importDBMLToDiagram = async (
|
||||
if (schema.enums) {
|
||||
schema.enums.forEach((enumDef) => {
|
||||
// Get schema name from enum or use schema's name
|
||||
const enumSchema =
|
||||
// DBML parser uses 'public' as its default - treat it as empty
|
||||
const rawEnumSchema =
|
||||
typeof enumDef.schema === 'string'
|
||||
? enumDef.schema
|
||||
: enumDef.schema?.name || schema.name;
|
||||
const defaultSchema = defaultSchemas[options.databaseType];
|
||||
const isEnumSchemaEmpty =
|
||||
isStringEmpty(rawEnumSchema) ||
|
||||
rawEnumSchema === 'public';
|
||||
const enumSchema = isEnumSchemaEmpty
|
||||
? defaultSchema
|
||||
: rawEnumSchema;
|
||||
|
||||
allEnums.push({
|
||||
name: enumDef.name,
|
||||
schema: enumSchema === 'public' ? '' : enumSchema,
|
||||
schema: enumSchema,
|
||||
values: enumDef.values || [],
|
||||
note: enumDef.note,
|
||||
});
|
||||
@@ -722,15 +731,21 @@ export const importDBMLToDiagram = async (
|
||||
}
|
||||
}
|
||||
|
||||
// Get raw schema from DBML, then apply defaultSchema if empty
|
||||
// DBML parser uses 'public' as its default - treat it as empty
|
||||
const defaultSchema = defaultSchemas[options.databaseType];
|
||||
const rawSchema =
|
||||
typeof table.schema === 'string'
|
||||
? table.schema
|
||||
: table.schema?.name;
|
||||
const isSchemaEmpty =
|
||||
isStringEmpty(rawSchema) || rawSchema === 'public';
|
||||
const tableSchema = isSchemaEmpty ? defaultSchema : rawSchema;
|
||||
|
||||
const tableToReturn: DBTable = {
|
||||
id: generateId(),
|
||||
name: table.name.replace(/['"]/g, ''),
|
||||
schema:
|
||||
typeof table.schema === 'string'
|
||||
? table.schema === 'public'
|
||||
? ''
|
||||
: table.schema
|
||||
: table.schema?.name || '',
|
||||
schema: tableSchema,
|
||||
order: index,
|
||||
fields,
|
||||
indexes,
|
||||
|
||||
@@ -123,3 +123,7 @@ export function mergeRefs<T>(
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const isStringEmpty = (str: string | undefined | null): boolean => {
|
||||
return !str || str.trim().length === 0;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user