From e97b3e0401b234912490852cc10529ff1f83c44b Mon Sep 17 00:00:00 2001 From: Sarthak Shah <92942966+Not-Sarthak@users.noreply.github.com> Date: Tue, 17 Mar 2026 18:04:04 +0530 Subject: [PATCH 1/4] feat(adapter-pg): accept connection string URL in PrismaPg constructor (#29287) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Adds support for passing a raw connection string URL directly to `PrismaPg` (e.g., `new PrismaPg(process.env.DATABASE_URL)`) - The constructor now accepts `pg.Pool | pg.PoolConfig | string`, converting strings to `{ connectionString: string }` internally - This is a convenience feature — `pg.PoolConfig` already supports `{ connectionString: '...' }`, but accepting a plain string is more ergonomic Related: #29151 ## Test plan - [x] Added unit test verifying `PrismaPgAdapterFactory` accepts a connection string URL and creates a working adapter - [x] All existing adapter-pg tests pass (36/36) - [x] Lint passes with no new warnings > **Note:** The CodSpeed regression (`compile findUnique -97.77%`) is unrelated — this PR only touches `adapter-pg` constructor logic, not query compilation. --- packages/adapter-pg/src/__tests__/pg.test.ts | 11 +++++++++++ packages/adapter-pg/src/pg.ts | 5 ++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/adapter-pg/src/__tests__/pg.test.ts b/packages/adapter-pg/src/__tests__/pg.test.ts index 125163af994d..13b75a5288f0 100644 --- a/packages/adapter-pg/src/__tests__/pg.test.ts +++ b/packages/adapter-pg/src/__tests__/pg.test.ts @@ -34,6 +34,17 @@ describe('PrismaPgAdapterFactory', () => { await adapter.dispose() }) + it('should accept a connection string URL', async () => { + const connectionString = 'postgresql://test:test@localhost:5432/test' + const factory = new PrismaPgAdapterFactory(connectionString) + + expect((factory as any).config).toEqual({ connectionString }) + + const adapter = await factory.connect() + expect(adapter.underlyingDriver().options.connectionString).toBe(connectionString) + await adapter.dispose() + }) + it('should add and remove error event listener when using an external Pool', async () => { const pool = new pg.Pool({ user: 'test', password: 'test', database: 'test', port: 5432, host: 'localhost' }) pool.on('error', () => {}) diff --git a/packages/adapter-pg/src/pg.ts b/packages/adapter-pg/src/pg.ts index 8d01ee593875..481b94415213 100644 --- a/packages/adapter-pg/src/pg.ts +++ b/packages/adapter-pg/src/pg.ts @@ -278,12 +278,15 @@ export class PrismaPgAdapterFactory implements SqlMigrationAwareDriverAdapterFac private externalPool: pg.Pool | null constructor( - poolOrConfig: pg.Pool | pg.PoolConfig, + poolOrConfig: pg.Pool | pg.PoolConfig | string, private readonly options?: PrismaPgOptions, ) { if (poolOrConfig instanceof pg.Pool) { this.externalPool = poolOrConfig this.config = poolOrConfig.options + } else if (typeof poolOrConfig === 'string') { + this.externalPool = null + this.config = { connectionString: poolOrConfig } } else { this.externalPool = null this.config = poolOrConfig From 67b6986419ef6281e44cecfa3e19f2d52965deb0 Mon Sep 17 00:00:00 2001 From: Nurul Sundarani Date: Tue, 17 Mar 2026 20:08:51 +0530 Subject: [PATCH 2/4] fix: export GetGroupByPayload type in prisma-client generator (#29346) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Adds `export` keyword to the `GetGroupByPayload` type in `client-generator-ts` so it's accessible to users after generation Fixes https://github.com/prisma/prisma/issues/29345 ## Problem In the `prisma-client` generator (Prisma 7), `GetGroupByPayload` is defined as a **private type** in each model file: ```typescript // generated/models/MealInteraction.ts export type MealInteractionGroupByArgs = { ... } // ✅ exported export type MealInteractionGroupByOutputType = { ... } // ✅ exported type GetMealInteractionGroupByPayload = ... // ❌ NOT exported ``` In the old `prisma-client-js` generator, this type was accessible via the `Prisma` namespace (`Prisma.GetMealInteractionGroupByPayload<{...}>`). With the new per-model-file architecture, the missing `export` makes it completely unreachable. ## Fix One-line change in `packages/client-generator-ts/src/TSClient/Model.ts`: ```diff -type ${getGroupByPayloadName(model.name)} = Prisma.PrismaPromise< +export type ${getGroupByPayloadName(model.name)} = Prisma.PrismaPromise< ``` ## Test plan - [ ] Run `prisma generate` with `prisma-client` generator - [ ] Verify `GetMealInteractionGroupByPayload` is now exported from the generated model file - [ ] Verify it can be imported from the models barrel export - [ ] Verify `Awaited>` resolves correctly ## Summary by CodeRabbit * **New Features** * The GroupBy payload type is now publicly exportable and can be imported for use in your TypeScript applications. --- packages/client-generator-ts/src/TSClient/Model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client-generator-ts/src/TSClient/Model.ts b/packages/client-generator-ts/src/TSClient/Model.ts index bf8d99c4d49a..35e85452f7c3 100644 --- a/packages/client-generator-ts/src/TSClient/Model.ts +++ b/packages/client-generator-ts/src/TSClient/Model.ts @@ -207,7 +207,7 @@ ${indent( ${ts.stringify(buildOutputType(groupByType))} -type ${getGroupByPayloadName(model.name)} = Prisma.PrismaPromise< +export type ${getGroupByPayloadName(model.name)} = Prisma.PrismaPromise< Array< Prisma.PickEnumerable<${groupByType.name}, T['by']> & { From 309b4bccd632d8b40246eab131f3ada578fb4c23 Mon Sep 17 00:00:00 2001 From: Felipe <60716370+Felipeness@users.noreply.github.com> Date: Tue, 17 Mar 2026 13:53:34 -0300 Subject: [PATCH 3/4] refactor: extract 'prisma-client-js' into PRISMA_CLIENT_JS_PROVIDER constant (#29317) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Closes #29316 Extracts the hardcoded `'prisma-client-js'` string literal into a shared `PRISMA_CLIENT_JS_PROVIDER` constant defined in `@prisma/internals`, replacing all occurrences across the codebase. ### Changes - **New**: `packages/internals/src/prisma-client-js-provider.ts` — single source of truth for the constant - **Modified** (8 files across 4 packages): - `packages/internals/src/index.ts` — export the constant - `packages/internals/src/cli/getGeneratorSuccessMessage.ts` — use constant - `packages/internals/src/utils/extractPreviewFeatures.ts` — use constant - `packages/cli/src/Generate.ts` — use constant (2 occurrences) - `packages/cli/src/utils/checkpoint.ts` — use constant - `packages/client-generator-js/src/generator.ts` — use constant - `packages/client-generator-js/src/utils/types/dmmfToTypes.ts` — use constant (2 occurrences) - `packages/client/src/utils/getTestClient.ts` — use constant ### Motivation This was identified during the review of #29315 by @coderabbitai. Having a single constant: - Eliminates risk of typos in the provider string - Makes it easy to find all usages via "Find References" - Provides a single place to update if the value ever changes ## Test plan - [x] `pnpm build` passes (43 tasks) - [x] `getGenerators` tests pass (40/40, 33 snapshots) - [x] No behavioral changes — pure mechanical refactor ## Summary by CodeRabbit * **Refactor** * Standardized provider identifiers across internal modules using a centralized constant. * No changes to behavior or public APIs; end-user experience remains unchanged. * Improved internal consistency and reliability of provider detection and generator selection logic. --- packages/cli/src/Generate.ts | 5 +++-- packages/cli/src/utils/checkpoint.ts | 3 ++- packages/client-generator-js/src/generator.ts | 4 ++-- packages/client-generator-js/src/utils/types/dmmfToTypes.ts | 5 +++-- packages/client/src/utils/getTestClient.ts | 3 ++- packages/internals/src/built-in-provider.ts | 5 +++++ packages/internals/src/cli/getGeneratorSuccessMessage.ts | 3 ++- packages/internals/src/index.ts | 1 + packages/internals/src/utils/extractPreviewFeatures.ts | 3 ++- 9 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 packages/internals/src/built-in-provider.ts diff --git a/packages/cli/src/Generate.ts b/packages/cli/src/Generate.ts index a3e10b0134a3..b245177e2170 100644 --- a/packages/cli/src/Generate.ts +++ b/packages/cli/src/Generate.ts @@ -4,6 +4,7 @@ import { enginesVersion } from '@prisma/engines' import { SqlQueryOutput } from '@prisma/generator' import { arg, + BuiltInProvider, Command, createSchemaPathInput, format, @@ -179,7 +180,7 @@ ${bold('Examples')} } else { // Only used for CLI output, ie Go client doesn't want JS example output const jsClient = generators.find( - (g) => g.options && parseEnvValue(g.options.generator.provider) === 'prisma-client-js', + (g) => g.options && parseEnvValue(g.options.generator.provider) === BuiltInProvider.PrismaClientJs, ) clientGeneratorVersion = jsClient?.manifest?.version ?? null @@ -230,7 +231,7 @@ Please run \`prisma generate\` manually.` if (!watchMode) { const prismaClientJSGenerator = generators?.find( ({ options }) => - options?.generator.provider && parseEnvValue(options?.generator.provider) === 'prisma-client-js', + options?.generator.provider && parseEnvValue(options?.generator.provider) === BuiltInProvider.PrismaClientJs, ) let hint = '' diff --git a/packages/cli/src/utils/checkpoint.ts b/packages/cli/src/utils/checkpoint.ts index 426720649dfa..00353d35855e 100644 --- a/packages/cli/src/utils/checkpoint.ts +++ b/packages/cli/src/utils/checkpoint.ts @@ -1,6 +1,7 @@ import { Debug } from '@prisma/debug' import { arg, + BuiltInProvider, createSchemaPathInput, getCLIPathHash, getProjectHash, @@ -134,7 +135,7 @@ export async function tryToReadDataFromSchema(schemaPath: SchemaPathInput) { .filter((generator) => generator && generator.provider) .map((generator) => parseEnvValue(generator.provider)) - const clientGeneratorProviders = ['prisma-client', 'prisma-client-js'] + const clientGeneratorProviders: string[] = [BuiltInProvider.PrismaClientTs, BuiltInProvider.PrismaClientJs] const previewFeatures = schemaContext.generators .filter((generator) => { const provider = generator?.provider ? parseEnvValue(generator.provider) : undefined diff --git a/packages/client-generator-js/src/generator.ts b/packages/client-generator-js/src/generator.ts index 998aaa0f2df8..ac32a4b8ca9b 100755 --- a/packages/client-generator-js/src/generator.ts +++ b/packages/client-generator-js/src/generator.ts @@ -2,7 +2,7 @@ import path from 'node:path' import { enginesVersion } from '@prisma/engines-version' import { Generator, GeneratorConfig, GeneratorManifest, GeneratorOptions } from '@prisma/generator' -import { parseEnvValue } from '@prisma/internals' +import { BuiltInProvider, parseEnvValue } from '@prisma/internals' import { version as clientVersion } from '../package.json' import { generateClient } from './generateClient' @@ -20,7 +20,7 @@ type PrismaClientJsGeneratorOptions = { // visit https://pris.ly/cli/output-path` export class PrismaClientJsGenerator implements Generator { - readonly name = 'prisma-client-js' + readonly name = BuiltInProvider.PrismaClientJs #shouldResolvePrismaClient: boolean #runtimePath?: string diff --git a/packages/client-generator-js/src/utils/types/dmmfToTypes.ts b/packages/client-generator-js/src/utils/types/dmmfToTypes.ts index d298d1891fbf..69f10075aebc 100644 --- a/packages/client-generator-js/src/utils/types/dmmfToTypes.ts +++ b/packages/client-generator-js/src/utils/types/dmmfToTypes.ts @@ -1,6 +1,7 @@ import path from 'node:path' import type * as DMMF from '@prisma/dmmf' +import { BuiltInProvider } from '@prisma/internals' import { TSClient } from '../../TSClient/TSClient' @@ -24,9 +25,9 @@ export function dmmfToTypes(dmmf: DMMF.Document) { generator: { binaryTargets: [], config: {}, - name: 'prisma-client-js', + name: BuiltInProvider.PrismaClientJs, output: null, - provider: { value: 'prisma-client-js', fromEnvVar: null }, + provider: { value: BuiltInProvider.PrismaClientJs, fromEnvVar: null }, previewFeatures: [], isCustomOutput: false, sourceFilePath: 'schema.prisma', diff --git a/packages/client/src/utils/getTestClient.ts b/packages/client/src/utils/getTestClient.ts index 2b2493b5b5dd..0be6a8ef8608 100644 --- a/packages/client/src/utils/getTestClient.ts +++ b/packages/client/src/utils/getTestClient.ts @@ -1,6 +1,7 @@ import { dmmfToRuntimeDataModel, GetPrismaClientConfig } from '@prisma/client-common' import { getDMMF } from '@prisma/client-generator-js' import { + BuiltInProvider, extractPreviewFeatures, getConfig, getSchemaWithPath, @@ -34,7 +35,7 @@ export async function getTestClient(schemaDir?: string, printWarnings?: boolean) printConfigWarnings(config.warnings) } - const generator = config.generators.find((g) => parseEnvValue(g.provider) === 'prisma-client-js') + const generator = config.generators.find((g) => parseEnvValue(g.provider) === BuiltInProvider.PrismaClientJs) const previewFeatures = extractPreviewFeatures(config.generators) ;(global as any).TARGET_BUILD_TYPE = 'client' diff --git a/packages/internals/src/built-in-provider.ts b/packages/internals/src/built-in-provider.ts new file mode 100644 index 000000000000..5435a29ca1c1 --- /dev/null +++ b/packages/internals/src/built-in-provider.ts @@ -0,0 +1,5 @@ +/** Built-in generator provider identifiers used across Prisma packages. */ +export const BuiltInProvider = { + PrismaClientJs: 'prisma-client-js', + PrismaClientTs: 'prisma-client', +} as const diff --git a/packages/internals/src/cli/getGeneratorSuccessMessage.ts b/packages/internals/src/cli/getGeneratorSuccessMessage.ts index 157a324708ec..3249190cad26 100644 --- a/packages/internals/src/cli/getGeneratorSuccessMessage.ts +++ b/packages/internals/src/cli/getGeneratorSuccessMessage.ts @@ -1,6 +1,7 @@ import { bold, dim } from 'kleur/colors' import path from 'path' +import { BuiltInProvider } from '../built-in-provider' import type { Generator } from '../Generator' import { formatms } from '../utils/formatms' import { parseEnvValue } from '../utils/parseEnvValue' @@ -20,7 +21,7 @@ export function getGeneratorSuccessMessage(generator: Generator, time: number): function formatVersion(generator: Generator): string | undefined { const version = generator.manifest?.version - if (generator.getProvider() === 'prisma-client-js') { + if (generator.getProvider() === BuiltInProvider.PrismaClientJs) { // version is always defined for prisma-client-js return `v${version ?? '?.?.?'}` } diff --git a/packages/internals/src/index.ts b/packages/internals/src/index.ts index 16e3b045b985..5ff294386fcf 100644 --- a/packages/internals/src/index.ts +++ b/packages/internals/src/index.ts @@ -1,3 +1,4 @@ +export { BuiltInProvider } from './built-in-provider' export { checkUnsupportedDataProxy } from './cli/checkUnsupportedDataProxy' export { type DirectoryConfig, inferDirectoryConfig } from './cli/directoryConfig' export { getGeneratorSuccessMessage } from './cli/getGeneratorSuccessMessage' diff --git a/packages/internals/src/utils/extractPreviewFeatures.ts b/packages/internals/src/utils/extractPreviewFeatures.ts index 0f6232dcd63a..e99e6088f3c0 100644 --- a/packages/internals/src/utils/extractPreviewFeatures.ts +++ b/packages/internals/src/utils/extractPreviewFeatures.ts @@ -1,7 +1,8 @@ import type { GeneratorConfig } from '@prisma/generator' +import { BuiltInProvider } from '../built-in-provider' import { parseEnvValue } from './parseEnvValue' export function extractPreviewFeatures(generators: GeneratorConfig[]): string[] { - return generators.find((g) => parseEnvValue(g.provider) === 'prisma-client-js')?.previewFeatures || [] + return generators.find((g) => parseEnvValue(g.provider) === BuiltInProvider.PrismaClientJs)?.previewFeatures || [] } From 33667c373c0ae84ad37c7aed2497b99452da589c Mon Sep 17 00:00:00 2001 From: Alex Solomaha Date: Tue, 17 Mar 2026 18:53:49 +0200 Subject: [PATCH 4/4] fix(adapter-pg): handle both quoted/unquoted column names in ColumnNotFound error (#29307) This pull request improves the handling and testing of PostgreSQL "ColumnNotFound" errors in the adapter. The main focus is on correctly extracting column names from error messages, regardless of whether the column name is quoted, and ensuring the tests cover both scenarios. We were getting "The column `(not available)` does not exist in the current database" error all the time, the sql was trying to get qualified name (e.g. schema.table_name) which generated postgres error message without quotes: `column table.column does not exist` vs `column "column" does not exist`. Our setup included: - multi-schema DB - custom @@map() fields. - Postgres v18.3 Error handling improvements: * Updated the `mapDriverError` function in `errors.ts` to strip quotes from column names when parsing "ColumnNotFound" (42703) errors, ensuring consistent extraction of the column name. Testing enhancements: * Added a new test case for handling unquoted column names and clarified the description of the existing test for quoted column names in `errors.test.ts`, ensuring both cases are covered for "ColumnNotFound" errors. ## Summary by CodeRabbit * **Bug Fixes** * Improved detection and normalization of PostgreSQL "column not found" errors so column names are identified correctly for unquoted, quoted, qualified, spaced, and escaped-quote formats, providing clearer, consistent error details. * **Tests** * Added comprehensive tests covering many column-name formats to ensure accurate detection and unchanged original error messaging. --- .../adapter-pg/src/__tests__/errors.test.ts | 17 ++++++++++++++--- packages/adapter-pg/src/errors.ts | 6 ++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/adapter-pg/src/__tests__/errors.test.ts b/packages/adapter-pg/src/__tests__/errors.test.ts index 7577cadb75a4..07d7a2d3b409 100644 --- a/packages/adapter-pg/src/__tests__/errors.test.ts +++ b/packages/adapter-pg/src/__tests__/errors.test.ts @@ -126,11 +126,22 @@ describe('convertDriverError', () => { }) }) - it('should handle ColumnNotFound (42703)', () => { - const error = { code: '42703', message: 'column "foo" does not exist', severity: 'ERROR' } + it.each([ + ['unquoted column name', 'column foo does not exist', 'foo'], + ['quoted column name', 'column "foo" does not exist', 'foo'], + ['unquoted qualified column name', 'column users.first_name does not exist', 'users.first_name'], + ['quoted qualified column name', 'column "users"."first name" does not exist', 'users.first name'], + ['partially quoted qualified column name (1)', 'column users."first name" does not exist', 'users.first name'], + ['partially quoted qualified column name (2)', 'column "users".first_name does not exist', 'users.first_name'], + ['quoted column name containing spaces', 'column "first name" does not exist', 'first name'], + ['quoted column name containing dots', 'column "first.name" does not exist', 'first.name'], + ['quoted qualified column name containing dots', 'column "users"."first.name" does not exist', 'users.first.name'], + ['quoted column name containing escaped quotes', 'column "a""b" does not exist', 'a"b'], + ])('should handle ColumnNotFound (42703) with %s', (description, message, expectedColumn) => { + const error = { code: '42703', message, severity: 'ERROR' } expect(convertDriverError(error)).toEqual({ kind: 'ColumnNotFound', - column: 'foo', + column: expectedColumn, originalCode: error.code, originalMessage: error.message, }) diff --git a/packages/adapter-pg/src/errors.ts b/packages/adapter-pg/src/errors.ts index e7188172231c..1c17555317a9 100644 --- a/packages/adapter-pg/src/errors.ts +++ b/packages/adapter-pg/src/errors.ts @@ -136,11 +136,13 @@ function mapDriverError(error: DatabaseError): MappedError { kind: 'TableDoesNotExist', table: error.message.split(' ').at(1)?.split('"').at(1), } - case '42703': + case '42703': { + const rawColumn = error.message.match(/^column (.+) does not exist$/)?.at(1) return { kind: 'ColumnNotFound', - column: error.message.split(' ').at(1)?.split('"').at(1), + column: rawColumn?.replace(/"((?:""|[^"])*)"/g, (_, id) => id.replaceAll('""', '"')), } + } case '42P04': return { kind: 'DatabaseAlreadyExists',