zenstack/packages/cli/test/db/pull.test.ts

1445 lines
52 KiB
TypeScript
Raw Permalink Normal View History

feat: db pull implementation (#268) * feat: initial db pull implementation * fix: generate imports and attributes for zmodel-code-generator * fix: add option to not exclude imports in loadDocument * fix: continue work on db pull * fix: missing import * fix: rewrite model generation generate model from ground up and diff later * feat: add ast factory * fix: ast factory import order * fix: some runtime bugs * fix: lint fix * fix: update zmodel code generator - include imports in output - fix indentaions - include comments in output * feat: add exclude schemas option * feat: implement initial diff update * fix: update format in zmodel code generator * fix: typo * feat: progress on database introspection and syncing * fix: make ignore behave it does in prisma with no index models * fix: lint fix * feat: make all format options configurable * fix: lint fix * feat: Handle the database type mapping * fix: catch up with feature updates - improve code styling - enable schema support for db pull * fix: add sqlite e2e test and fix some bugs * fix: lint fix * fix: formatting for e2e test schemas * test: run db pull e2e test also for postgres * fix: postgres instorspection schema filter * test: update cli tests * feat(cli): Improves database introspection and syncing Enhances the `db pull` command with a spinner for better UX. Adds color-coded logging to highlight important steps. Provides more detailed output on schema changes, including deleted models, enums, added fields, and deleted attributes. Also includes minor improvements to enum mapping and constraint handling. * fix(cli): fixes field casing and sort issues * chore(cli): remove temporary test script Deletes an unused script used for experimenting with URI path resolution. Cleans up the codebase by removing development-only artifacts. * chore: update pnpm-lock.yaml * feat(cli): add MySQL support for schema introspection Introduces a MySQL-specific introspection provider to support pulling existing database schemas into ZenStack. The implementation includes logic for mapping MySQL data types to ZenStack types, handling auto-incrementing fields, and parsing MySQL-specific enum definitions. It utilizes dynamic imports for database drivers to minimize the CLI footprint for users not targeting MySQL. * fix(cli): improve field matching logic during db pull * feat(cli): enhance SQLite introspection with autoincrement support * fix(cli): refine attribute generation in db pull * test(cli): update db pull tests for SQLite specific behavior * refactor(language): export ZModelServices type * fix(cli): improve sqlite introspection for autoincrement and fk names * feat(cli): enhance field matching logic during pull by using relation fields * refactor(cli): refine relation name generation and table syncing * test(cli): update pull tests to reflect improved schema generation * test(cli): add MySQL support to test utility helpers Extends the testing infrastructure to support MySQL databases. Adds MySQL configuration defaults and environment variable overrides. Updates the prelude generation logic to handle MySQL connection strings and provider types, enabling broader database integration testing across the CLI. * fix(cli): omit default constraint names in table sync Avoids explicitly declaring unique constraint names when they match the default database naming convention. This results in cleaner generated schema code by removing redundant mapping arguments. * fix: correctly handle default values for 'text' type in PostgreSQL * fix: sort table indexes to ensure stable schema generation * refactor: dynamically determine supported db providers in CLI * test: fix typo in pull test description * chore(cli): remove debug artifacts and silence test logs Removes hardcoded file system path debugging and unnecessary console logging from the introspector and test suites. Silences CLI command output during tests to provide a cleaner test execution environment. * fix(cli): ensure MySQL column and index ordering Wraps JSON_ARRAYAGG calls in subqueries with explicit ORDER BY clauses to maintain correct metadata ordering. This addresses a limitation in MySQL versions prior to 8.0.21, where ORDER BY is not supported directly within the JSON_ARRAYAGG function, ensuring consistent introspection results across different database versions. * fix(cli): preserve column order during MySQL pull Ensures database columns are sorted by their ordinal position during the introspection process. This maintains the original schema structure and provides a consistent output that matches the physical database layout. * refactor(cli): remove schema fields from MySQL queries Eliminates redundant schema and database name fields from the MySQL introspection query. Since MySQL does not support multi-schema architectures internal to a single connection in this context, removing these fields simplifies the data structure and avoids unnecessary metadata overhead. * fix(cli): improve MySQL introspection and index mapping Refines the database pull process to better handle MySQL-specific patterns. Improves unique constraint detection to prevent redundant mapping attributes when default naming conventions are used. Updates the MySQL introspection logic to correctly identify boolean types, handle timestamp precision in default values, and normalize numeric defaults. Also ensures auto-incrementing columns and primary key indexes are correctly mapped to prevent schema duplication. * test(cli): pass provider to default prelude in tests Ensures that the default schema prelude correctly reflects the database provider specified in test options. This prevents inconsistencies when generating test projects with non-default providers. * fix(cli): improve MySQL introspection for types and defaults Disables NativeEnum support for MySQL to prevent loss of schema-level enums since MySQL enums are column-specific. Refines boolean and numeric type mapping to better handle synthetic boolean types and preserve decimal precision in default values. Updates default value parsing logic to correctly identify unquoted strings and avoid misinterpreting numeric literals as booleans. * fix(cli): improve MySQL default value introspection Refines how default values are handled during database introspection for MySQL by considering the specific field type. This ensures that boolean variants and numeric literals for Float and Decimal types are correctly formatted and preserved. Also clarifies unsupported features in the SQLite provider to improve codebase maintainability. * test(cli): expand and reorganize db pull tests Enhances the test suite for the database pull command by adding comprehensive coverage for common schema features and PostgreSQL-specific functionality. Includes new test cases for: - Restoring complex schemas from scratch, including relations and indexes - Preserving existing imports in multi-file schema setups - Handling PostgreSQL-specific features like multi-schema support and native enums - Verifying schema preservation for field and table mappings The tests are restructured for better clarity across different database providers. * refactor: restructure introspection provider interface and attribute generation * feat: modernize MySQL introspection provider * feat: modernize PostgreSQL introspection provider * feat: modernize SQLite introspection provider * fix: improve relation field naming and default action handling * feat: track imports and auto-format during db pull * test: update pull tests to reflect naming and formatting improvements * fix(cli): refactor PostgreSQL type casting and fix index order Extracts PostgreSQL type casting logic into a reusable helper function to improve maintainability and ensure consistent attribute handling across all field types. Adjusts the table index sorting logic to better preserve the original database creation order while maintaining the priority of unique indexes. * fix(cli): filter out auto-generated MySQL indexes Prevents foreign key indexes created automatically by MySQL from appearing in the introspected schema. This ensures the output reflects manually defined indexes and avoids redundancy in schema definitions. * test(cli): support datasource extras in test utils Enhances the test utility helpers to allow passing extra datasource properties, such as multi-schema configurations for PostgreSQL. Refactors existing database pull tests to use these extra properties, ensuring the generated ZModel schema correctly reflects multi-schema environments while simplifying assertions. * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix(cli): improve file path resolution in pull action * refactor(cli): extract and enhance name casing logic * refactor(cli): consolidate default value normalization * feat(cli): improve enum syncing and relation naming during pull * docs(cli): add documentation comments to SQL introspection queries * test(cli): refactor test utilities and modernize test suites * fix(cli): improve db pull for composite FKs and MySQL uniqueness Enhances database introspection to correctly handle composite foreign keys by mapping columns by position rather than name alone. Improves MySQL introspection by checking statistics tables for single-column unique indexes, ensuring accurate model generation even when column keys are ambiguous. Ensures MySQL synthetic enum names respect requested model casing to prevent unnecessary schema mapping. Adds comprehensive tests for composite relations and database-specific uniqueness detection. * fix: address PR comments * fix(cli): improve SQLite introspection for untyped columns and composite FKs Ensures columns with no declared type are correctly mapped to Bytes following SQLite affinity rules, preventing them from being marked as Unsupported. Updates the DDL parser to correctly identify and map constraint names for composite foreign keys. This ensures that multi-column relations are properly restored during the pull process. Adds regression tests for both untyped columns and composite foreign key restoration. * feat(cli): pull generated/computed columns as Unsupported type Improves database introspection by identifying generated columns in MySQL, PostgreSQL, and SQLite. These columns are now pulled as `Unsupported` types containing their full DDL definition, preventing issues where read-only database fields were incorrectly treated as writable application-level fields. Includes normalization for expression formatting and a fix for string literal escaping in the code generator to ensure stable schema output. Relates to ZModel introspection consistency. * fix(cli): Use parameterized queries for MySQL introspection Switches from template literal interpolation to parameterized queries in MySQL introspection functions. This improves security by preventing potential SQL injection and ensures better handling of database names containing special characters. * fix(cli): use nullish coalescing for precision check
2026-02-08 02:02:26 +00:00
import fs from 'node:fs';
import path from 'node:path';
import { describe, expect, it } from 'vitest';
import { createProject, getDefaultPrelude, getTestDbName, getTestDbUrl, runCli } from '../utils';
import { formatDocument } from '@zenstackhq/language';
import { getTestDbProvider } from '@zenstackhq/testtools';
const getSchema = (workDir: string) => fs.readFileSync(path.join(workDir, 'zenstack/schema.zmodel')).toString();
describe('DB pull - Common features (all providers)', () => {
describe('Pull from zero - restore complete schema from database', () => {
it('should restore basic schema with all supported types', async () => {
const { workDir, schema } = await createProject(
`model User {
id Int @id @default(autoincrement())
email String @unique
name String?
age Int @default(0)
balance Decimal @default(0.00)
isActive Boolean @default(true)
bigCounter BigInt @default(0)
score Float @default(0.0)
bio String?
avatar Bytes?
metadata Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}`,
);
runCli('db push', workDir);
// Store the schema after db push (this is what provider names will be)
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
// Remove schema content to simulate restoration from zero
fs.writeFileSync(schemaFile, getDefaultPrelude());
// Pull should fully restore the schema
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
it('should restore schema with relations', async () => {
const { workDir, schema } = await createProject(
`model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId Int
}
model User {
id Int @id @default(autoincrement())
email String @unique
posts Post[]
}`,
);
runCli('db push', workDir);
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude());
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
it('should restore schema with many-to-many relations', async () => {
const { workDir, schema } = await createProject(
`model Post {
id Int @id @default(autoincrement())
title String
postTags PostTag[]
}
model PostTag {
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
postId Int
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
tagId Int
@@id([postId, tagId])
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
postTags PostTag[]
}`,
);
runCli('db push', workDir);
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude());
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
it('should restore self-referencing model with multiple FK columns without duplicate fields', async () => {
const { workDir, schema } = await createProject(
`model Category {
id Int @id @default(autoincrement())
categoryParentId Category? @relation('Category_parentIdToCategory', fields: [parentId], references: [id])
parentId Int?
categoryBuddyId Category? @relation('Category_buddyIdToCategory', fields: [buddyId], references: [id])
buddyId Int?
categoryMentorId Category? @relation('Category_mentorIdToCategory', fields: [mentorId], references: [id])
mentorId Int?
categoryParentIdToCategoryId Category[] @relation('Category_parentIdToCategory')
categoryBuddyIdToCategoryId Category[] @relation('Category_buddyIdToCategory')
categoryMentorIdToCategoryId Category[] @relation('Category_mentorIdToCategory')
}`,
);
runCli('db push', workDir);
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude());
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
it('should preserve self-referencing model with multiple FK columns', async () => {
const { workDir, schema } = await createProject(
`model Category {
id Int @id @default(autoincrement())
category Category? @relation('Category_parentIdToCategory', fields: [parentId], references: [id])
parentId Int?
buddy Category? @relation('Category_buddyIdToCategory', fields: [buddyId], references: [id])
buddyId Int?
mentor Category? @relation('Category_mentorIdToCategory', fields: [mentorId], references: [id])
mentorId Int?
categories Category[] @relation('Category_parentIdToCategory')
buddys Category[] @relation('Category_buddyIdToCategory')
mentees Category[] @relation('Category_mentorIdToCategory')
}`,
);
runCli('db push', workDir);
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
it('should restore opposite relation fields when multiple models have FKs to the same target', async () => {
const { workDir, schema } = await createProject(
`model Comment {
id Int @id @default(autoincrement())
text String
commentCreatedBy User? @relation('Comment_createdByToUser', fields: [createdBy], references: [id])
createdBy Int?
commentUpdatedBy User? @relation('Comment_updatedByToUser', fields: [updatedBy], references: [id])
updatedBy Int?
}
model Post {
id Int @id @default(autoincrement())
title String
postCreatedBy User? @relation('Post_createdByToUser', fields: [createdBy], references: [id])
createdBy Int?
postUpdatedBy User? @relation('Post_updatedByToUser', fields: [updatedBy], references: [id])
updatedBy Int?
}
model User {
id Int @id @default(autoincrement())
email String @unique
commentCreatedBy Comment[] @relation('Comment_createdByToUser')
commentUpdatedBy Comment[] @relation('Comment_updatedByToUser')
postCreatedBy Post[] @relation('Post_createdByToUser')
postUpdatedBy Post[] @relation('Post_updatedByToUser')
}`,
);
runCli('db push', workDir);
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude());
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
it('should preserve opposite relation fields when multiple models have FKs to the same target', async () => {
const { workDir, schema } = await createProject(
`model Comment {
id Int @id @default(autoincrement())
text String
commentCreatedBy User? @relation('Comment_createdByToUser', fields: [createdBy], references: [id])
createdBy Int?
commentUpdatedBy User? @relation('Comment_updatedByToUser', fields: [updatedBy], references: [id])
updatedBy Int?
}
model Post {
id Int @id @default(autoincrement())
title String
postCreatedBy User? @relation('Post_createdByToUser', fields: [createdBy], references: [id])
createdBy Int?
postUpdatedBy User? @relation('Post_updatedByToUser', fields: [updatedBy], references: [id])
updatedBy Int?
}
model User {
id Int @id @default(autoincrement())
email String @unique
commentCreatedBy Comment[] @relation('Comment_createdByToUser')
commentUpdatedBy Comment[] @relation('Comment_updatedByToUser')
postCreatedBy Post[] @relation('Post_createdByToUser')
postUpdatedBy Post[] @relation('Post_updatedByToUser')
}`,
);
runCli('db push', workDir);
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
feat: db pull implementation (#268) * feat: initial db pull implementation * fix: generate imports and attributes for zmodel-code-generator * fix: add option to not exclude imports in loadDocument * fix: continue work on db pull * fix: missing import * fix: rewrite model generation generate model from ground up and diff later * feat: add ast factory * fix: ast factory import order * fix: some runtime bugs * fix: lint fix * fix: update zmodel code generator - include imports in output - fix indentaions - include comments in output * feat: add exclude schemas option * feat: implement initial diff update * fix: update format in zmodel code generator * fix: typo * feat: progress on database introspection and syncing * fix: make ignore behave it does in prisma with no index models * fix: lint fix * feat: make all format options configurable * fix: lint fix * feat: Handle the database type mapping * fix: catch up with feature updates - improve code styling - enable schema support for db pull * fix: add sqlite e2e test and fix some bugs * fix: lint fix * fix: formatting for e2e test schemas * test: run db pull e2e test also for postgres * fix: postgres instorspection schema filter * test: update cli tests * feat(cli): Improves database introspection and syncing Enhances the `db pull` command with a spinner for better UX. Adds color-coded logging to highlight important steps. Provides more detailed output on schema changes, including deleted models, enums, added fields, and deleted attributes. Also includes minor improvements to enum mapping and constraint handling. * fix(cli): fixes field casing and sort issues * chore(cli): remove temporary test script Deletes an unused script used for experimenting with URI path resolution. Cleans up the codebase by removing development-only artifacts. * chore: update pnpm-lock.yaml * feat(cli): add MySQL support for schema introspection Introduces a MySQL-specific introspection provider to support pulling existing database schemas into ZenStack. The implementation includes logic for mapping MySQL data types to ZenStack types, handling auto-incrementing fields, and parsing MySQL-specific enum definitions. It utilizes dynamic imports for database drivers to minimize the CLI footprint for users not targeting MySQL. * fix(cli): improve field matching logic during db pull * feat(cli): enhance SQLite introspection with autoincrement support * fix(cli): refine attribute generation in db pull * test(cli): update db pull tests for SQLite specific behavior * refactor(language): export ZModelServices type * fix(cli): improve sqlite introspection for autoincrement and fk names * feat(cli): enhance field matching logic during pull by using relation fields * refactor(cli): refine relation name generation and table syncing * test(cli): update pull tests to reflect improved schema generation * test(cli): add MySQL support to test utility helpers Extends the testing infrastructure to support MySQL databases. Adds MySQL configuration defaults and environment variable overrides. Updates the prelude generation logic to handle MySQL connection strings and provider types, enabling broader database integration testing across the CLI. * fix(cli): omit default constraint names in table sync Avoids explicitly declaring unique constraint names when they match the default database naming convention. This results in cleaner generated schema code by removing redundant mapping arguments. * fix: correctly handle default values for 'text' type in PostgreSQL * fix: sort table indexes to ensure stable schema generation * refactor: dynamically determine supported db providers in CLI * test: fix typo in pull test description * chore(cli): remove debug artifacts and silence test logs Removes hardcoded file system path debugging and unnecessary console logging from the introspector and test suites. Silences CLI command output during tests to provide a cleaner test execution environment. * fix(cli): ensure MySQL column and index ordering Wraps JSON_ARRAYAGG calls in subqueries with explicit ORDER BY clauses to maintain correct metadata ordering. This addresses a limitation in MySQL versions prior to 8.0.21, where ORDER BY is not supported directly within the JSON_ARRAYAGG function, ensuring consistent introspection results across different database versions. * fix(cli): preserve column order during MySQL pull Ensures database columns are sorted by their ordinal position during the introspection process. This maintains the original schema structure and provides a consistent output that matches the physical database layout. * refactor(cli): remove schema fields from MySQL queries Eliminates redundant schema and database name fields from the MySQL introspection query. Since MySQL does not support multi-schema architectures internal to a single connection in this context, removing these fields simplifies the data structure and avoids unnecessary metadata overhead. * fix(cli): improve MySQL introspection and index mapping Refines the database pull process to better handle MySQL-specific patterns. Improves unique constraint detection to prevent redundant mapping attributes when default naming conventions are used. Updates the MySQL introspection logic to correctly identify boolean types, handle timestamp precision in default values, and normalize numeric defaults. Also ensures auto-incrementing columns and primary key indexes are correctly mapped to prevent schema duplication. * test(cli): pass provider to default prelude in tests Ensures that the default schema prelude correctly reflects the database provider specified in test options. This prevents inconsistencies when generating test projects with non-default providers. * fix(cli): improve MySQL introspection for types and defaults Disables NativeEnum support for MySQL to prevent loss of schema-level enums since MySQL enums are column-specific. Refines boolean and numeric type mapping to better handle synthetic boolean types and preserve decimal precision in default values. Updates default value parsing logic to correctly identify unquoted strings and avoid misinterpreting numeric literals as booleans. * fix(cli): improve MySQL default value introspection Refines how default values are handled during database introspection for MySQL by considering the specific field type. This ensures that boolean variants and numeric literals for Float and Decimal types are correctly formatted and preserved. Also clarifies unsupported features in the SQLite provider to improve codebase maintainability. * test(cli): expand and reorganize db pull tests Enhances the test suite for the database pull command by adding comprehensive coverage for common schema features and PostgreSQL-specific functionality. Includes new test cases for: - Restoring complex schemas from scratch, including relations and indexes - Preserving existing imports in multi-file schema setups - Handling PostgreSQL-specific features like multi-schema support and native enums - Verifying schema preservation for field and table mappings The tests are restructured for better clarity across different database providers. * refactor: restructure introspection provider interface and attribute generation * feat: modernize MySQL introspection provider * feat: modernize PostgreSQL introspection provider * feat: modernize SQLite introspection provider * fix: improve relation field naming and default action handling * feat: track imports and auto-format during db pull * test: update pull tests to reflect naming and formatting improvements * fix(cli): refactor PostgreSQL type casting and fix index order Extracts PostgreSQL type casting logic into a reusable helper function to improve maintainability and ensure consistent attribute handling across all field types. Adjusts the table index sorting logic to better preserve the original database creation order while maintaining the priority of unique indexes. * fix(cli): filter out auto-generated MySQL indexes Prevents foreign key indexes created automatically by MySQL from appearing in the introspected schema. This ensures the output reflects manually defined indexes and avoids redundancy in schema definitions. * test(cli): support datasource extras in test utils Enhances the test utility helpers to allow passing extra datasource properties, such as multi-schema configurations for PostgreSQL. Refactors existing database pull tests to use these extra properties, ensuring the generated ZModel schema correctly reflects multi-schema environments while simplifying assertions. * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix(cli): improve file path resolution in pull action * refactor(cli): extract and enhance name casing logic * refactor(cli): consolidate default value normalization * feat(cli): improve enum syncing and relation naming during pull * docs(cli): add documentation comments to SQL introspection queries * test(cli): refactor test utilities and modernize test suites * fix(cli): improve db pull for composite FKs and MySQL uniqueness Enhances database introspection to correctly handle composite foreign keys by mapping columns by position rather than name alone. Improves MySQL introspection by checking statistics tables for single-column unique indexes, ensuring accurate model generation even when column keys are ambiguous. Ensures MySQL synthetic enum names respect requested model casing to prevent unnecessary schema mapping. Adds comprehensive tests for composite relations and database-specific uniqueness detection. * fix: address PR comments * fix(cli): improve SQLite introspection for untyped columns and composite FKs Ensures columns with no declared type are correctly mapped to Bytes following SQLite affinity rules, preventing them from being marked as Unsupported. Updates the DDL parser to correctly identify and map constraint names for composite foreign keys. This ensures that multi-column relations are properly restored during the pull process. Adds regression tests for both untyped columns and composite foreign key restoration. * feat(cli): pull generated/computed columns as Unsupported type Improves database introspection by identifying generated columns in MySQL, PostgreSQL, and SQLite. These columns are now pulled as `Unsupported` types containing their full DDL definition, preventing issues where read-only database fields were incorrectly treated as writable application-level fields. Includes normalization for expression formatting and a fix for string literal escaping in the code generator to ensure stable schema output. Relates to ZModel introspection consistency. * fix(cli): Use parameterized queries for MySQL introspection Switches from template literal interpolation to parameterized queries in MySQL introspection functions. This improves security by preventing potential SQL injection and ensures better handling of database names containing special characters. * fix(cli): use nullish coalescing for precision check
2026-02-08 02:02:26 +00:00
it('should restore one-to-one relation when FK is the single-column primary key', async () => {
const { workDir, schema } = await createProject(
`model Profile {
user User @relation(fields: [id], references: [id], onDelete: Cascade)
id Int @id @default(autoincrement())
bio String?
}
model User {
id Int @id @default(autoincrement())
email String @unique
profile Profile?
}`,
);
runCli('db push', workDir);
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude());
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
it('should restore schema with indexes and unique constraints', async () => {
const { workDir, schema } = await createProject(
`model User {
id Int @id @default(autoincrement())
email String @unique
username String
firstName String
lastName String
role String
@@unique([username, email])
@@index([role])
@@index([firstName, lastName])
@@index([email, username, role])
}`,
);
runCli('db push', workDir);
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude());
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
it('should restore schema with composite primary keys', async () => {
const { workDir, schema } = await createProject(
`model UserRole {
userId String
role String
grantedAt DateTime @default(now())
@@id([userId, role])
}`,
);
runCli('db push', workDir);
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude());
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
it('should preserve Decimal and Float default value precision', async () => {
const { workDir, schema } = await createProject(
`model Product {
id Int @id @default(autoincrement())
price Decimal @default(99.99)
discount Decimal @default(0.50)
taxRate Decimal @default(7.00)
weight Float @default(1.5)
rating Float @default(4.0)
temperature Float @default(98.6)
}`,
);
runCli('db push', workDir);
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude());
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
});
describe('Pull with existing schema - preserve schema features', () => {
it('should preserve field and table mappings', async () => {
const { workDir, schema } = await createProject(
`model User {
id Int @id @default(autoincrement())
email String @unique @map('email_address')
firstName String @map('first_name')
lastName String @map('last_name')
@@map('users')
}`,
);
runCli('db push', workDir);
runCli('db pull --indent 4', workDir);
expect(getSchema(workDir)).toEqual(schema);
});
it('should not modify a comprehensive schema with all features', async () => {
const { workDir, schema } = await createProject(`model User {
id Int @id @default(autoincrement())
email String @unique @map('email_address')
name String? @default('Anonymous')
role Role @default(USER)
feat: db pull implementation (#268) * feat: initial db pull implementation * fix: generate imports and attributes for zmodel-code-generator * fix: add option to not exclude imports in loadDocument * fix: continue work on db pull * fix: missing import * fix: rewrite model generation generate model from ground up and diff later * feat: add ast factory * fix: ast factory import order * fix: some runtime bugs * fix: lint fix * fix: update zmodel code generator - include imports in output - fix indentaions - include comments in output * feat: add exclude schemas option * feat: implement initial diff update * fix: update format in zmodel code generator * fix: typo * feat: progress on database introspection and syncing * fix: make ignore behave it does in prisma with no index models * fix: lint fix * feat: make all format options configurable * fix: lint fix * feat: Handle the database type mapping * fix: catch up with feature updates - improve code styling - enable schema support for db pull * fix: add sqlite e2e test and fix some bugs * fix: lint fix * fix: formatting for e2e test schemas * test: run db pull e2e test also for postgres * fix: postgres instorspection schema filter * test: update cli tests * feat(cli): Improves database introspection and syncing Enhances the `db pull` command with a spinner for better UX. Adds color-coded logging to highlight important steps. Provides more detailed output on schema changes, including deleted models, enums, added fields, and deleted attributes. Also includes minor improvements to enum mapping and constraint handling. * fix(cli): fixes field casing and sort issues * chore(cli): remove temporary test script Deletes an unused script used for experimenting with URI path resolution. Cleans up the codebase by removing development-only artifacts. * chore: update pnpm-lock.yaml * feat(cli): add MySQL support for schema introspection Introduces a MySQL-specific introspection provider to support pulling existing database schemas into ZenStack. The implementation includes logic for mapping MySQL data types to ZenStack types, handling auto-incrementing fields, and parsing MySQL-specific enum definitions. It utilizes dynamic imports for database drivers to minimize the CLI footprint for users not targeting MySQL. * fix(cli): improve field matching logic during db pull * feat(cli): enhance SQLite introspection with autoincrement support * fix(cli): refine attribute generation in db pull * test(cli): update db pull tests for SQLite specific behavior * refactor(language): export ZModelServices type * fix(cli): improve sqlite introspection for autoincrement and fk names * feat(cli): enhance field matching logic during pull by using relation fields * refactor(cli): refine relation name generation and table syncing * test(cli): update pull tests to reflect improved schema generation * test(cli): add MySQL support to test utility helpers Extends the testing infrastructure to support MySQL databases. Adds MySQL configuration defaults and environment variable overrides. Updates the prelude generation logic to handle MySQL connection strings and provider types, enabling broader database integration testing across the CLI. * fix(cli): omit default constraint names in table sync Avoids explicitly declaring unique constraint names when they match the default database naming convention. This results in cleaner generated schema code by removing redundant mapping arguments. * fix: correctly handle default values for 'text' type in PostgreSQL * fix: sort table indexes to ensure stable schema generation * refactor: dynamically determine supported db providers in CLI * test: fix typo in pull test description * chore(cli): remove debug artifacts and silence test logs Removes hardcoded file system path debugging and unnecessary console logging from the introspector and test suites. Silences CLI command output during tests to provide a cleaner test execution environment. * fix(cli): ensure MySQL column and index ordering Wraps JSON_ARRAYAGG calls in subqueries with explicit ORDER BY clauses to maintain correct metadata ordering. This addresses a limitation in MySQL versions prior to 8.0.21, where ORDER BY is not supported directly within the JSON_ARRAYAGG function, ensuring consistent introspection results across different database versions. * fix(cli): preserve column order during MySQL pull Ensures database columns are sorted by their ordinal position during the introspection process. This maintains the original schema structure and provides a consistent output that matches the physical database layout. * refactor(cli): remove schema fields from MySQL queries Eliminates redundant schema and database name fields from the MySQL introspection query. Since MySQL does not support multi-schema architectures internal to a single connection in this context, removing these fields simplifies the data structure and avoids unnecessary metadata overhead. * fix(cli): improve MySQL introspection and index mapping Refines the database pull process to better handle MySQL-specific patterns. Improves unique constraint detection to prevent redundant mapping attributes when default naming conventions are used. Updates the MySQL introspection logic to correctly identify boolean types, handle timestamp precision in default values, and normalize numeric defaults. Also ensures auto-incrementing columns and primary key indexes are correctly mapped to prevent schema duplication. * test(cli): pass provider to default prelude in tests Ensures that the default schema prelude correctly reflects the database provider specified in test options. This prevents inconsistencies when generating test projects with non-default providers. * fix(cli): improve MySQL introspection for types and defaults Disables NativeEnum support for MySQL to prevent loss of schema-level enums since MySQL enums are column-specific. Refines boolean and numeric type mapping to better handle synthetic boolean types and preserve decimal precision in default values. Updates default value parsing logic to correctly identify unquoted strings and avoid misinterpreting numeric literals as booleans. * fix(cli): improve MySQL default value introspection Refines how default values are handled during database introspection for MySQL by considering the specific field type. This ensures that boolean variants and numeric literals for Float and Decimal types are correctly formatted and preserved. Also clarifies unsupported features in the SQLite provider to improve codebase maintainability. * test(cli): expand and reorganize db pull tests Enhances the test suite for the database pull command by adding comprehensive coverage for common schema features and PostgreSQL-specific functionality. Includes new test cases for: - Restoring complex schemas from scratch, including relations and indexes - Preserving existing imports in multi-file schema setups - Handling PostgreSQL-specific features like multi-schema support and native enums - Verifying schema preservation for field and table mappings The tests are restructured for better clarity across different database providers. * refactor: restructure introspection provider interface and attribute generation * feat: modernize MySQL introspection provider * feat: modernize PostgreSQL introspection provider * feat: modernize SQLite introspection provider * fix: improve relation field naming and default action handling * feat: track imports and auto-format during db pull * test: update pull tests to reflect naming and formatting improvements * fix(cli): refactor PostgreSQL type casting and fix index order Extracts PostgreSQL type casting logic into a reusable helper function to improve maintainability and ensure consistent attribute handling across all field types. Adjusts the table index sorting logic to better preserve the original database creation order while maintaining the priority of unique indexes. * fix(cli): filter out auto-generated MySQL indexes Prevents foreign key indexes created automatically by MySQL from appearing in the introspected schema. This ensures the output reflects manually defined indexes and avoids redundancy in schema definitions. * test(cli): support datasource extras in test utils Enhances the test utility helpers to allow passing extra datasource properties, such as multi-schema configurations for PostgreSQL. Refactors existing database pull tests to use these extra properties, ensuring the generated ZModel schema correctly reflects multi-schema environments while simplifying assertions. * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix(cli): improve file path resolution in pull action * refactor(cli): extract and enhance name casing logic * refactor(cli): consolidate default value normalization * feat(cli): improve enum syncing and relation naming during pull * docs(cli): add documentation comments to SQL introspection queries * test(cli): refactor test utilities and modernize test suites * fix(cli): improve db pull for composite FKs and MySQL uniqueness Enhances database introspection to correctly handle composite foreign keys by mapping columns by position rather than name alone. Improves MySQL introspection by checking statistics tables for single-column unique indexes, ensuring accurate model generation even when column keys are ambiguous. Ensures MySQL synthetic enum names respect requested model casing to prevent unnecessary schema mapping. Adds comprehensive tests for composite relations and database-specific uniqueness detection. * fix: address PR comments * fix(cli): improve SQLite introspection for untyped columns and composite FKs Ensures columns with no declared type are correctly mapped to Bytes following SQLite affinity rules, preventing them from being marked as Unsupported. Updates the DDL parser to correctly identify and map constraint names for composite foreign keys. This ensures that multi-column relations are properly restored during the pull process. Adds regression tests for both untyped columns and composite foreign key restoration. * feat(cli): pull generated/computed columns as Unsupported type Improves database introspection by identifying generated columns in MySQL, PostgreSQL, and SQLite. These columns are now pulled as `Unsupported` types containing their full DDL definition, preventing issues where read-only database fields were incorrectly treated as writable application-level fields. Includes normalization for expression formatting and a fix for string literal escaping in the code generator to ensure stable schema output. Relates to ZModel introspection consistency. * fix(cli): Use parameterized queries for MySQL introspection Switches from template literal interpolation to parameterized queries in MySQL introspection functions. This improves security by preventing potential SQL injection and ensures better handling of database names containing special characters. * fix(cli): use nullish coalescing for precision check
2026-02-08 02:02:26 +00:00
profile Profile?
shared_profile Profile? @relation('shared')
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
jsonData Json?
balance Decimal @default(0.00)
isActive Boolean @default(true)
bigCounter BigInt @default(0)
bytes Bytes?
@@index([role])
@@map('users')
}
model Profile {
id Int @id @default(autoincrement())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId Int @unique
user_shared User @relation('shared', fields: [shared_userId], references: [id], onDelete: Cascade)
shared_userId Int @unique
bio String?
avatarUrl String?
@@map('profiles')
}
model Post {
id Int @id @default(autoincrement())
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId Int
title String
content String?
published Boolean @default(false)
tags PostTag[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
slug String
score Float @default(0.0)
metadata Json?
@@unique([authorId, slug])
@@index([authorId, published])
@@map('posts')
}
model Tag {
id Int @id @default(autoincrement())
name String @unique
posts PostTag[]
createdAt DateTime @default(now())
@@index([name], name: 'tag_name_idx')
@@map('tags')
}
model PostTag {
post Post @relation(fields: [postId], references: [id], onDelete: Cascade)
postId Int
tag Tag @relation(fields: [tagId], references: [id], onDelete: Cascade)
tagId Int
assignedAt DateTime @default(now())
note String? @default('initial')
@@id([postId, tagId])
@@map('post_tags')
}
enum Role {
feat: db pull implementation (#268) * feat: initial db pull implementation * fix: generate imports and attributes for zmodel-code-generator * fix: add option to not exclude imports in loadDocument * fix: continue work on db pull * fix: missing import * fix: rewrite model generation generate model from ground up and diff later * feat: add ast factory * fix: ast factory import order * fix: some runtime bugs * fix: lint fix * fix: update zmodel code generator - include imports in output - fix indentaions - include comments in output * feat: add exclude schemas option * feat: implement initial diff update * fix: update format in zmodel code generator * fix: typo * feat: progress on database introspection and syncing * fix: make ignore behave it does in prisma with no index models * fix: lint fix * feat: make all format options configurable * fix: lint fix * feat: Handle the database type mapping * fix: catch up with feature updates - improve code styling - enable schema support for db pull * fix: add sqlite e2e test and fix some bugs * fix: lint fix * fix: formatting for e2e test schemas * test: run db pull e2e test also for postgres * fix: postgres instorspection schema filter * test: update cli tests * feat(cli): Improves database introspection and syncing Enhances the `db pull` command with a spinner for better UX. Adds color-coded logging to highlight important steps. Provides more detailed output on schema changes, including deleted models, enums, added fields, and deleted attributes. Also includes minor improvements to enum mapping and constraint handling. * fix(cli): fixes field casing and sort issues * chore(cli): remove temporary test script Deletes an unused script used for experimenting with URI path resolution. Cleans up the codebase by removing development-only artifacts. * chore: update pnpm-lock.yaml * feat(cli): add MySQL support for schema introspection Introduces a MySQL-specific introspection provider to support pulling existing database schemas into ZenStack. The implementation includes logic for mapping MySQL data types to ZenStack types, handling auto-incrementing fields, and parsing MySQL-specific enum definitions. It utilizes dynamic imports for database drivers to minimize the CLI footprint for users not targeting MySQL. * fix(cli): improve field matching logic during db pull * feat(cli): enhance SQLite introspection with autoincrement support * fix(cli): refine attribute generation in db pull * test(cli): update db pull tests for SQLite specific behavior * refactor(language): export ZModelServices type * fix(cli): improve sqlite introspection for autoincrement and fk names * feat(cli): enhance field matching logic during pull by using relation fields * refactor(cli): refine relation name generation and table syncing * test(cli): update pull tests to reflect improved schema generation * test(cli): add MySQL support to test utility helpers Extends the testing infrastructure to support MySQL databases. Adds MySQL configuration defaults and environment variable overrides. Updates the prelude generation logic to handle MySQL connection strings and provider types, enabling broader database integration testing across the CLI. * fix(cli): omit default constraint names in table sync Avoids explicitly declaring unique constraint names when they match the default database naming convention. This results in cleaner generated schema code by removing redundant mapping arguments. * fix: correctly handle default values for 'text' type in PostgreSQL * fix: sort table indexes to ensure stable schema generation * refactor: dynamically determine supported db providers in CLI * test: fix typo in pull test description * chore(cli): remove debug artifacts and silence test logs Removes hardcoded file system path debugging and unnecessary console logging from the introspector and test suites. Silences CLI command output during tests to provide a cleaner test execution environment. * fix(cli): ensure MySQL column and index ordering Wraps JSON_ARRAYAGG calls in subqueries with explicit ORDER BY clauses to maintain correct metadata ordering. This addresses a limitation in MySQL versions prior to 8.0.21, where ORDER BY is not supported directly within the JSON_ARRAYAGG function, ensuring consistent introspection results across different database versions. * fix(cli): preserve column order during MySQL pull Ensures database columns are sorted by their ordinal position during the introspection process. This maintains the original schema structure and provides a consistent output that matches the physical database layout. * refactor(cli): remove schema fields from MySQL queries Eliminates redundant schema and database name fields from the MySQL introspection query. Since MySQL does not support multi-schema architectures internal to a single connection in this context, removing these fields simplifies the data structure and avoids unnecessary metadata overhead. * fix(cli): improve MySQL introspection and index mapping Refines the database pull process to better handle MySQL-specific patterns. Improves unique constraint detection to prevent redundant mapping attributes when default naming conventions are used. Updates the MySQL introspection logic to correctly identify boolean types, handle timestamp precision in default values, and normalize numeric defaults. Also ensures auto-incrementing columns and primary key indexes are correctly mapped to prevent schema duplication. * test(cli): pass provider to default prelude in tests Ensures that the default schema prelude correctly reflects the database provider specified in test options. This prevents inconsistencies when generating test projects with non-default providers. * fix(cli): improve MySQL introspection for types and defaults Disables NativeEnum support for MySQL to prevent loss of schema-level enums since MySQL enums are column-specific. Refines boolean and numeric type mapping to better handle synthetic boolean types and preserve decimal precision in default values. Updates default value parsing logic to correctly identify unquoted strings and avoid misinterpreting numeric literals as booleans. * fix(cli): improve MySQL default value introspection Refines how default values are handled during database introspection for MySQL by considering the specific field type. This ensures that boolean variants and numeric literals for Float and Decimal types are correctly formatted and preserved. Also clarifies unsupported features in the SQLite provider to improve codebase maintainability. * test(cli): expand and reorganize db pull tests Enhances the test suite for the database pull command by adding comprehensive coverage for common schema features and PostgreSQL-specific functionality. Includes new test cases for: - Restoring complex schemas from scratch, including relations and indexes - Preserving existing imports in multi-file schema setups - Handling PostgreSQL-specific features like multi-schema support and native enums - Verifying schema preservation for field and table mappings The tests are restructured for better clarity across different database providers. * refactor: restructure introspection provider interface and attribute generation * feat: modernize MySQL introspection provider * feat: modernize PostgreSQL introspection provider * feat: modernize SQLite introspection provider * fix: improve relation field naming and default action handling * feat: track imports and auto-format during db pull * test: update pull tests to reflect naming and formatting improvements * fix(cli): refactor PostgreSQL type casting and fix index order Extracts PostgreSQL type casting logic into a reusable helper function to improve maintainability and ensure consistent attribute handling across all field types. Adjusts the table index sorting logic to better preserve the original database creation order while maintaining the priority of unique indexes. * fix(cli): filter out auto-generated MySQL indexes Prevents foreign key indexes created automatically by MySQL from appearing in the introspected schema. This ensures the output reflects manually defined indexes and avoids redundancy in schema definitions. * test(cli): support datasource extras in test utils Enhances the test utility helpers to allow passing extra datasource properties, such as multi-schema configurations for PostgreSQL. Refactors existing database pull tests to use these extra properties, ensuring the generated ZModel schema correctly reflects multi-schema environments while simplifying assertions. * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix(cli): improve file path resolution in pull action * refactor(cli): extract and enhance name casing logic * refactor(cli): consolidate default value normalization * feat(cli): improve enum syncing and relation naming during pull * docs(cli): add documentation comments to SQL introspection queries * test(cli): refactor test utilities and modernize test suites * fix(cli): improve db pull for composite FKs and MySQL uniqueness Enhances database introspection to correctly handle composite foreign keys by mapping columns by position rather than name alone. Improves MySQL introspection by checking statistics tables for single-column unique indexes, ensuring accurate model generation even when column keys are ambiguous. Ensures MySQL synthetic enum names respect requested model casing to prevent unnecessary schema mapping. Adds comprehensive tests for composite relations and database-specific uniqueness detection. * fix: address PR comments * fix(cli): improve SQLite introspection for untyped columns and composite FKs Ensures columns with no declared type are correctly mapped to Bytes following SQLite affinity rules, preventing them from being marked as Unsupported. Updates the DDL parser to correctly identify and map constraint names for composite foreign keys. This ensures that multi-column relations are properly restored during the pull process. Adds regression tests for both untyped columns and composite foreign key restoration. * feat(cli): pull generated/computed columns as Unsupported type Improves database introspection by identifying generated columns in MySQL, PostgreSQL, and SQLite. These columns are now pulled as `Unsupported` types containing their full DDL definition, preventing issues where read-only database fields were incorrectly treated as writable application-level fields. Includes normalization for expression formatting and a fix for string literal escaping in the code generator to ensure stable schema output. Relates to ZModel introspection consistency. * fix(cli): Use parameterized queries for MySQL introspection Switches from template literal interpolation to parameterized queries in MySQL introspection functions. This improves security by preventing potential SQL injection and ensures better handling of database names containing special characters. * fix(cli): use nullish coalescing for precision check
2026-02-08 02:02:26 +00:00
USER
ADMIN
MODERATOR
}`,
// When using MySQL, the introspection simply overrides the enum and cannot detect if it exists with the same name because it only stores the values.
// TODO: Create a better way to handle this, possibly by finding enums by their values as well if the schema exists.
);
runCli('db push', workDir);
runCli('db pull --indent 4', workDir);
expect(getSchema(workDir)).toEqual(schema);
});
it('should preserve imports when pulling with multi-file schema', async () => {
const { workDir } = await createProject('', { customPrelude: true });
const schemaPath = path.join(workDir, 'zenstack/schema.zmodel');
const modelsDir = path.join(workDir, 'zenstack/models');
fs.mkdirSync(modelsDir, { recursive: true });
// Create main schema with imports
const mainSchema = await formatDocument(`import './models/user'
import './models/post'
${getDefaultPrelude()}`);
fs.writeFileSync(schemaPath, mainSchema);
// Create user model
const userModel = await formatDocument(`import './post'
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
}`);
fs.writeFileSync(path.join(modelsDir, 'user.zmodel'), userModel);
// Create post model
const postModel = await formatDocument(`import './user'
model Post {
id Int @id @default(autoincrement())
title String
content String?
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId Int
createdAt DateTime @default(now())
}`);
fs.writeFileSync(path.join(modelsDir, 'post.zmodel'), postModel);
runCli('db push', workDir);
// Pull and verify imports are preserved
runCli('db pull --indent 4', workDir);
const pulledMainSchema = fs.readFileSync(schemaPath).toString();
const pulledUserSchema = fs.readFileSync(path.join(modelsDir, 'user.zmodel')).toString();
const pulledPostSchema = fs.readFileSync(path.join(modelsDir, 'post.zmodel')).toString();
expect(pulledMainSchema).toEqual(mainSchema);
expect(pulledUserSchema).toEqual(userModel);
expect(pulledPostSchema).toEqual(postModel);
});
});
describe('Pull should preserve enum declaration order', () => {
it('should preserve interleaved enum and model ordering', async () => {
const { workDir, schema } = await createProject(
`enum Role {
USER
ADMIN
}
model User {
id Int @id @default(autoincrement())
email String @unique
role Role @default(USER)
status Status @default(ACTIVE)
}
enum Status {
ACTIVE
INACTIVE
SUSPENDED
}`,
);
runCli('db push', workDir);
runCli('db pull --indent 4', workDir);
// Enum-model-enum ordering should be preserved
expect(getSchema(workDir)).toEqual(schema);
});
});
describe('Pull should consolidate shared enums', () => {
it('should consolidate per-column enums back to the original shared enum', async () => {
const { workDir, schema } = await createProject(
`enum Status {
ACTIVE
INACTIVE
SUSPENDED
}
model User {
id Int @id @default(autoincrement())
status Status @default(ACTIVE)
}
model Group {
id Int @id @default(autoincrement())
status Status @default(ACTIVE)
}`,
);
runCli('db push', workDir);
runCli('db pull --indent 4', workDir);
// MySQL creates per-column enums (UserStatus, GroupStatus) but
// consolidation should map them back to the original shared Status enum
expect(getSchema(workDir)).toEqual(schema);
});
it('should consolidate per-column enums with --always-map without stale @@map', async () => {
// This test targets a bug where consolidateEnums renames keepEnum.name
// to oldEnum.name but leaves the synthetic @@map attribute added by
// syncEnums, so getDbName(keepEnum) still returns the old mapped name
// (e.g., 'UserStatus') instead of the consolidated name ('Status'),
// preventing matching in the downstream delete/add enum logic.
const { workDir } = await createProject(
`enum Status {
ACTIVE
INACTIVE
SUSPENDED
}
model User {
id Int @id @default(autoincrement())
status Status @default(ACTIVE)
}
model Group {
id Int @id @default(autoincrement())
status Status @default(ACTIVE)
}`,
);
runCli('db push', workDir);
runCli('db pull --indent 4 --always-map', workDir);
const pulledSchema = getSchema(workDir);
// The consolidated enum should be named Status, not UserStatus/GroupStatus
expect(pulledSchema).toContain('enum Status');
expect(pulledSchema).not.toContain('enum UserStatus');
expect(pulledSchema).not.toContain('enum GroupStatus');
// There should be no stale @@map referencing the synthetic per-column name
expect(pulledSchema).not.toMatch(/@@map\(['"]UserStatus['"]\)/);
expect(pulledSchema).not.toMatch(/@@map\(['"]GroupStatus['"]\)/);
});
});
describe('Pull should preserve triple-slash comments on enums', () => {
it('should preserve triple-slash comments on enum declarations and fields', async () => {
const { workDir, schema } = await createProject(
`model User {
id Int @id @default(autoincrement())
status Status @default(ACTIVE)
}
/// User account status
/// ACTIVE - user can log in
/// INACTIVE - user is disabled
enum Status {
/// User can log in
ACTIVE
/// User is disabled
INACTIVE
/// User is suspended
SUSPENDED
}`,
);
runCli('db push', workDir);
runCli('db pull --indent 4', workDir);
expect(getSchema(workDir)).toEqual(schema);
});
});
describe('Pull should preserve data validation attributes', () => {
it('should preserve field-level validation attributes after db pull', async () => {
const { workDir, schema } = await createProject(
`model User {
id Int @id @default(autoincrement())
email String @unique @email
2026-05-24 04:27:54 +00:00
phone String @phone
name String @length(min: 2, max: 100)
website String? @url
code String? @regex('^[A-Z]+$')
age Int @gt(0)
score Float @gte(0.0)
rating Decimal @lt(10)
rank BigInt @lte(999)
}`,
);
runCli('db push', workDir);
// Pull should preserve all validation attributes
runCli('db pull --indent 4', workDir);
expect(getSchema(workDir)).toEqual(schema);
});
it('should preserve string transformation attributes after db pull', async () => {
const { workDir, schema } = await createProject(
`model Setting {
id Int @id @default(autoincrement())
key String @trim @lower
value String @trim @upper
}`,
);
runCli('db push', workDir);
runCli('db pull --indent 4', workDir);
expect(getSchema(workDir)).toEqual(schema);
});
it('should preserve model-level @@validate attribute after db pull', async () => {
const { workDir, schema } = await createProject(
`model Product {
id Int @id @default(autoincrement())
minPrice Decimal @default(0.00)
maxPrice Decimal @default(100.00)
@@validate(minPrice < maxPrice, 'minPrice must be less than maxPrice')
}`,
);
runCli('db push', workDir);
runCli('db pull --indent 4', workDir);
expect(getSchema(workDir)).toEqual(schema);
});
});
feat: db pull implementation (#268) * feat: initial db pull implementation * fix: generate imports and attributes for zmodel-code-generator * fix: add option to not exclude imports in loadDocument * fix: continue work on db pull * fix: missing import * fix: rewrite model generation generate model from ground up and diff later * feat: add ast factory * fix: ast factory import order * fix: some runtime bugs * fix: lint fix * fix: update zmodel code generator - include imports in output - fix indentaions - include comments in output * feat: add exclude schemas option * feat: implement initial diff update * fix: update format in zmodel code generator * fix: typo * feat: progress on database introspection and syncing * fix: make ignore behave it does in prisma with no index models * fix: lint fix * feat: make all format options configurable * fix: lint fix * feat: Handle the database type mapping * fix: catch up with feature updates - improve code styling - enable schema support for db pull * fix: add sqlite e2e test and fix some bugs * fix: lint fix * fix: formatting for e2e test schemas * test: run db pull e2e test also for postgres * fix: postgres instorspection schema filter * test: update cli tests * feat(cli): Improves database introspection and syncing Enhances the `db pull` command with a spinner for better UX. Adds color-coded logging to highlight important steps. Provides more detailed output on schema changes, including deleted models, enums, added fields, and deleted attributes. Also includes minor improvements to enum mapping and constraint handling. * fix(cli): fixes field casing and sort issues * chore(cli): remove temporary test script Deletes an unused script used for experimenting with URI path resolution. Cleans up the codebase by removing development-only artifacts. * chore: update pnpm-lock.yaml * feat(cli): add MySQL support for schema introspection Introduces a MySQL-specific introspection provider to support pulling existing database schemas into ZenStack. The implementation includes logic for mapping MySQL data types to ZenStack types, handling auto-incrementing fields, and parsing MySQL-specific enum definitions. It utilizes dynamic imports for database drivers to minimize the CLI footprint for users not targeting MySQL. * fix(cli): improve field matching logic during db pull * feat(cli): enhance SQLite introspection with autoincrement support * fix(cli): refine attribute generation in db pull * test(cli): update db pull tests for SQLite specific behavior * refactor(language): export ZModelServices type * fix(cli): improve sqlite introspection for autoincrement and fk names * feat(cli): enhance field matching logic during pull by using relation fields * refactor(cli): refine relation name generation and table syncing * test(cli): update pull tests to reflect improved schema generation * test(cli): add MySQL support to test utility helpers Extends the testing infrastructure to support MySQL databases. Adds MySQL configuration defaults and environment variable overrides. Updates the prelude generation logic to handle MySQL connection strings and provider types, enabling broader database integration testing across the CLI. * fix(cli): omit default constraint names in table sync Avoids explicitly declaring unique constraint names when they match the default database naming convention. This results in cleaner generated schema code by removing redundant mapping arguments. * fix: correctly handle default values for 'text' type in PostgreSQL * fix: sort table indexes to ensure stable schema generation * refactor: dynamically determine supported db providers in CLI * test: fix typo in pull test description * chore(cli): remove debug artifacts and silence test logs Removes hardcoded file system path debugging and unnecessary console logging from the introspector and test suites. Silences CLI command output during tests to provide a cleaner test execution environment. * fix(cli): ensure MySQL column and index ordering Wraps JSON_ARRAYAGG calls in subqueries with explicit ORDER BY clauses to maintain correct metadata ordering. This addresses a limitation in MySQL versions prior to 8.0.21, where ORDER BY is not supported directly within the JSON_ARRAYAGG function, ensuring consistent introspection results across different database versions. * fix(cli): preserve column order during MySQL pull Ensures database columns are sorted by their ordinal position during the introspection process. This maintains the original schema structure and provides a consistent output that matches the physical database layout. * refactor(cli): remove schema fields from MySQL queries Eliminates redundant schema and database name fields from the MySQL introspection query. Since MySQL does not support multi-schema architectures internal to a single connection in this context, removing these fields simplifies the data structure and avoids unnecessary metadata overhead. * fix(cli): improve MySQL introspection and index mapping Refines the database pull process to better handle MySQL-specific patterns. Improves unique constraint detection to prevent redundant mapping attributes when default naming conventions are used. Updates the MySQL introspection logic to correctly identify boolean types, handle timestamp precision in default values, and normalize numeric defaults. Also ensures auto-incrementing columns and primary key indexes are correctly mapped to prevent schema duplication. * test(cli): pass provider to default prelude in tests Ensures that the default schema prelude correctly reflects the database provider specified in test options. This prevents inconsistencies when generating test projects with non-default providers. * fix(cli): improve MySQL introspection for types and defaults Disables NativeEnum support for MySQL to prevent loss of schema-level enums since MySQL enums are column-specific. Refines boolean and numeric type mapping to better handle synthetic boolean types and preserve decimal precision in default values. Updates default value parsing logic to correctly identify unquoted strings and avoid misinterpreting numeric literals as booleans. * fix(cli): improve MySQL default value introspection Refines how default values are handled during database introspection for MySQL by considering the specific field type. This ensures that boolean variants and numeric literals for Float and Decimal types are correctly formatted and preserved. Also clarifies unsupported features in the SQLite provider to improve codebase maintainability. * test(cli): expand and reorganize db pull tests Enhances the test suite for the database pull command by adding comprehensive coverage for common schema features and PostgreSQL-specific functionality. Includes new test cases for: - Restoring complex schemas from scratch, including relations and indexes - Preserving existing imports in multi-file schema setups - Handling PostgreSQL-specific features like multi-schema support and native enums - Verifying schema preservation for field and table mappings The tests are restructured for better clarity across different database providers. * refactor: restructure introspection provider interface and attribute generation * feat: modernize MySQL introspection provider * feat: modernize PostgreSQL introspection provider * feat: modernize SQLite introspection provider * fix: improve relation field naming and default action handling * feat: track imports and auto-format during db pull * test: update pull tests to reflect naming and formatting improvements * fix(cli): refactor PostgreSQL type casting and fix index order Extracts PostgreSQL type casting logic into a reusable helper function to improve maintainability and ensure consistent attribute handling across all field types. Adjusts the table index sorting logic to better preserve the original database creation order while maintaining the priority of unique indexes. * fix(cli): filter out auto-generated MySQL indexes Prevents foreign key indexes created automatically by MySQL from appearing in the introspected schema. This ensures the output reflects manually defined indexes and avoids redundancy in schema definitions. * test(cli): support datasource extras in test utils Enhances the test utility helpers to allow passing extra datasource properties, such as multi-schema configurations for PostgreSQL. Refactors existing database pull tests to use these extra properties, ensuring the generated ZModel schema correctly reflects multi-schema environments while simplifying assertions. * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix(cli): improve file path resolution in pull action * refactor(cli): extract and enhance name casing logic * refactor(cli): consolidate default value normalization * feat(cli): improve enum syncing and relation naming during pull * docs(cli): add documentation comments to SQL introspection queries * test(cli): refactor test utilities and modernize test suites * fix(cli): improve db pull for composite FKs and MySQL uniqueness Enhances database introspection to correctly handle composite foreign keys by mapping columns by position rather than name alone. Improves MySQL introspection by checking statistics tables for single-column unique indexes, ensuring accurate model generation even when column keys are ambiguous. Ensures MySQL synthetic enum names respect requested model casing to prevent unnecessary schema mapping. Adds comprehensive tests for composite relations and database-specific uniqueness detection. * fix: address PR comments * fix(cli): improve SQLite introspection for untyped columns and composite FKs Ensures columns with no declared type are correctly mapped to Bytes following SQLite affinity rules, preventing them from being marked as Unsupported. Updates the DDL parser to correctly identify and map constraint names for composite foreign keys. This ensures that multi-column relations are properly restored during the pull process. Adds regression tests for both untyped columns and composite foreign key restoration. * feat(cli): pull generated/computed columns as Unsupported type Improves database introspection by identifying generated columns in MySQL, PostgreSQL, and SQLite. These columns are now pulled as `Unsupported` types containing their full DDL definition, preventing issues where read-only database fields were incorrectly treated as writable application-level fields. Includes normalization for expression formatting and a fix for string literal escaping in the code generator to ensure stable schema output. Relates to ZModel introspection consistency. * fix(cli): Use parameterized queries for MySQL introspection Switches from template literal interpolation to parameterized queries in MySQL introspection functions. This improves security by preventing potential SQL injection and ensures better handling of database names containing special characters. * fix(cli): use nullish coalescing for precision check
2026-02-08 02:02:26 +00:00
describe('Pull should update existing field definitions when database changes', () => {
it('should update field type when database column type changes', async () => {
// Step 1: Create initial schema with String field
const { workDir } = await createProject(
`model User {
id Int @id @default(autoincrement())
email String @unique
age String
}`,
);
runCli('db push', workDir);
// Step 2: Modify schema to change age from String to Int
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
const updatedSchema = await formatDocument(`${getDefaultPrelude()}
model User {
id Int @id @default(autoincrement())
email String @unique
age Int
}`);
fs.writeFileSync(schemaFile, updatedSchema);
runCli('db push', workDir);
// Step 3: Revert schema back to original (with String type)
const originalSchema = await formatDocument(`${getDefaultPrelude()}
model User {
id Int @id @default(autoincrement())
email String @unique
age String
}`);
fs.writeFileSync(schemaFile, originalSchema);
// Step 4: Pull from database - should detect that age is now Int
runCli('db pull --indent 4', workDir);
// Step 5: Verify that pulled schema has Int type (matching database)
const pulledSchema = getSchema(workDir);
expect(pulledSchema).toEqual(updatedSchema);
});
it('should update field optionality when database column nullability changes', async () => {
// Step 1: Create initial schema with required field
const { workDir } = await createProject(
`model User {
id Int @id @default(autoincrement())
email String @unique
name String
}`,
);
runCli('db push', workDir);
// Step 2: Modify schema to make name optional
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
const updatedSchema = await formatDocument(`${getDefaultPrelude()}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
}`);
fs.writeFileSync(schemaFile, updatedSchema);
runCli('db push', workDir);
// Step 3: Revert schema back to original (with required name)
const originalSchema = await formatDocument(`${getDefaultPrelude()}
model User {
id Int @id @default(autoincrement())
email String @unique
name String
}`);
fs.writeFileSync(schemaFile, originalSchema);
// Step 4: Pull from database - should detect that name is now optional
runCli('db pull --indent 4', workDir);
// Step 5: Verify that pulled schema has optional name (matching database)
const pulledSchema = getSchema(workDir);
expect(pulledSchema).toEqual(updatedSchema);
});
it('should update default value when database default changes', async () => {
// Step 1: Create initial schema with default value
const { workDir } = await createProject(
`model User {
id Int @id @default(autoincrement())
email String @unique
status String @default('active')
}`,
);
runCli('db push', workDir);
// Step 2: Modify schema to change default value
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
const updatedSchema = await formatDocument(`${getDefaultPrelude()}
model User {
id Int @id @default(autoincrement())
email String @unique
status String @default('pending')
}`);
fs.writeFileSync(schemaFile, updatedSchema);
runCli('db push', workDir);
// Step 3: Revert schema back to original default
const originalSchema = await formatDocument(`${getDefaultPrelude()}
model User {
id Int @id @default(autoincrement())
email String @unique
status String @default('active')
}`);
fs.writeFileSync(schemaFile, originalSchema);
// Step 4: Pull from database - should detect that default changed
runCli('db pull --indent 4', workDir);
// Step 5: Verify that pulled schema has updated default (matching database)
const pulledSchema = getSchema(workDir);
expect(pulledSchema).toEqual(updatedSchema);
});
});
});
describe('DB pull - PostgreSQL specific features', () => {
it('should restore schema with multiple database schemas', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'postgresql') {
skip();
return;
}
const { workDir, schema } = await createProject(
`model User {
id Int @id @default(autoincrement())
email String @unique
posts Post[]
@@schema('auth')
}
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId Int
@@schema('content')
}`,
{ provider: 'postgresql', datasourceFields:{ schemas: ['public', 'content', 'auth'] } },
);
runCli('db push', workDir);
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude({ provider: 'postgresql', datasourceFields:{ schemas: ['public', 'content', 'auth']} }));
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
it('should preserve native PostgreSQL enums when schema exists', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'postgresql') {
skip();
return;
}
const { workDir, schema } = await createProject(
`model User {
id Int @id @default(autoincrement())
email String @unique
status Status @default(ACTIVE)
role Role @default(USER)
feat: db pull implementation (#268) * feat: initial db pull implementation * fix: generate imports and attributes for zmodel-code-generator * fix: add option to not exclude imports in loadDocument * fix: continue work on db pull * fix: missing import * fix: rewrite model generation generate model from ground up and diff later * feat: add ast factory * fix: ast factory import order * fix: some runtime bugs * fix: lint fix * fix: update zmodel code generator - include imports in output - fix indentaions - include comments in output * feat: add exclude schemas option * feat: implement initial diff update * fix: update format in zmodel code generator * fix: typo * feat: progress on database introspection and syncing * fix: make ignore behave it does in prisma with no index models * fix: lint fix * feat: make all format options configurable * fix: lint fix * feat: Handle the database type mapping * fix: catch up with feature updates - improve code styling - enable schema support for db pull * fix: add sqlite e2e test and fix some bugs * fix: lint fix * fix: formatting for e2e test schemas * test: run db pull e2e test also for postgres * fix: postgres instorspection schema filter * test: update cli tests * feat(cli): Improves database introspection and syncing Enhances the `db pull` command with a spinner for better UX. Adds color-coded logging to highlight important steps. Provides more detailed output on schema changes, including deleted models, enums, added fields, and deleted attributes. Also includes minor improvements to enum mapping and constraint handling. * fix(cli): fixes field casing and sort issues * chore(cli): remove temporary test script Deletes an unused script used for experimenting with URI path resolution. Cleans up the codebase by removing development-only artifacts. * chore: update pnpm-lock.yaml * feat(cli): add MySQL support for schema introspection Introduces a MySQL-specific introspection provider to support pulling existing database schemas into ZenStack. The implementation includes logic for mapping MySQL data types to ZenStack types, handling auto-incrementing fields, and parsing MySQL-specific enum definitions. It utilizes dynamic imports for database drivers to minimize the CLI footprint for users not targeting MySQL. * fix(cli): improve field matching logic during db pull * feat(cli): enhance SQLite introspection with autoincrement support * fix(cli): refine attribute generation in db pull * test(cli): update db pull tests for SQLite specific behavior * refactor(language): export ZModelServices type * fix(cli): improve sqlite introspection for autoincrement and fk names * feat(cli): enhance field matching logic during pull by using relation fields * refactor(cli): refine relation name generation and table syncing * test(cli): update pull tests to reflect improved schema generation * test(cli): add MySQL support to test utility helpers Extends the testing infrastructure to support MySQL databases. Adds MySQL configuration defaults and environment variable overrides. Updates the prelude generation logic to handle MySQL connection strings and provider types, enabling broader database integration testing across the CLI. * fix(cli): omit default constraint names in table sync Avoids explicitly declaring unique constraint names when they match the default database naming convention. This results in cleaner generated schema code by removing redundant mapping arguments. * fix: correctly handle default values for 'text' type in PostgreSQL * fix: sort table indexes to ensure stable schema generation * refactor: dynamically determine supported db providers in CLI * test: fix typo in pull test description * chore(cli): remove debug artifacts and silence test logs Removes hardcoded file system path debugging and unnecessary console logging from the introspector and test suites. Silences CLI command output during tests to provide a cleaner test execution environment. * fix(cli): ensure MySQL column and index ordering Wraps JSON_ARRAYAGG calls in subqueries with explicit ORDER BY clauses to maintain correct metadata ordering. This addresses a limitation in MySQL versions prior to 8.0.21, where ORDER BY is not supported directly within the JSON_ARRAYAGG function, ensuring consistent introspection results across different database versions. * fix(cli): preserve column order during MySQL pull Ensures database columns are sorted by their ordinal position during the introspection process. This maintains the original schema structure and provides a consistent output that matches the physical database layout. * refactor(cli): remove schema fields from MySQL queries Eliminates redundant schema and database name fields from the MySQL introspection query. Since MySQL does not support multi-schema architectures internal to a single connection in this context, removing these fields simplifies the data structure and avoids unnecessary metadata overhead. * fix(cli): improve MySQL introspection and index mapping Refines the database pull process to better handle MySQL-specific patterns. Improves unique constraint detection to prevent redundant mapping attributes when default naming conventions are used. Updates the MySQL introspection logic to correctly identify boolean types, handle timestamp precision in default values, and normalize numeric defaults. Also ensures auto-incrementing columns and primary key indexes are correctly mapped to prevent schema duplication. * test(cli): pass provider to default prelude in tests Ensures that the default schema prelude correctly reflects the database provider specified in test options. This prevents inconsistencies when generating test projects with non-default providers. * fix(cli): improve MySQL introspection for types and defaults Disables NativeEnum support for MySQL to prevent loss of schema-level enums since MySQL enums are column-specific. Refines boolean and numeric type mapping to better handle synthetic boolean types and preserve decimal precision in default values. Updates default value parsing logic to correctly identify unquoted strings and avoid misinterpreting numeric literals as booleans. * fix(cli): improve MySQL default value introspection Refines how default values are handled during database introspection for MySQL by considering the specific field type. This ensures that boolean variants and numeric literals for Float and Decimal types are correctly formatted and preserved. Also clarifies unsupported features in the SQLite provider to improve codebase maintainability. * test(cli): expand and reorganize db pull tests Enhances the test suite for the database pull command by adding comprehensive coverage for common schema features and PostgreSQL-specific functionality. Includes new test cases for: - Restoring complex schemas from scratch, including relations and indexes - Preserving existing imports in multi-file schema setups - Handling PostgreSQL-specific features like multi-schema support and native enums - Verifying schema preservation for field and table mappings The tests are restructured for better clarity across different database providers. * refactor: restructure introspection provider interface and attribute generation * feat: modernize MySQL introspection provider * feat: modernize PostgreSQL introspection provider * feat: modernize SQLite introspection provider * fix: improve relation field naming and default action handling * feat: track imports and auto-format during db pull * test: update pull tests to reflect naming and formatting improvements * fix(cli): refactor PostgreSQL type casting and fix index order Extracts PostgreSQL type casting logic into a reusable helper function to improve maintainability and ensure consistent attribute handling across all field types. Adjusts the table index sorting logic to better preserve the original database creation order while maintaining the priority of unique indexes. * fix(cli): filter out auto-generated MySQL indexes Prevents foreign key indexes created automatically by MySQL from appearing in the introspected schema. This ensures the output reflects manually defined indexes and avoids redundancy in schema definitions. * test(cli): support datasource extras in test utils Enhances the test utility helpers to allow passing extra datasource properties, such as multi-schema configurations for PostgreSQL. Refactors existing database pull tests to use these extra properties, ensuring the generated ZModel schema correctly reflects multi-schema environments while simplifying assertions. * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix(cli): improve file path resolution in pull action * refactor(cli): extract and enhance name casing logic * refactor(cli): consolidate default value normalization * feat(cli): improve enum syncing and relation naming during pull * docs(cli): add documentation comments to SQL introspection queries * test(cli): refactor test utilities and modernize test suites * fix(cli): improve db pull for composite FKs and MySQL uniqueness Enhances database introspection to correctly handle composite foreign keys by mapping columns by position rather than name alone. Improves MySQL introspection by checking statistics tables for single-column unique indexes, ensuring accurate model generation even when column keys are ambiguous. Ensures MySQL synthetic enum names respect requested model casing to prevent unnecessary schema mapping. Adds comprehensive tests for composite relations and database-specific uniqueness detection. * fix: address PR comments * fix(cli): improve SQLite introspection for untyped columns and composite FKs Ensures columns with no declared type are correctly mapped to Bytes following SQLite affinity rules, preventing them from being marked as Unsupported. Updates the DDL parser to correctly identify and map constraint names for composite foreign keys. This ensures that multi-column relations are properly restored during the pull process. Adds regression tests for both untyped columns and composite foreign key restoration. * feat(cli): pull generated/computed columns as Unsupported type Improves database introspection by identifying generated columns in MySQL, PostgreSQL, and SQLite. These columns are now pulled as `Unsupported` types containing their full DDL definition, preventing issues where read-only database fields were incorrectly treated as writable application-level fields. Includes normalization for expression formatting and a fix for string literal escaping in the code generator to ensure stable schema output. Relates to ZModel introspection consistency. * fix(cli): Use parameterized queries for MySQL introspection Switches from template literal interpolation to parameterized queries in MySQL introspection functions. This improves security by preventing potential SQL injection and ensures better handling of database names containing special characters. * fix(cli): use nullish coalescing for precision check
2026-02-08 02:02:26 +00:00
}
enum Status {
feat: db pull implementation (#268) * feat: initial db pull implementation * fix: generate imports and attributes for zmodel-code-generator * fix: add option to not exclude imports in loadDocument * fix: continue work on db pull * fix: missing import * fix: rewrite model generation generate model from ground up and diff later * feat: add ast factory * fix: ast factory import order * fix: some runtime bugs * fix: lint fix * fix: update zmodel code generator - include imports in output - fix indentaions - include comments in output * feat: add exclude schemas option * feat: implement initial diff update * fix: update format in zmodel code generator * fix: typo * feat: progress on database introspection and syncing * fix: make ignore behave it does in prisma with no index models * fix: lint fix * feat: make all format options configurable * fix: lint fix * feat: Handle the database type mapping * fix: catch up with feature updates - improve code styling - enable schema support for db pull * fix: add sqlite e2e test and fix some bugs * fix: lint fix * fix: formatting for e2e test schemas * test: run db pull e2e test also for postgres * fix: postgres instorspection schema filter * test: update cli tests * feat(cli): Improves database introspection and syncing Enhances the `db pull` command with a spinner for better UX. Adds color-coded logging to highlight important steps. Provides more detailed output on schema changes, including deleted models, enums, added fields, and deleted attributes. Also includes minor improvements to enum mapping and constraint handling. * fix(cli): fixes field casing and sort issues * chore(cli): remove temporary test script Deletes an unused script used for experimenting with URI path resolution. Cleans up the codebase by removing development-only artifacts. * chore: update pnpm-lock.yaml * feat(cli): add MySQL support for schema introspection Introduces a MySQL-specific introspection provider to support pulling existing database schemas into ZenStack. The implementation includes logic for mapping MySQL data types to ZenStack types, handling auto-incrementing fields, and parsing MySQL-specific enum definitions. It utilizes dynamic imports for database drivers to minimize the CLI footprint for users not targeting MySQL. * fix(cli): improve field matching logic during db pull * feat(cli): enhance SQLite introspection with autoincrement support * fix(cli): refine attribute generation in db pull * test(cli): update db pull tests for SQLite specific behavior * refactor(language): export ZModelServices type * fix(cli): improve sqlite introspection for autoincrement and fk names * feat(cli): enhance field matching logic during pull by using relation fields * refactor(cli): refine relation name generation and table syncing * test(cli): update pull tests to reflect improved schema generation * test(cli): add MySQL support to test utility helpers Extends the testing infrastructure to support MySQL databases. Adds MySQL configuration defaults and environment variable overrides. Updates the prelude generation logic to handle MySQL connection strings and provider types, enabling broader database integration testing across the CLI. * fix(cli): omit default constraint names in table sync Avoids explicitly declaring unique constraint names when they match the default database naming convention. This results in cleaner generated schema code by removing redundant mapping arguments. * fix: correctly handle default values for 'text' type in PostgreSQL * fix: sort table indexes to ensure stable schema generation * refactor: dynamically determine supported db providers in CLI * test: fix typo in pull test description * chore(cli): remove debug artifacts and silence test logs Removes hardcoded file system path debugging and unnecessary console logging from the introspector and test suites. Silences CLI command output during tests to provide a cleaner test execution environment. * fix(cli): ensure MySQL column and index ordering Wraps JSON_ARRAYAGG calls in subqueries with explicit ORDER BY clauses to maintain correct metadata ordering. This addresses a limitation in MySQL versions prior to 8.0.21, where ORDER BY is not supported directly within the JSON_ARRAYAGG function, ensuring consistent introspection results across different database versions. * fix(cli): preserve column order during MySQL pull Ensures database columns are sorted by their ordinal position during the introspection process. This maintains the original schema structure and provides a consistent output that matches the physical database layout. * refactor(cli): remove schema fields from MySQL queries Eliminates redundant schema and database name fields from the MySQL introspection query. Since MySQL does not support multi-schema architectures internal to a single connection in this context, removing these fields simplifies the data structure and avoids unnecessary metadata overhead. * fix(cli): improve MySQL introspection and index mapping Refines the database pull process to better handle MySQL-specific patterns. Improves unique constraint detection to prevent redundant mapping attributes when default naming conventions are used. Updates the MySQL introspection logic to correctly identify boolean types, handle timestamp precision in default values, and normalize numeric defaults. Also ensures auto-incrementing columns and primary key indexes are correctly mapped to prevent schema duplication. * test(cli): pass provider to default prelude in tests Ensures that the default schema prelude correctly reflects the database provider specified in test options. This prevents inconsistencies when generating test projects with non-default providers. * fix(cli): improve MySQL introspection for types and defaults Disables NativeEnum support for MySQL to prevent loss of schema-level enums since MySQL enums are column-specific. Refines boolean and numeric type mapping to better handle synthetic boolean types and preserve decimal precision in default values. Updates default value parsing logic to correctly identify unquoted strings and avoid misinterpreting numeric literals as booleans. * fix(cli): improve MySQL default value introspection Refines how default values are handled during database introspection for MySQL by considering the specific field type. This ensures that boolean variants and numeric literals for Float and Decimal types are correctly formatted and preserved. Also clarifies unsupported features in the SQLite provider to improve codebase maintainability. * test(cli): expand and reorganize db pull tests Enhances the test suite for the database pull command by adding comprehensive coverage for common schema features and PostgreSQL-specific functionality. Includes new test cases for: - Restoring complex schemas from scratch, including relations and indexes - Preserving existing imports in multi-file schema setups - Handling PostgreSQL-specific features like multi-schema support and native enums - Verifying schema preservation for field and table mappings The tests are restructured for better clarity across different database providers. * refactor: restructure introspection provider interface and attribute generation * feat: modernize MySQL introspection provider * feat: modernize PostgreSQL introspection provider * feat: modernize SQLite introspection provider * fix: improve relation field naming and default action handling * feat: track imports and auto-format during db pull * test: update pull tests to reflect naming and formatting improvements * fix(cli): refactor PostgreSQL type casting and fix index order Extracts PostgreSQL type casting logic into a reusable helper function to improve maintainability and ensure consistent attribute handling across all field types. Adjusts the table index sorting logic to better preserve the original database creation order while maintaining the priority of unique indexes. * fix(cli): filter out auto-generated MySQL indexes Prevents foreign key indexes created automatically by MySQL from appearing in the introspected schema. This ensures the output reflects manually defined indexes and avoids redundancy in schema definitions. * test(cli): support datasource extras in test utils Enhances the test utility helpers to allow passing extra datasource properties, such as multi-schema configurations for PostgreSQL. Refactors existing database pull tests to use these extra properties, ensuring the generated ZModel schema correctly reflects multi-schema environments while simplifying assertions. * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix(cli): improve file path resolution in pull action * refactor(cli): extract and enhance name casing logic * refactor(cli): consolidate default value normalization * feat(cli): improve enum syncing and relation naming during pull * docs(cli): add documentation comments to SQL introspection queries * test(cli): refactor test utilities and modernize test suites * fix(cli): improve db pull for composite FKs and MySQL uniqueness Enhances database introspection to correctly handle composite foreign keys by mapping columns by position rather than name alone. Improves MySQL introspection by checking statistics tables for single-column unique indexes, ensuring accurate model generation even when column keys are ambiguous. Ensures MySQL synthetic enum names respect requested model casing to prevent unnecessary schema mapping. Adds comprehensive tests for composite relations and database-specific uniqueness detection. * fix: address PR comments * fix(cli): improve SQLite introspection for untyped columns and composite FKs Ensures columns with no declared type are correctly mapped to Bytes following SQLite affinity rules, preventing them from being marked as Unsupported. Updates the DDL parser to correctly identify and map constraint names for composite foreign keys. This ensures that multi-column relations are properly restored during the pull process. Adds regression tests for both untyped columns and composite foreign key restoration. * feat(cli): pull generated/computed columns as Unsupported type Improves database introspection by identifying generated columns in MySQL, PostgreSQL, and SQLite. These columns are now pulled as `Unsupported` types containing their full DDL definition, preventing issues where read-only database fields were incorrectly treated as writable application-level fields. Includes normalization for expression formatting and a fix for string literal escaping in the code generator to ensure stable schema output. Relates to ZModel introspection consistency. * fix(cli): Use parameterized queries for MySQL introspection Switches from template literal interpolation to parameterized queries in MySQL introspection functions. This improves security by preventing potential SQL injection and ensures better handling of database names containing special characters. * fix(cli): use nullish coalescing for precision check
2026-02-08 02:02:26 +00:00
ACTIVE
INACTIVE
SUSPENDED
}
enum Role {
feat: db pull implementation (#268) * feat: initial db pull implementation * fix: generate imports and attributes for zmodel-code-generator * fix: add option to not exclude imports in loadDocument * fix: continue work on db pull * fix: missing import * fix: rewrite model generation generate model from ground up and diff later * feat: add ast factory * fix: ast factory import order * fix: some runtime bugs * fix: lint fix * fix: update zmodel code generator - include imports in output - fix indentaions - include comments in output * feat: add exclude schemas option * feat: implement initial diff update * fix: update format in zmodel code generator * fix: typo * feat: progress on database introspection and syncing * fix: make ignore behave it does in prisma with no index models * fix: lint fix * feat: make all format options configurable * fix: lint fix * feat: Handle the database type mapping * fix: catch up with feature updates - improve code styling - enable schema support for db pull * fix: add sqlite e2e test and fix some bugs * fix: lint fix * fix: formatting for e2e test schemas * test: run db pull e2e test also for postgres * fix: postgres instorspection schema filter * test: update cli tests * feat(cli): Improves database introspection and syncing Enhances the `db pull` command with a spinner for better UX. Adds color-coded logging to highlight important steps. Provides more detailed output on schema changes, including deleted models, enums, added fields, and deleted attributes. Also includes minor improvements to enum mapping and constraint handling. * fix(cli): fixes field casing and sort issues * chore(cli): remove temporary test script Deletes an unused script used for experimenting with URI path resolution. Cleans up the codebase by removing development-only artifacts. * chore: update pnpm-lock.yaml * feat(cli): add MySQL support for schema introspection Introduces a MySQL-specific introspection provider to support pulling existing database schemas into ZenStack. The implementation includes logic for mapping MySQL data types to ZenStack types, handling auto-incrementing fields, and parsing MySQL-specific enum definitions. It utilizes dynamic imports for database drivers to minimize the CLI footprint for users not targeting MySQL. * fix(cli): improve field matching logic during db pull * feat(cli): enhance SQLite introspection with autoincrement support * fix(cli): refine attribute generation in db pull * test(cli): update db pull tests for SQLite specific behavior * refactor(language): export ZModelServices type * fix(cli): improve sqlite introspection for autoincrement and fk names * feat(cli): enhance field matching logic during pull by using relation fields * refactor(cli): refine relation name generation and table syncing * test(cli): update pull tests to reflect improved schema generation * test(cli): add MySQL support to test utility helpers Extends the testing infrastructure to support MySQL databases. Adds MySQL configuration defaults and environment variable overrides. Updates the prelude generation logic to handle MySQL connection strings and provider types, enabling broader database integration testing across the CLI. * fix(cli): omit default constraint names in table sync Avoids explicitly declaring unique constraint names when they match the default database naming convention. This results in cleaner generated schema code by removing redundant mapping arguments. * fix: correctly handle default values for 'text' type in PostgreSQL * fix: sort table indexes to ensure stable schema generation * refactor: dynamically determine supported db providers in CLI * test: fix typo in pull test description * chore(cli): remove debug artifacts and silence test logs Removes hardcoded file system path debugging and unnecessary console logging from the introspector and test suites. Silences CLI command output during tests to provide a cleaner test execution environment. * fix(cli): ensure MySQL column and index ordering Wraps JSON_ARRAYAGG calls in subqueries with explicit ORDER BY clauses to maintain correct metadata ordering. This addresses a limitation in MySQL versions prior to 8.0.21, where ORDER BY is not supported directly within the JSON_ARRAYAGG function, ensuring consistent introspection results across different database versions. * fix(cli): preserve column order during MySQL pull Ensures database columns are sorted by their ordinal position during the introspection process. This maintains the original schema structure and provides a consistent output that matches the physical database layout. * refactor(cli): remove schema fields from MySQL queries Eliminates redundant schema and database name fields from the MySQL introspection query. Since MySQL does not support multi-schema architectures internal to a single connection in this context, removing these fields simplifies the data structure and avoids unnecessary metadata overhead. * fix(cli): improve MySQL introspection and index mapping Refines the database pull process to better handle MySQL-specific patterns. Improves unique constraint detection to prevent redundant mapping attributes when default naming conventions are used. Updates the MySQL introspection logic to correctly identify boolean types, handle timestamp precision in default values, and normalize numeric defaults. Also ensures auto-incrementing columns and primary key indexes are correctly mapped to prevent schema duplication. * test(cli): pass provider to default prelude in tests Ensures that the default schema prelude correctly reflects the database provider specified in test options. This prevents inconsistencies when generating test projects with non-default providers. * fix(cli): improve MySQL introspection for types and defaults Disables NativeEnum support for MySQL to prevent loss of schema-level enums since MySQL enums are column-specific. Refines boolean and numeric type mapping to better handle synthetic boolean types and preserve decimal precision in default values. Updates default value parsing logic to correctly identify unquoted strings and avoid misinterpreting numeric literals as booleans. * fix(cli): improve MySQL default value introspection Refines how default values are handled during database introspection for MySQL by considering the specific field type. This ensures that boolean variants and numeric literals for Float and Decimal types are correctly formatted and preserved. Also clarifies unsupported features in the SQLite provider to improve codebase maintainability. * test(cli): expand and reorganize db pull tests Enhances the test suite for the database pull command by adding comprehensive coverage for common schema features and PostgreSQL-specific functionality. Includes new test cases for: - Restoring complex schemas from scratch, including relations and indexes - Preserving existing imports in multi-file schema setups - Handling PostgreSQL-specific features like multi-schema support and native enums - Verifying schema preservation for field and table mappings The tests are restructured for better clarity across different database providers. * refactor: restructure introspection provider interface and attribute generation * feat: modernize MySQL introspection provider * feat: modernize PostgreSQL introspection provider * feat: modernize SQLite introspection provider * fix: improve relation field naming and default action handling * feat: track imports and auto-format during db pull * test: update pull tests to reflect naming and formatting improvements * fix(cli): refactor PostgreSQL type casting and fix index order Extracts PostgreSQL type casting logic into a reusable helper function to improve maintainability and ensure consistent attribute handling across all field types. Adjusts the table index sorting logic to better preserve the original database creation order while maintaining the priority of unique indexes. * fix(cli): filter out auto-generated MySQL indexes Prevents foreign key indexes created automatically by MySQL from appearing in the introspected schema. This ensures the output reflects manually defined indexes and avoids redundancy in schema definitions. * test(cli): support datasource extras in test utils Enhances the test utility helpers to allow passing extra datasource properties, such as multi-schema configurations for PostgreSQL. Refactors existing database pull tests to use these extra properties, ensuring the generated ZModel schema correctly reflects multi-schema environments while simplifying assertions. * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix(cli): improve file path resolution in pull action * refactor(cli): extract and enhance name casing logic * refactor(cli): consolidate default value normalization * feat(cli): improve enum syncing and relation naming during pull * docs(cli): add documentation comments to SQL introspection queries * test(cli): refactor test utilities and modernize test suites * fix(cli): improve db pull for composite FKs and MySQL uniqueness Enhances database introspection to correctly handle composite foreign keys by mapping columns by position rather than name alone. Improves MySQL introspection by checking statistics tables for single-column unique indexes, ensuring accurate model generation even when column keys are ambiguous. Ensures MySQL synthetic enum names respect requested model casing to prevent unnecessary schema mapping. Adds comprehensive tests for composite relations and database-specific uniqueness detection. * fix: address PR comments * fix(cli): improve SQLite introspection for untyped columns and composite FKs Ensures columns with no declared type are correctly mapped to Bytes following SQLite affinity rules, preventing them from being marked as Unsupported. Updates the DDL parser to correctly identify and map constraint names for composite foreign keys. This ensures that multi-column relations are properly restored during the pull process. Adds regression tests for both untyped columns and composite foreign key restoration. * feat(cli): pull generated/computed columns as Unsupported type Improves database introspection by identifying generated columns in MySQL, PostgreSQL, and SQLite. These columns are now pulled as `Unsupported` types containing their full DDL definition, preventing issues where read-only database fields were incorrectly treated as writable application-level fields. Includes normalization for expression formatting and a fix for string literal escaping in the code generator to ensure stable schema output. Relates to ZModel introspection consistency. * fix(cli): Use parameterized queries for MySQL introspection Switches from template literal interpolation to parameterized queries in MySQL introspection functions. This improves security by preventing potential SQL injection and ensures better handling of database names containing special characters. * fix(cli): use nullish coalescing for precision check
2026-02-08 02:02:26 +00:00
USER
ADMIN
MODERATOR
}`,
{ provider: 'postgresql' },
);
runCli('db push', workDir);
runCli('db pull --indent 4', workDir);
const pulledSchema = getSchema(workDir);
expect(pulledSchema).toEqual(schema);
});
it('should not modify schema with PostgreSQL-specific features', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'postgresql') {
skip();
return;
}
const { workDir, schema } = await createProject(
`model User {
id Int @id @default(autoincrement())
email String @unique
status Status @default(ACTIVE)
feat: db pull implementation (#268) * feat: initial db pull implementation * fix: generate imports and attributes for zmodel-code-generator * fix: add option to not exclude imports in loadDocument * fix: continue work on db pull * fix: missing import * fix: rewrite model generation generate model from ground up and diff later * feat: add ast factory * fix: ast factory import order * fix: some runtime bugs * fix: lint fix * fix: update zmodel code generator - include imports in output - fix indentaions - include comments in output * feat: add exclude schemas option * feat: implement initial diff update * fix: update format in zmodel code generator * fix: typo * feat: progress on database introspection and syncing * fix: make ignore behave it does in prisma with no index models * fix: lint fix * feat: make all format options configurable * fix: lint fix * feat: Handle the database type mapping * fix: catch up with feature updates - improve code styling - enable schema support for db pull * fix: add sqlite e2e test and fix some bugs * fix: lint fix * fix: formatting for e2e test schemas * test: run db pull e2e test also for postgres * fix: postgres instorspection schema filter * test: update cli tests * feat(cli): Improves database introspection and syncing Enhances the `db pull` command with a spinner for better UX. Adds color-coded logging to highlight important steps. Provides more detailed output on schema changes, including deleted models, enums, added fields, and deleted attributes. Also includes minor improvements to enum mapping and constraint handling. * fix(cli): fixes field casing and sort issues * chore(cli): remove temporary test script Deletes an unused script used for experimenting with URI path resolution. Cleans up the codebase by removing development-only artifacts. * chore: update pnpm-lock.yaml * feat(cli): add MySQL support for schema introspection Introduces a MySQL-specific introspection provider to support pulling existing database schemas into ZenStack. The implementation includes logic for mapping MySQL data types to ZenStack types, handling auto-incrementing fields, and parsing MySQL-specific enum definitions. It utilizes dynamic imports for database drivers to minimize the CLI footprint for users not targeting MySQL. * fix(cli): improve field matching logic during db pull * feat(cli): enhance SQLite introspection with autoincrement support * fix(cli): refine attribute generation in db pull * test(cli): update db pull tests for SQLite specific behavior * refactor(language): export ZModelServices type * fix(cli): improve sqlite introspection for autoincrement and fk names * feat(cli): enhance field matching logic during pull by using relation fields * refactor(cli): refine relation name generation and table syncing * test(cli): update pull tests to reflect improved schema generation * test(cli): add MySQL support to test utility helpers Extends the testing infrastructure to support MySQL databases. Adds MySQL configuration defaults and environment variable overrides. Updates the prelude generation logic to handle MySQL connection strings and provider types, enabling broader database integration testing across the CLI. * fix(cli): omit default constraint names in table sync Avoids explicitly declaring unique constraint names when they match the default database naming convention. This results in cleaner generated schema code by removing redundant mapping arguments. * fix: correctly handle default values for 'text' type in PostgreSQL * fix: sort table indexes to ensure stable schema generation * refactor: dynamically determine supported db providers in CLI * test: fix typo in pull test description * chore(cli): remove debug artifacts and silence test logs Removes hardcoded file system path debugging and unnecessary console logging from the introspector and test suites. Silences CLI command output during tests to provide a cleaner test execution environment. * fix(cli): ensure MySQL column and index ordering Wraps JSON_ARRAYAGG calls in subqueries with explicit ORDER BY clauses to maintain correct metadata ordering. This addresses a limitation in MySQL versions prior to 8.0.21, where ORDER BY is not supported directly within the JSON_ARRAYAGG function, ensuring consistent introspection results across different database versions. * fix(cli): preserve column order during MySQL pull Ensures database columns are sorted by their ordinal position during the introspection process. This maintains the original schema structure and provides a consistent output that matches the physical database layout. * refactor(cli): remove schema fields from MySQL queries Eliminates redundant schema and database name fields from the MySQL introspection query. Since MySQL does not support multi-schema architectures internal to a single connection in this context, removing these fields simplifies the data structure and avoids unnecessary metadata overhead. * fix(cli): improve MySQL introspection and index mapping Refines the database pull process to better handle MySQL-specific patterns. Improves unique constraint detection to prevent redundant mapping attributes when default naming conventions are used. Updates the MySQL introspection logic to correctly identify boolean types, handle timestamp precision in default values, and normalize numeric defaults. Also ensures auto-incrementing columns and primary key indexes are correctly mapped to prevent schema duplication. * test(cli): pass provider to default prelude in tests Ensures that the default schema prelude correctly reflects the database provider specified in test options. This prevents inconsistencies when generating test projects with non-default providers. * fix(cli): improve MySQL introspection for types and defaults Disables NativeEnum support for MySQL to prevent loss of schema-level enums since MySQL enums are column-specific. Refines boolean and numeric type mapping to better handle synthetic boolean types and preserve decimal precision in default values. Updates default value parsing logic to correctly identify unquoted strings and avoid misinterpreting numeric literals as booleans. * fix(cli): improve MySQL default value introspection Refines how default values are handled during database introspection for MySQL by considering the specific field type. This ensures that boolean variants and numeric literals for Float and Decimal types are correctly formatted and preserved. Also clarifies unsupported features in the SQLite provider to improve codebase maintainability. * test(cli): expand and reorganize db pull tests Enhances the test suite for the database pull command by adding comprehensive coverage for common schema features and PostgreSQL-specific functionality. Includes new test cases for: - Restoring complex schemas from scratch, including relations and indexes - Preserving existing imports in multi-file schema setups - Handling PostgreSQL-specific features like multi-schema support and native enums - Verifying schema preservation for field and table mappings The tests are restructured for better clarity across different database providers. * refactor: restructure introspection provider interface and attribute generation * feat: modernize MySQL introspection provider * feat: modernize PostgreSQL introspection provider * feat: modernize SQLite introspection provider * fix: improve relation field naming and default action handling * feat: track imports and auto-format during db pull * test: update pull tests to reflect naming and formatting improvements * fix(cli): refactor PostgreSQL type casting and fix index order Extracts PostgreSQL type casting logic into a reusable helper function to improve maintainability and ensure consistent attribute handling across all field types. Adjusts the table index sorting logic to better preserve the original database creation order while maintaining the priority of unique indexes. * fix(cli): filter out auto-generated MySQL indexes Prevents foreign key indexes created automatically by MySQL from appearing in the introspected schema. This ensures the output reflects manually defined indexes and avoids redundancy in schema definitions. * test(cli): support datasource extras in test utils Enhances the test utility helpers to allow passing extra datasource properties, such as multi-schema configurations for PostgreSQL. Refactors existing database pull tests to use these extra properties, ensuring the generated ZModel schema correctly reflects multi-schema environments while simplifying assertions. * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix(cli): improve file path resolution in pull action * refactor(cli): extract and enhance name casing logic * refactor(cli): consolidate default value normalization * feat(cli): improve enum syncing and relation naming during pull * docs(cli): add documentation comments to SQL introspection queries * test(cli): refactor test utilities and modernize test suites * fix(cli): improve db pull for composite FKs and MySQL uniqueness Enhances database introspection to correctly handle composite foreign keys by mapping columns by position rather than name alone. Improves MySQL introspection by checking statistics tables for single-column unique indexes, ensuring accurate model generation even when column keys are ambiguous. Ensures MySQL synthetic enum names respect requested model casing to prevent unnecessary schema mapping. Adds comprehensive tests for composite relations and database-specific uniqueness detection. * fix: address PR comments * fix(cli): improve SQLite introspection for untyped columns and composite FKs Ensures columns with no declared type are correctly mapped to Bytes following SQLite affinity rules, preventing them from being marked as Unsupported. Updates the DDL parser to correctly identify and map constraint names for composite foreign keys. This ensures that multi-column relations are properly restored during the pull process. Adds regression tests for both untyped columns and composite foreign key restoration. * feat(cli): pull generated/computed columns as Unsupported type Improves database introspection by identifying generated columns in MySQL, PostgreSQL, and SQLite. These columns are now pulled as `Unsupported` types containing their full DDL definition, preventing issues where read-only database fields were incorrectly treated as writable application-level fields. Includes normalization for expression formatting and a fix for string literal escaping in the code generator to ensure stable schema output. Relates to ZModel introspection consistency. * fix(cli): Use parameterized queries for MySQL introspection Switches from template literal interpolation to parameterized queries in MySQL introspection functions. This improves security by preventing potential SQL injection and ensures better handling of database names containing special characters. * fix(cli): use nullish coalescing for precision check
2026-02-08 02:02:26 +00:00
posts Post[]
metadata Json?
@@schema('auth')
@@index([status])
}
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId Int
tags String[]
@@schema('content')
@@index([authorId])
}
enum Status {
feat: db pull implementation (#268) * feat: initial db pull implementation * fix: generate imports and attributes for zmodel-code-generator * fix: add option to not exclude imports in loadDocument * fix: continue work on db pull * fix: missing import * fix: rewrite model generation generate model from ground up and diff later * feat: add ast factory * fix: ast factory import order * fix: some runtime bugs * fix: lint fix * fix: update zmodel code generator - include imports in output - fix indentaions - include comments in output * feat: add exclude schemas option * feat: implement initial diff update * fix: update format in zmodel code generator * fix: typo * feat: progress on database introspection and syncing * fix: make ignore behave it does in prisma with no index models * fix: lint fix * feat: make all format options configurable * fix: lint fix * feat: Handle the database type mapping * fix: catch up with feature updates - improve code styling - enable schema support for db pull * fix: add sqlite e2e test and fix some bugs * fix: lint fix * fix: formatting for e2e test schemas * test: run db pull e2e test also for postgres * fix: postgres instorspection schema filter * test: update cli tests * feat(cli): Improves database introspection and syncing Enhances the `db pull` command with a spinner for better UX. Adds color-coded logging to highlight important steps. Provides more detailed output on schema changes, including deleted models, enums, added fields, and deleted attributes. Also includes minor improvements to enum mapping and constraint handling. * fix(cli): fixes field casing and sort issues * chore(cli): remove temporary test script Deletes an unused script used for experimenting with URI path resolution. Cleans up the codebase by removing development-only artifacts. * chore: update pnpm-lock.yaml * feat(cli): add MySQL support for schema introspection Introduces a MySQL-specific introspection provider to support pulling existing database schemas into ZenStack. The implementation includes logic for mapping MySQL data types to ZenStack types, handling auto-incrementing fields, and parsing MySQL-specific enum definitions. It utilizes dynamic imports for database drivers to minimize the CLI footprint for users not targeting MySQL. * fix(cli): improve field matching logic during db pull * feat(cli): enhance SQLite introspection with autoincrement support * fix(cli): refine attribute generation in db pull * test(cli): update db pull tests for SQLite specific behavior * refactor(language): export ZModelServices type * fix(cli): improve sqlite introspection for autoincrement and fk names * feat(cli): enhance field matching logic during pull by using relation fields * refactor(cli): refine relation name generation and table syncing * test(cli): update pull tests to reflect improved schema generation * test(cli): add MySQL support to test utility helpers Extends the testing infrastructure to support MySQL databases. Adds MySQL configuration defaults and environment variable overrides. Updates the prelude generation logic to handle MySQL connection strings and provider types, enabling broader database integration testing across the CLI. * fix(cli): omit default constraint names in table sync Avoids explicitly declaring unique constraint names when they match the default database naming convention. This results in cleaner generated schema code by removing redundant mapping arguments. * fix: correctly handle default values for 'text' type in PostgreSQL * fix: sort table indexes to ensure stable schema generation * refactor: dynamically determine supported db providers in CLI * test: fix typo in pull test description * chore(cli): remove debug artifacts and silence test logs Removes hardcoded file system path debugging and unnecessary console logging from the introspector and test suites. Silences CLI command output during tests to provide a cleaner test execution environment. * fix(cli): ensure MySQL column and index ordering Wraps JSON_ARRAYAGG calls in subqueries with explicit ORDER BY clauses to maintain correct metadata ordering. This addresses a limitation in MySQL versions prior to 8.0.21, where ORDER BY is not supported directly within the JSON_ARRAYAGG function, ensuring consistent introspection results across different database versions. * fix(cli): preserve column order during MySQL pull Ensures database columns are sorted by their ordinal position during the introspection process. This maintains the original schema structure and provides a consistent output that matches the physical database layout. * refactor(cli): remove schema fields from MySQL queries Eliminates redundant schema and database name fields from the MySQL introspection query. Since MySQL does not support multi-schema architectures internal to a single connection in this context, removing these fields simplifies the data structure and avoids unnecessary metadata overhead. * fix(cli): improve MySQL introspection and index mapping Refines the database pull process to better handle MySQL-specific patterns. Improves unique constraint detection to prevent redundant mapping attributes when default naming conventions are used. Updates the MySQL introspection logic to correctly identify boolean types, handle timestamp precision in default values, and normalize numeric defaults. Also ensures auto-incrementing columns and primary key indexes are correctly mapped to prevent schema duplication. * test(cli): pass provider to default prelude in tests Ensures that the default schema prelude correctly reflects the database provider specified in test options. This prevents inconsistencies when generating test projects with non-default providers. * fix(cli): improve MySQL introspection for types and defaults Disables NativeEnum support for MySQL to prevent loss of schema-level enums since MySQL enums are column-specific. Refines boolean and numeric type mapping to better handle synthetic boolean types and preserve decimal precision in default values. Updates default value parsing logic to correctly identify unquoted strings and avoid misinterpreting numeric literals as booleans. * fix(cli): improve MySQL default value introspection Refines how default values are handled during database introspection for MySQL by considering the specific field type. This ensures that boolean variants and numeric literals for Float and Decimal types are correctly formatted and preserved. Also clarifies unsupported features in the SQLite provider to improve codebase maintainability. * test(cli): expand and reorganize db pull tests Enhances the test suite for the database pull command by adding comprehensive coverage for common schema features and PostgreSQL-specific functionality. Includes new test cases for: - Restoring complex schemas from scratch, including relations and indexes - Preserving existing imports in multi-file schema setups - Handling PostgreSQL-specific features like multi-schema support and native enums - Verifying schema preservation for field and table mappings The tests are restructured for better clarity across different database providers. * refactor: restructure introspection provider interface and attribute generation * feat: modernize MySQL introspection provider * feat: modernize PostgreSQL introspection provider * feat: modernize SQLite introspection provider * fix: improve relation field naming and default action handling * feat: track imports and auto-format during db pull * test: update pull tests to reflect naming and formatting improvements * fix(cli): refactor PostgreSQL type casting and fix index order Extracts PostgreSQL type casting logic into a reusable helper function to improve maintainability and ensure consistent attribute handling across all field types. Adjusts the table index sorting logic to better preserve the original database creation order while maintaining the priority of unique indexes. * fix(cli): filter out auto-generated MySQL indexes Prevents foreign key indexes created automatically by MySQL from appearing in the introspected schema. This ensures the output reflects manually defined indexes and avoids redundancy in schema definitions. * test(cli): support datasource extras in test utils Enhances the test utility helpers to allow passing extra datasource properties, such as multi-schema configurations for PostgreSQL. Refactors existing database pull tests to use these extra properties, ensuring the generated ZModel schema correctly reflects multi-schema environments while simplifying assertions. * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix(cli): improve file path resolution in pull action * refactor(cli): extract and enhance name casing logic * refactor(cli): consolidate default value normalization * feat(cli): improve enum syncing and relation naming during pull * docs(cli): add documentation comments to SQL introspection queries * test(cli): refactor test utilities and modernize test suites * fix(cli): improve db pull for composite FKs and MySQL uniqueness Enhances database introspection to correctly handle composite foreign keys by mapping columns by position rather than name alone. Improves MySQL introspection by checking statistics tables for single-column unique indexes, ensuring accurate model generation even when column keys are ambiguous. Ensures MySQL synthetic enum names respect requested model casing to prevent unnecessary schema mapping. Adds comprehensive tests for composite relations and database-specific uniqueness detection. * fix: address PR comments * fix(cli): improve SQLite introspection for untyped columns and composite FKs Ensures columns with no declared type are correctly mapped to Bytes following SQLite affinity rules, preventing them from being marked as Unsupported. Updates the DDL parser to correctly identify and map constraint names for composite foreign keys. This ensures that multi-column relations are properly restored during the pull process. Adds regression tests for both untyped columns and composite foreign key restoration. * feat(cli): pull generated/computed columns as Unsupported type Improves database introspection by identifying generated columns in MySQL, PostgreSQL, and SQLite. These columns are now pulled as `Unsupported` types containing their full DDL definition, preventing issues where read-only database fields were incorrectly treated as writable application-level fields. Includes normalization for expression formatting and a fix for string literal escaping in the code generator to ensure stable schema output. Relates to ZModel introspection consistency. * fix(cli): Use parameterized queries for MySQL introspection Switches from template literal interpolation to parameterized queries in MySQL introspection functions. This improves security by preventing potential SQL injection and ensures better handling of database names containing special characters. * fix(cli): use nullish coalescing for precision check
2026-02-08 02:02:26 +00:00
ACTIVE
INACTIVE
SUSPENDED
}`,
{ provider: 'postgresql', datasourceFields:{ schemas: ['public', 'content', 'auth'] } },
);
runCli('db push', workDir);
runCli('db pull --indent 4', workDir);
expect(getSchema(workDir)).toEqual(schema);
});
it('should restore native type attributes from PostgreSQL typnames', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'postgresql') {
skip();
return;
}
// PostgreSQL introspection returns typnames like 'int2', 'float8', 'bpchar',
// but Prisma/ZenStack attributes are named @db.SmallInt, @db.DoublePrecision, @db.Char, etc.
// This test verifies the mapping works correctly.
// Note: Default native types (jsonb for Json, bytea for Bytes) are not added when pulling from zero
// because they match the default database type for that field type.
const { workDir } = await createProject(
`model TypeTest {
id Int @id @default(autoincrement())
smallNumber Int @db.SmallInt()
realNumber Float @db.Real()
doubleNum Float @db.DoublePrecision()
fixedChar String @db.Char(10)
uuid String @db.Uuid()
jsonData Json @db.Json()
jsonbData Json @db.JsonB()
binaryData Bytes @db.ByteA()
}`,
{ provider: 'postgresql' },
);
runCli('db push', workDir);
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
// Remove schema content to simulate restoration from zero
fs.writeFileSync(schemaFile, getDefaultPrelude({ provider: 'postgresql' }));
// Pull should restore non-default native type attributes
// Default types (jsonb for Json, bytea for Bytes) are not added
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
// Verify key native type mappings are restored correctly:
// - @db.SmallInt for int2 (non-default for Int which defaults to integer/int4)
// - @db.Real for float4 (non-default for Float which defaults to double precision/float8)
// - @db.Char(10) for bpchar with length (non-default for String which defaults to text)
// - @db.Uuid for uuid (non-default for String which defaults to text)
// - @db.Json for json (non-default for Json which defaults to jsonb)
expect(restoredSchema).toContain('@db.SmallInt');
expect(restoredSchema).toContain('@db.Real');
expect(restoredSchema).toContain('@db.Char(10)');
expect(restoredSchema).toContain('@db.Uuid');
expect(restoredSchema).toContain('@db.Json');
// Default types should NOT be added when pulling from zero
expect(restoredSchema).not.toContain('@db.Integer'); // integer is default for Int
expect(restoredSchema).not.toContain('@db.DoublePrecision'); // double precision is default for Float
expect(restoredSchema).not.toContain('@db.JsonB'); // jsonb is default for Json
expect(restoredSchema).not.toContain('@db.ByteA'); // bytea is default for Bytes
});
it('should correctly map composite foreign key columns by position', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'postgresql') {
skip();
return;
}
// Composite FK: (tenantId, authorId) REFERENCES Tenant(tenantId, userId)
// The introspection must correlate by position, not match each source column
// to every target column. Without the fix, tenantId would incorrectly map to
// both tenantId AND userId in the target table.
const { workDir, schema } = await createProject(
`model Post {
id Int @id @default(autoincrement())
title String
tenant Tenant @relation(fields: [tenantId, authorId], references: [tenantId, userId], onDelete: Cascade)
tenantId Int
authorId Int
@@index([tenantId, authorId])
}
model Tenant {
tenantId Int
userId Int
name String
posts Post[]
@@id([tenantId, userId])
}`,
{ provider: 'postgresql' },
);
runCli('db push', workDir);
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude({ provider: 'postgresql' }));
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
it('should pull stored generated columns as Unsupported with full expression', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'postgresql') {
skip();
return;
}
// PostgreSQL supports GENERATED ALWAYS AS (expr) STORED since PG 12.
// The introspection should include the full generation expression in the
// datatype so it is rendered as Unsupported("type GENERATED ALWAYS AS (expr) STORED").
// 1. Create a project with a base table (we need the DB to exist first)
const { workDir } = await createProject(
`model ComputedUsers {
id Int @id @default(autoincrement())
firstName String
lastName String
}`,
{ provider: 'postgresql' },
);
runCli('db push', workDir);
// 2. Add a generated column via raw SQL (can't be defined in ZModel)
const { Client } = await import('pg');
const dbName = getTestDbName('postgresql');
const client = new Client({ connectionString: getTestDbUrl('postgresql', dbName) });
await client.connect();
try {
await client.query(
`ALTER TABLE "ComputedUsers" ADD COLUMN "fullName" text GENERATED ALWAYS AS ("firstName" || ' ' || "lastName") STORED`
);
} finally {
await client.end();
}
// 3. Pull from zero
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude({ provider: 'postgresql' }));
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
// The generated column should be pulled as Unsupported with the full expression.
// format_type returns 'text', and pg_get_expr returns the expression.
expect(restoredSchema).toEqual(await formatDocument(`${getDefaultPrelude({ provider: 'postgresql' })}
model ComputedUsers {
id Int @id @default(autoincrement())
firstName String
lastName String
fullName Unsupported('text GENERATED ALWAYS AS ((("firstName" || \\' \\'::text) || "lastName")) STORED')?
}`));
});
it('should pull virtual generated columns as Unsupported with full expression', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'postgresql') {
skip();
return;
}
// PostgreSQL 17+ supports VIRTUAL generated columns.
// For earlier versions, only STORED is supported, so this test may need to be
// adapted. We test STORED here since it's universally supported.
const { workDir } = await createProject(
`model ComputedProducts {
id Int @id @default(autoincrement())
price Int @default(0)
qty Int @default(0)
}`,
{ provider: 'postgresql' },
);
runCli('db push', workDir);
const { Client } = await import('pg');
const dbName = getTestDbName('postgresql');
const client = new Client({ connectionString: getTestDbUrl('postgresql', dbName) });
await client.connect();
try {
await client.query(
`ALTER TABLE "ComputedProducts" ADD COLUMN "total" integer GENERATED ALWAYS AS ("price" * "qty") STORED`
);
} finally {
await client.end();
}
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude({ provider: 'postgresql' }));
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(await formatDocument(`${getDefaultPrelude({ provider: 'postgresql' })}
model ComputedProducts {
id Int @id @default(autoincrement())
price Int @default(0)
qty Int @default(0)
total Unsupported('integer GENERATED ALWAYS AS ((price * qty)) STORED')?
}`));
});
});
describe('DB pull - MySQL specific features', () => {
it('should detect single-column unique indexes via STATISTICS', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'mysql') {
skip();
return;
}
// MySQL's COLUMN_KEY may not reliably reflect unique indexes in all cases.
// The introspection should also check INFORMATION_SCHEMA.STATISTICS for
// NON_UNIQUE = 0 single-column indexes to correctly detect uniqueness,
// so that the index-processing skip logic (which checks index.unique +
// single-column) doesn't cause a missing @unique attribute.
const { workDir, schema } = await createProject(
`model User {
id Int @id @default(autoincrement())
email String @unique
nickname String? @unique
}`,
{ provider: 'mysql' },
);
runCli('db push', workDir);
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
// Pull from zero to test introspection detects unique columns correctly
fs.writeFileSync(schemaFile, getDefaultPrelude({ provider: 'mysql' }));
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
it('should pull stored generated columns as Unsupported with full expression', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'mysql') {
skip();
return;
}
// MySQL supports both VIRTUAL and STORED generated columns.
// The introspection should include the full generation expression in the
// datatype so it is rendered as Unsupported("type GENERATED ALWAYS AS (expr) STORED").
// 1. Create a project with a base table (we need the DB to exist first)
const { workDir } = await createProject(
`model ComputedUsers {
id Int @id @default(autoincrement())
firstName String @db.VarChar(255)
lastName String @db.VarChar(255)
}`,
{ provider: 'mysql' },
);
runCli('db push', workDir);
// 2. Add a generated column via raw SQL (can't be defined in ZModel)
const mysql = await import('mysql2/promise');
const dbName = getTestDbName('mysql');
const connection = await mysql.createConnection(getTestDbUrl('mysql', dbName));
try {
await connection.execute(
"ALTER TABLE `ComputedUsers` ADD COLUMN `fullName` varchar(511) GENERATED ALWAYS AS (CONCAT(`firstName`, ' ', `lastName`)) STORED"
);
} finally {
await connection.end();
}
// 3. Pull from zero
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude({ provider: 'mysql' }));
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
// The generated column should be pulled as Unsupported with the full expression.
// MySQL uses COLUMN_TYPE (e.g., 'varchar(511)') and GENERATION_EXPRESSION for the expr,
// and EXTRA contains 'STORED GENERATED' or 'VIRTUAL GENERATED'.
expect(restoredSchema).toEqual(await formatDocument(`${getDefaultPrelude({ provider: 'mysql' })}
model ComputedUsers {
id Int @id @default(autoincrement())
firstName String @db.VarChar(255)
lastName String @db.VarChar(255)
fullName Unsupported('varchar(511) GENERATED ALWAYS AS (concat(\`firstName\`,\\' \\',\`lastName\`)) STORED')?
}`));
});
it('should pull virtual generated columns as Unsupported with full expression', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'mysql') {
skip();
return;
}
const { workDir } = await createProject(
`model ComputedProducts {
id Int @id @default(autoincrement())
price Int @default(0)
qty Int @default(0)
}`,
{ provider: 'mysql' },
);
runCli('db push', workDir);
const mysql = await import('mysql2/promise');
const dbName = getTestDbName('mysql');
const connection = await mysql.createConnection(getTestDbUrl('mysql', dbName));
try {
await connection.execute(
"ALTER TABLE `ComputedProducts` ADD COLUMN `total` int GENERATED ALWAYS AS (`price` * `qty`) VIRTUAL"
);
} finally {
await connection.end();
}
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude({ provider: 'mysql' }));
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(await formatDocument(`${getDefaultPrelude({ provider: 'mysql' })}
model ComputedProducts {
id Int @id @default(autoincrement())
price Int @default(0)
qty Int @default(0)
total Unsupported('int GENERATED ALWAYS AS ((\`price\` * \`qty\`)) VIRTUAL')?
}`));
});
});
describe('DB pull - SQLite specific features', () => {
it('should restore composite foreign key relations', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'sqlite') {
skip();
return;
}
// Composite FK: (tenantId, authorId) REFERENCES Tenant(tenantId, userId).
// The SQLite introspection extracts FK constraint names by parsing the
// CREATE TABLE DDL. The current regex only captures a single column inside
// FOREIGN KEY(...), so composite FK constraint names are lost. Without a
// constraint name, the downstream relation grouping (pull/index.ts) skips
// the FK columns entirely and the relation is not restored.
const { workDir, schema } = await createProject(
`model Post {
id Int @id @default(autoincrement())
title String
tenant Tenant @relation(fields: [tenantId, authorId], references: [tenantId, userId], onDelete: Cascade)
tenantId Int
authorId Int
@@index([tenantId, authorId])
}
model Tenant {
tenantId Int
userId Int
name String
posts Post[]
@@id([tenantId, userId])
}`,
);
runCli('db push', workDir);
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude());
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(schema);
});
it('should map columns without a declared type to Bytes', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'sqlite') {
skip();
return;
}
// Create a minimal project and push to get the database file.
const { workDir } = await createProject("");
// Open the SQLite database directly and add a table with an untyped column.
// In SQLite, CREATE TABLE t("data") gives column "data" no declared type,
// which per affinity rules means BLOB affinity — should map to Bytes.
const dbPath = path.join(workDir, 'zenstack', 'test.db');
const SQLite = (await import('better-sqlite3')).default;
const db = new SQLite(dbPath);
db.exec('CREATE TABLE "UntypedTest" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "data")');
db.close();
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude());
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
// The untyped "data" column should be pulled as Bytes (BLOB affinity),
// not as Unsupported.
expect(restoredSchema).toContain('data Bytes?');
expect(restoredSchema).not.toContain('Unsupported');
});
it('should pull stored generated columns as Unsupported', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'sqlite') {
skip();
return;
}
// SQLite PRAGMA table_xinfo reports generated columns with hidden values:
// hidden = 2 → VIRTUAL generated column
// hidden = 3 → STORED generated column
// Both types should be pulled as Unsupported("full type definition")
// because generated columns are read-only and cannot be written to.
const { workDir } = await createProject('');
const dbPath = path.join(workDir, 'zenstack', 'test.db');
const SQLite = (await import('better-sqlite3')).default;
const db = new SQLite(dbPath);
db.exec(`
CREATE TABLE "ComputedUsers" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"firstName" TEXT NOT NULL,
"lastName" TEXT NOT NULL,
"fullName" TEXT GENERATED ALWAYS AS (firstName || ' ' || lastName) STORED
)
`);
db.close();
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude());
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
// first_name and last_name should be regular String fields
expect(restoredSchema).toEqual(await formatDocument(`${getDefaultPrelude()}
model ComputedUsers {
id Int @id @default(autoincrement())
firstName String
lastName String
fullName Unsupported('TEXT GENERATED ALWAYS AS (firstName || \\' \\' || lastName) STORED')?
}`));
});
it('should pull virtual generated columns as Unsupported', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'sqlite') {
skip();
return;
}
const { workDir } = await createProject('');
const dbPath = path.join(workDir, 'zenstack', 'test.db');
const SQLite = (await import('better-sqlite3')).default;
const db = new SQLite(dbPath);
db.exec(`
CREATE TABLE "ComputedProducts" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"price" INTEGER NOT NULL DEFAULT 0,
"qty" INTEGER NOT NULL DEFAULT 0,
"total" INTEGER GENERATED ALWAYS AS ("price" * "qty") VIRTUAL
)
`);
db.close();
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
fs.writeFileSync(schemaFile, getDefaultPrelude());
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toEqual(await formatDocument(`${getDefaultPrelude()}
model ComputedProducts {
id Int @id @default(autoincrement())
price Int @default(0)
qty Int @default(0)
total Unsupported('INTEGER GENERATED ALWAYS AS ("price" * "qty") VIRTUAL')?
}`));
});
});
describe('DB pull - SQL specific features', () => {
it('should restore enum fields from zero', async ({ skip }) => {
const provider = getTestDbProvider();
if (provider !== 'mysql' && provider !== 'postgresql') {
skip();
return;
}
const { workDir, schema } = await createProject(
`model User {
id Int @id @default(autoincrement())
email String @unique
feat: db pull implementation (#268) * feat: initial db pull implementation * fix: generate imports and attributes for zmodel-code-generator * fix: add option to not exclude imports in loadDocument * fix: continue work on db pull * fix: missing import * fix: rewrite model generation generate model from ground up and diff later * feat: add ast factory * fix: ast factory import order * fix: some runtime bugs * fix: lint fix * fix: update zmodel code generator - include imports in output - fix indentaions - include comments in output * feat: add exclude schemas option * feat: implement initial diff update * fix: update format in zmodel code generator * fix: typo * feat: progress on database introspection and syncing * fix: make ignore behave it does in prisma with no index models * fix: lint fix * feat: make all format options configurable * fix: lint fix * feat: Handle the database type mapping * fix: catch up with feature updates - improve code styling - enable schema support for db pull * fix: add sqlite e2e test and fix some bugs * fix: lint fix * fix: formatting for e2e test schemas * test: run db pull e2e test also for postgres * fix: postgres instorspection schema filter * test: update cli tests * feat(cli): Improves database introspection and syncing Enhances the `db pull` command with a spinner for better UX. Adds color-coded logging to highlight important steps. Provides more detailed output on schema changes, including deleted models, enums, added fields, and deleted attributes. Also includes minor improvements to enum mapping and constraint handling. * fix(cli): fixes field casing and sort issues * chore(cli): remove temporary test script Deletes an unused script used for experimenting with URI path resolution. Cleans up the codebase by removing development-only artifacts. * chore: update pnpm-lock.yaml * feat(cli): add MySQL support for schema introspection Introduces a MySQL-specific introspection provider to support pulling existing database schemas into ZenStack. The implementation includes logic for mapping MySQL data types to ZenStack types, handling auto-incrementing fields, and parsing MySQL-specific enum definitions. It utilizes dynamic imports for database drivers to minimize the CLI footprint for users not targeting MySQL. * fix(cli): improve field matching logic during db pull * feat(cli): enhance SQLite introspection with autoincrement support * fix(cli): refine attribute generation in db pull * test(cli): update db pull tests for SQLite specific behavior * refactor(language): export ZModelServices type * fix(cli): improve sqlite introspection for autoincrement and fk names * feat(cli): enhance field matching logic during pull by using relation fields * refactor(cli): refine relation name generation and table syncing * test(cli): update pull tests to reflect improved schema generation * test(cli): add MySQL support to test utility helpers Extends the testing infrastructure to support MySQL databases. Adds MySQL configuration defaults and environment variable overrides. Updates the prelude generation logic to handle MySQL connection strings and provider types, enabling broader database integration testing across the CLI. * fix(cli): omit default constraint names in table sync Avoids explicitly declaring unique constraint names when they match the default database naming convention. This results in cleaner generated schema code by removing redundant mapping arguments. * fix: correctly handle default values for 'text' type in PostgreSQL * fix: sort table indexes to ensure stable schema generation * refactor: dynamically determine supported db providers in CLI * test: fix typo in pull test description * chore(cli): remove debug artifacts and silence test logs Removes hardcoded file system path debugging and unnecessary console logging from the introspector and test suites. Silences CLI command output during tests to provide a cleaner test execution environment. * fix(cli): ensure MySQL column and index ordering Wraps JSON_ARRAYAGG calls in subqueries with explicit ORDER BY clauses to maintain correct metadata ordering. This addresses a limitation in MySQL versions prior to 8.0.21, where ORDER BY is not supported directly within the JSON_ARRAYAGG function, ensuring consistent introspection results across different database versions. * fix(cli): preserve column order during MySQL pull Ensures database columns are sorted by their ordinal position during the introspection process. This maintains the original schema structure and provides a consistent output that matches the physical database layout. * refactor(cli): remove schema fields from MySQL queries Eliminates redundant schema and database name fields from the MySQL introspection query. Since MySQL does not support multi-schema architectures internal to a single connection in this context, removing these fields simplifies the data structure and avoids unnecessary metadata overhead. * fix(cli): improve MySQL introspection and index mapping Refines the database pull process to better handle MySQL-specific patterns. Improves unique constraint detection to prevent redundant mapping attributes when default naming conventions are used. Updates the MySQL introspection logic to correctly identify boolean types, handle timestamp precision in default values, and normalize numeric defaults. Also ensures auto-incrementing columns and primary key indexes are correctly mapped to prevent schema duplication. * test(cli): pass provider to default prelude in tests Ensures that the default schema prelude correctly reflects the database provider specified in test options. This prevents inconsistencies when generating test projects with non-default providers. * fix(cli): improve MySQL introspection for types and defaults Disables NativeEnum support for MySQL to prevent loss of schema-level enums since MySQL enums are column-specific. Refines boolean and numeric type mapping to better handle synthetic boolean types and preserve decimal precision in default values. Updates default value parsing logic to correctly identify unquoted strings and avoid misinterpreting numeric literals as booleans. * fix(cli): improve MySQL default value introspection Refines how default values are handled during database introspection for MySQL by considering the specific field type. This ensures that boolean variants and numeric literals for Float and Decimal types are correctly formatted and preserved. Also clarifies unsupported features in the SQLite provider to improve codebase maintainability. * test(cli): expand and reorganize db pull tests Enhances the test suite for the database pull command by adding comprehensive coverage for common schema features and PostgreSQL-specific functionality. Includes new test cases for: - Restoring complex schemas from scratch, including relations and indexes - Preserving existing imports in multi-file schema setups - Handling PostgreSQL-specific features like multi-schema support and native enums - Verifying schema preservation for field and table mappings The tests are restructured for better clarity across different database providers. * refactor: restructure introspection provider interface and attribute generation * feat: modernize MySQL introspection provider * feat: modernize PostgreSQL introspection provider * feat: modernize SQLite introspection provider * fix: improve relation field naming and default action handling * feat: track imports and auto-format during db pull * test: update pull tests to reflect naming and formatting improvements * fix(cli): refactor PostgreSQL type casting and fix index order Extracts PostgreSQL type casting logic into a reusable helper function to improve maintainability and ensure consistent attribute handling across all field types. Adjusts the table index sorting logic to better preserve the original database creation order while maintaining the priority of unique indexes. * fix(cli): filter out auto-generated MySQL indexes Prevents foreign key indexes created automatically by MySQL from appearing in the introspected schema. This ensures the output reflects manually defined indexes and avoids redundancy in schema definitions. * test(cli): support datasource extras in test utils Enhances the test utility helpers to allow passing extra datasource properties, such as multi-schema configurations for PostgreSQL. Refactors existing database pull tests to use these extra properties, ensuring the generated ZModel schema correctly reflects multi-schema environments while simplifying assertions. * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix(cli): improve file path resolution in pull action * refactor(cli): extract and enhance name casing logic * refactor(cli): consolidate default value normalization * feat(cli): improve enum syncing and relation naming during pull * docs(cli): add documentation comments to SQL introspection queries * test(cli): refactor test utilities and modernize test suites * fix(cli): improve db pull for composite FKs and MySQL uniqueness Enhances database introspection to correctly handle composite foreign keys by mapping columns by position rather than name alone. Improves MySQL introspection by checking statistics tables for single-column unique indexes, ensuring accurate model generation even when column keys are ambiguous. Ensures MySQL synthetic enum names respect requested model casing to prevent unnecessary schema mapping. Adds comprehensive tests for composite relations and database-specific uniqueness detection. * fix: address PR comments * fix(cli): improve SQLite introspection for untyped columns and composite FKs Ensures columns with no declared type are correctly mapped to Bytes following SQLite affinity rules, preventing them from being marked as Unsupported. Updates the DDL parser to correctly identify and map constraint names for composite foreign keys. This ensures that multi-column relations are properly restored during the pull process. Adds regression tests for both untyped columns and composite foreign key restoration. * feat(cli): pull generated/computed columns as Unsupported type Improves database introspection by identifying generated columns in MySQL, PostgreSQL, and SQLite. These columns are now pulled as `Unsupported` types containing their full DDL definition, preventing issues where read-only database fields were incorrectly treated as writable application-level fields. Includes normalization for expression formatting and a fix for string literal escaping in the code generator to ensure stable schema output. Relates to ZModel introspection consistency. * fix(cli): Use parameterized queries for MySQL introspection Switches from template literal interpolation to parameterized queries in MySQL introspection functions. This improves security by preventing potential SQL injection and ensures better handling of database names containing special characters. * fix(cli): use nullish coalescing for precision check
2026-02-08 02:02:26 +00:00
status UserStatus @default(ACTIVE)
}
enum UserStatus {
ACTIVE
INACTIVE
SUSPENDED
}`);
runCli('db push', workDir);
const schemaFile = path.join(workDir, 'zenstack/schema.zmodel');
// Remove schema content to simulate restoration from zero
fs.writeFileSync(schemaFile, getDefaultPrelude());
// Pull should fully restore the schema including enum fields
runCli('db pull --indent 4', workDir);
const restoredSchema = getSchema(workDir);
expect(restoredSchema).toContain(`model User {
id Int @id @default(autoincrement())
email String @unique
status UserStatus @default(ACTIVE)
}`);
expect(restoredSchema).toContain(`enum UserStatus {
ACTIVE
INACTIVE
SUSPENDED
}`);
feat: db pull implementation (#268) * feat: initial db pull implementation * fix: generate imports and attributes for zmodel-code-generator * fix: add option to not exclude imports in loadDocument * fix: continue work on db pull * fix: missing import * fix: rewrite model generation generate model from ground up and diff later * feat: add ast factory * fix: ast factory import order * fix: some runtime bugs * fix: lint fix * fix: update zmodel code generator - include imports in output - fix indentaions - include comments in output * feat: add exclude schemas option * feat: implement initial diff update * fix: update format in zmodel code generator * fix: typo * feat: progress on database introspection and syncing * fix: make ignore behave it does in prisma with no index models * fix: lint fix * feat: make all format options configurable * fix: lint fix * feat: Handle the database type mapping * fix: catch up with feature updates - improve code styling - enable schema support for db pull * fix: add sqlite e2e test and fix some bugs * fix: lint fix * fix: formatting for e2e test schemas * test: run db pull e2e test also for postgres * fix: postgres instorspection schema filter * test: update cli tests * feat(cli): Improves database introspection and syncing Enhances the `db pull` command with a spinner for better UX. Adds color-coded logging to highlight important steps. Provides more detailed output on schema changes, including deleted models, enums, added fields, and deleted attributes. Also includes minor improvements to enum mapping and constraint handling. * fix(cli): fixes field casing and sort issues * chore(cli): remove temporary test script Deletes an unused script used for experimenting with URI path resolution. Cleans up the codebase by removing development-only artifacts. * chore: update pnpm-lock.yaml * feat(cli): add MySQL support for schema introspection Introduces a MySQL-specific introspection provider to support pulling existing database schemas into ZenStack. The implementation includes logic for mapping MySQL data types to ZenStack types, handling auto-incrementing fields, and parsing MySQL-specific enum definitions. It utilizes dynamic imports for database drivers to minimize the CLI footprint for users not targeting MySQL. * fix(cli): improve field matching logic during db pull * feat(cli): enhance SQLite introspection with autoincrement support * fix(cli): refine attribute generation in db pull * test(cli): update db pull tests for SQLite specific behavior * refactor(language): export ZModelServices type * fix(cli): improve sqlite introspection for autoincrement and fk names * feat(cli): enhance field matching logic during pull by using relation fields * refactor(cli): refine relation name generation and table syncing * test(cli): update pull tests to reflect improved schema generation * test(cli): add MySQL support to test utility helpers Extends the testing infrastructure to support MySQL databases. Adds MySQL configuration defaults and environment variable overrides. Updates the prelude generation logic to handle MySQL connection strings and provider types, enabling broader database integration testing across the CLI. * fix(cli): omit default constraint names in table sync Avoids explicitly declaring unique constraint names when they match the default database naming convention. This results in cleaner generated schema code by removing redundant mapping arguments. * fix: correctly handle default values for 'text' type in PostgreSQL * fix: sort table indexes to ensure stable schema generation * refactor: dynamically determine supported db providers in CLI * test: fix typo in pull test description * chore(cli): remove debug artifacts and silence test logs Removes hardcoded file system path debugging and unnecessary console logging from the introspector and test suites. Silences CLI command output during tests to provide a cleaner test execution environment. * fix(cli): ensure MySQL column and index ordering Wraps JSON_ARRAYAGG calls in subqueries with explicit ORDER BY clauses to maintain correct metadata ordering. This addresses a limitation in MySQL versions prior to 8.0.21, where ORDER BY is not supported directly within the JSON_ARRAYAGG function, ensuring consistent introspection results across different database versions. * fix(cli): preserve column order during MySQL pull Ensures database columns are sorted by their ordinal position during the introspection process. This maintains the original schema structure and provides a consistent output that matches the physical database layout. * refactor(cli): remove schema fields from MySQL queries Eliminates redundant schema and database name fields from the MySQL introspection query. Since MySQL does not support multi-schema architectures internal to a single connection in this context, removing these fields simplifies the data structure and avoids unnecessary metadata overhead. * fix(cli): improve MySQL introspection and index mapping Refines the database pull process to better handle MySQL-specific patterns. Improves unique constraint detection to prevent redundant mapping attributes when default naming conventions are used. Updates the MySQL introspection logic to correctly identify boolean types, handle timestamp precision in default values, and normalize numeric defaults. Also ensures auto-incrementing columns and primary key indexes are correctly mapped to prevent schema duplication. * test(cli): pass provider to default prelude in tests Ensures that the default schema prelude correctly reflects the database provider specified in test options. This prevents inconsistencies when generating test projects with non-default providers. * fix(cli): improve MySQL introspection for types and defaults Disables NativeEnum support for MySQL to prevent loss of schema-level enums since MySQL enums are column-specific. Refines boolean and numeric type mapping to better handle synthetic boolean types and preserve decimal precision in default values. Updates default value parsing logic to correctly identify unquoted strings and avoid misinterpreting numeric literals as booleans. * fix(cli): improve MySQL default value introspection Refines how default values are handled during database introspection for MySQL by considering the specific field type. This ensures that boolean variants and numeric literals for Float and Decimal types are correctly formatted and preserved. Also clarifies unsupported features in the SQLite provider to improve codebase maintainability. * test(cli): expand and reorganize db pull tests Enhances the test suite for the database pull command by adding comprehensive coverage for common schema features and PostgreSQL-specific functionality. Includes new test cases for: - Restoring complex schemas from scratch, including relations and indexes - Preserving existing imports in multi-file schema setups - Handling PostgreSQL-specific features like multi-schema support and native enums - Verifying schema preservation for field and table mappings The tests are restructured for better clarity across different database providers. * refactor: restructure introspection provider interface and attribute generation * feat: modernize MySQL introspection provider * feat: modernize PostgreSQL introspection provider * feat: modernize SQLite introspection provider * fix: improve relation field naming and default action handling * feat: track imports and auto-format during db pull * test: update pull tests to reflect naming and formatting improvements * fix(cli): refactor PostgreSQL type casting and fix index order Extracts PostgreSQL type casting logic into a reusable helper function to improve maintainability and ensure consistent attribute handling across all field types. Adjusts the table index sorting logic to better preserve the original database creation order while maintaining the priority of unique indexes. * fix(cli): filter out auto-generated MySQL indexes Prevents foreign key indexes created automatically by MySQL from appearing in the introspected schema. This ensures the output reflects manually defined indexes and avoids redundancy in schema definitions. * test(cli): support datasource extras in test utils Enhances the test utility helpers to allow passing extra datasource properties, such as multi-schema configurations for PostgreSQL. Refactors existing database pull tests to use these extra properties, ensuring the generated ZModel schema correctly reflects multi-schema environments while simplifying assertions. * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix: address PR comments * fix(cli): improve file path resolution in pull action * refactor(cli): extract and enhance name casing logic * refactor(cli): consolidate default value normalization * feat(cli): improve enum syncing and relation naming during pull * docs(cli): add documentation comments to SQL introspection queries * test(cli): refactor test utilities and modernize test suites * fix(cli): improve db pull for composite FKs and MySQL uniqueness Enhances database introspection to correctly handle composite foreign keys by mapping columns by position rather than name alone. Improves MySQL introspection by checking statistics tables for single-column unique indexes, ensuring accurate model generation even when column keys are ambiguous. Ensures MySQL synthetic enum names respect requested model casing to prevent unnecessary schema mapping. Adds comprehensive tests for composite relations and database-specific uniqueness detection. * fix: address PR comments * fix(cli): improve SQLite introspection for untyped columns and composite FKs Ensures columns with no declared type are correctly mapped to Bytes following SQLite affinity rules, preventing them from being marked as Unsupported. Updates the DDL parser to correctly identify and map constraint names for composite foreign keys. This ensures that multi-column relations are properly restored during the pull process. Adds regression tests for both untyped columns and composite foreign key restoration. * feat(cli): pull generated/computed columns as Unsupported type Improves database introspection by identifying generated columns in MySQL, PostgreSQL, and SQLite. These columns are now pulled as `Unsupported` types containing their full DDL definition, preventing issues where read-only database fields were incorrectly treated as writable application-level fields. Includes normalization for expression formatting and a fix for string literal escaping in the code generator to ensure stable schema output. Relates to ZModel introspection consistency. * fix(cli): Use parameterized queries for MySQL introspection Switches from template literal interpolation to parameterized queries in MySQL introspection functions. This improves security by preventing potential SQL injection and ensures better handling of database names containing special characters. * fix(cli): use nullish coalescing for precision check
2026-02-08 02:02:26 +00:00
});
});