From 25d11e04192e8833f2610953d064ef03a82889f3 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Sun, 14 Dec 2025 14:22:25 +0800 Subject: [PATCH 1/3] fix(zmodel): require implicitly used "public" schema to be declared in "schemas" config --- .../src/validators/datasource-validator.ts | 32 +++++++++++++------ .../orm/client-api/pg-custom-schema.test.ts | 31 ++++++++++++++++++ 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/packages/language/src/validators/datasource-validator.ts b/packages/language/src/validators/datasource-validator.ts index b667d2b28..6d1a1e866 100644 --- a/packages/language/src/validators/datasource-validator.ts +++ b/packages/language/src/validators/datasource-validator.ts @@ -1,6 +1,6 @@ import type { ValidationAcceptor } from 'langium'; import { SUPPORTED_PROVIDERS } from '../constants'; -import { DataSource, isConfigArrayExpr, isInvocationExpr, isLiteralExpr } from '../generated/ast'; +import { DataSource, isConfigArrayExpr, isDataModel, isEnum, isInvocationExpr, isLiteralExpr } from '../generated/ast'; import { getStringLiteral } from '../utils'; import { validateDuplicatedDeclarations, type AstValidator } from './common'; @@ -70,14 +70,28 @@ export default class DataSourceValidator implements AstValidator { accept('error', '"schemas" must be an array of string literals', { node: schemasField, }); - } else if ( - // validate `defaultSchema` is included in `schemas` - defaultSchemaValue && - !schemasValue.items.some((e) => getStringLiteral(e) === defaultSchemaValue) - ) { - accept('error', `"${defaultSchemaValue}" must be included in the "schemas" array`, { - node: schemasField, - }); + } else { + const schemasArray = schemasValue.items.map((e) => getStringLiteral(e)!); + + if (defaultSchemaValue) { + // validate `defaultSchema` is included in `schemas` + if (!schemasArray.includes(defaultSchemaValue)) { + accept('error', `"${defaultSchemaValue}" must be included in the "schemas" array`, { + node: schemasField, + }); + } + } else { + // if no explicit default schema is specified, and there are models or enums without '@@schema', + // "public" is implicitly used, so it must be included in the "schemas" array + const hasImplicitPublicSchema = ds.$container.declarations.some( + (d) => (isDataModel(d) || isEnum(d)) && !d.attributes.some((a) => a.decl.$refText === 'schema'), + ); + if (hasImplicitPublicSchema && !schemasArray.includes('public')) { + accept('error', `"public" must be included in the "schemas" array`, { + node: schemasField, + }); + } + } } } } diff --git a/tests/e2e/orm/client-api/pg-custom-schema.test.ts b/tests/e2e/orm/client-api/pg-custom-schema.test.ts index 7f61f498a..3bc746de0 100644 --- a/tests/e2e/orm/client-api/pg-custom-schema.test.ts +++ b/tests/e2e/orm/client-api/pg-custom-schema.test.ts @@ -247,6 +247,37 @@ model Foo { ).rejects.toThrow('"mySchema" must be included in the "schemas" array'); }); + it('requires implicit public schema to be included in schemas', async () => { + await expect( + createTestClient( + ` +datasource db { + provider = 'postgresql' + schemas = ['mySchema'] + url = '$DB_URL' +} + +enum Role { + ADMIN + USER +} + +model Foo { + id Int @id + name String + role Role + @@schema('mySchema') +} + +model Bar { + id Int @id + name String +} +`, + ), + ).rejects.toThrow('"public" must be included in the "schemas" array'); + }); + it('allows specifying schema only on a few models', async () => { let fooQueriesVerified = false; let barQueriesVerified = false; From 94b69b730ce9b2fc0134ebe468ee6532ad1a35f5 Mon Sep 17 00:00:00 2001 From: Yiming Cao Date: Sun, 14 Dec 2025 14:36:10 +0800 Subject: [PATCH 2/3] Update packages/language/src/validators/datasource-validator.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/language/src/validators/datasource-validator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/language/src/validators/datasource-validator.ts b/packages/language/src/validators/datasource-validator.ts index 6d1a1e866..745d2773c 100644 --- a/packages/language/src/validators/datasource-validator.ts +++ b/packages/language/src/validators/datasource-validator.ts @@ -84,7 +84,7 @@ export default class DataSourceValidator implements AstValidator { // if no explicit default schema is specified, and there are models or enums without '@@schema', // "public" is implicitly used, so it must be included in the "schemas" array const hasImplicitPublicSchema = ds.$container.declarations.some( - (d) => (isDataModel(d) || isEnum(d)) && !d.attributes.some((a) => a.decl.$refText === 'schema'), + (d) => (isDataModel(d) || isEnum(d)) && !d.attributes.some((a) => a.decl.$refText === '@@schema'), ); if (hasImplicitPublicSchema && !schemasArray.includes('public')) { accept('error', `"public" must be included in the "schemas" array`, { From 5667acdd4a9eec5f174060d29092007504c7c213 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 14 Dec 2025 14:48:35 +0800 Subject: [PATCH 3/3] test: verify "public" schema not required when all models have explicit @@schema (#496) * Initial plan * Add test for explicit schema usage without public Co-authored-by: ymc9 <104139426+ymc9@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ymc9 <104139426+ymc9@users.noreply.github.com> --- .../orm/client-api/pg-custom-schema.test.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/e2e/orm/client-api/pg-custom-schema.test.ts b/tests/e2e/orm/client-api/pg-custom-schema.test.ts index 3bc746de0..dfe6fe895 100644 --- a/tests/e2e/orm/client-api/pg-custom-schema.test.ts +++ b/tests/e2e/orm/client-api/pg-custom-schema.test.ts @@ -278,6 +278,44 @@ model Bar { ).rejects.toThrow('"public" must be included in the "schemas" array'); }); + it('does not require public schema when all models and enums have explicit schema', async () => { + const db = await createTestClient( + ` +datasource db { + provider = 'postgresql' + schemas = ['mySchema'] + url = '$DB_URL' +} + +enum Role { + ADMIN + USER + @@schema('mySchema') +} + +model Foo { + id Int @id + name String + role Role + @@schema('mySchema') +} + +model Bar { + id Int @id + name String + @@schema('mySchema') +} +`, + { + provider: 'postgresql', + usePrismaPush: true, + }, + ); + + await expect(db.foo.create({ data: { id: 1, name: 'test', role: 'ADMIN' } })).toResolveTruthy(); + await expect(db.bar.create({ data: { id: 1, name: 'test' } })).toResolveTruthy(); + }); + it('allows specifying schema only on a few models', async () => { let fooQueriesVerified = false; let barQueriesVerified = false;