From 839a82934cfae759355de5d1712583c774e6dd2a Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Mon, 12 Oct 2026 17:00:40 +0200 Subject: [PATCH] fix(node): Instrument Prisma 8 CollectionBase from rc.15 Prisma 8.0.0-rc.15 renamed CollectionImpl to CollectionBase, so the orchestrion config stopped matching and no Prisma operation spans were created. Target CollectionBase and drop support for older RCs. The test suite and E2E app move to rc.17 with fixtures regenerated by the rc.17 CLI, since rc.15 also renames tables to match the model and the runtime checks the contract hash stored in the database. Co-Authored-By: Claude Opus 5.5 --- .../test-applications/node-prisma-8/init.sql | 22 ++-- .../node-prisma-8/package.json | 2 +- .../node-prisma-8/src/prisma/contract.d.ts | 104 +++++++++++------- .../node-prisma-8/src/prisma/contract.json | 23 ++-- .../node-prisma-8/tests/prisma.test.ts | 8 +- .../prisma-orm-v8/prisma/contract.json | 23 ++-- .../tracing/prisma-orm-v8/prisma/init.sql | 22 ++-- .../suites/tracing/prisma-orm-v8/test.ts | 12 +- .../src/orchestrion/config/prisma.ts | 7 +- 9 files changed, 132 insertions(+), 91 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/node-prisma-8/init.sql b/dev-packages/e2e-tests/test-applications/node-prisma-8/init.sql index 655d447ca485..ac93565c65b2 100644 --- a/dev-packages/e2e-tests/test-applications/node-prisma-8/init.sql +++ b/dev-packages/e2e-tests/test-applications/node-prisma-8/init.sql @@ -34,31 +34,31 @@ CREATE TABLE prisma_contract.marker ( meta jsonb DEFAULT '{}'::jsonb NOT NULL, invariants text[] DEFAULT '{}'::text[] NOT NULL ); -CREATE TABLE public."user" ( +CREATE TABLE public."User" ( email text NOT NULL, id integer NOT NULL, name text ); -CREATE SEQUENCE public.user_id_seq +CREATE SEQUENCE public."User_id_seq" AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; -ALTER SEQUENCE public.user_id_seq OWNED BY public."user".id; +ALTER SEQUENCE public."User_id_seq" OWNED BY public."User".id; ALTER TABLE ONLY prisma_contract.ledger ALTER COLUMN id SET DEFAULT nextval('prisma_contract.ledger_id_seq'::regclass); -ALTER TABLE ONLY public."user" ALTER COLUMN id SET DEFAULT nextval('public.user_id_seq'::regclass); -INSERT INTO prisma_contract.contract VALUES ('f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5', '2026-09-09 09:15:28.369559+00', '{"meta": {}, "roots": {"user": {"model": "User", "namespace": "public"}}, "domain": {"namespaces": {"public": {"models": {"User": {"fields": {"id": {"type": {"kind": "scalar", "codecId": "pg/int4@1"}, "nullable": false}, "name": {"type": {"kind": "scalar", "codecId": "pg/text@1"}, "nullable": true}, "email": {"type": {"kind": "scalar", "codecId": "pg/text@1"}, "nullable": false}}, "storage": {"table": "user", "fields": {"id": {"column": "id"}, "name": {"column": "name"}, "email": {"column": "email"}}, "namespaceId": "public"}, "relations": {}}}}}}, "target": "postgres", "storage": {"namespaces": {"public": {"id": "public", "entries": {"table": {"user": {"columns": {"id": {"codecId": "pg/int4@1", "default": {"kind": "function", "expression": "autoincrement()"}, "nullable": false, "nativeType": "int4"}, "name": {"codecId": "pg/text@1", "nullable": true, "nativeType": "text"}, "email": {"codecId": "pg/text@1", "nullable": false, "nativeType": "text"}}, "indexes": [], "uniques": [{"columns": ["email"]}], "primaryKey": {"columns": ["id"]}, "foreignKeys": []}}}}}, "storageHash": "f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5"}, "extensions": {}, "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "capabilities": {"sql": {"enums": true, "lateral": true, "returning": true, "scalarList": true, "checkConstraint": true, "defaultInInsert": true}, "postgres": {"limit": true, "jsonAgg": true, "lateral": true, "orderBy": true, "returning": true, "distinctOn": true}}, "targetFamily": "sql"}'); -INSERT INTO prisma_contract.ledger VALUES (1, '2026-09-09 09:15:28.369559+00', 'app', '', 'f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5', '', NULL, 'f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5', NULL, '[{"id": "table.user", "label": "Create table \"user\"", "target": {"id": "postgres", "details": {"name": "user", "schema": "public", "objectType": "table"}}, "execute": [{"sql": "CREATE TABLE \"public\".\"user\" (\n \"email\" text NOT NULL,\n \"id\" SERIAL NOT NULL,\n \"name\" text,\n PRIMARY KEY (\"id\")\n)", "params": [], "description": "create table \"user\""}], "summary": "Creates table \"user\"", "precheck": [{"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", "params": ["\"public\".\"user\""], "description": "ensure table \"user\" does not exist"}], "postcheck": [{"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"", "params": ["\"public\".\"user\""], "description": "verify table \"user\" exists"}], "operationClass": "additive"}, {"id": "unique.user.user_email_key", "label": "Add unique constraint on \"user\" (email)", "target": {"id": "postgres", "details": {"name": "user_email_key", "table": "user", "schema": "public", "objectType": "unique"}}, "execute": [{"sql": "ALTER TABLE \"public\".\"user\" ADD CONSTRAINT \"user_email_key\" UNIQUE (\"email\")", "description": "add unique constraint \"user_email_key\""}], "precheck": [{"sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", "params": ["user_email_key", "public", "\"public\".\"user\""], "description": "ensure constraint \"user_email_key\" does not exist"}], "postcheck": [{"sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", "params": ["user_email_key", "public", "\"public\".\"user\""], "description": "verify constraint \"user_email_key\" exists"}], "operationClass": "additive"}]'); -INSERT INTO prisma_contract.marker VALUES ('app', 'f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5', '3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2', NULL, NULL, '2026-09-09 09:15:28.369559+00', NULL, '{}', '{}'); +ALTER TABLE ONLY public."User" ALTER COLUMN id SET DEFAULT nextval('public."User_id_seq"'::regclass); +INSERT INTO prisma_contract.contract VALUES ('a62d15a094e80be7b342d75e0538935972b9699c76dabb5339940dd50d17a889', '2026-10-08 14:56:22.624077+00', '{"meta": {}, "roots": {"User": {"model": "User", "namespace": "public"}}, "domain": {"namespaces": {"public": {"models": {"User": {"fields": {"id": {"many": false, "type": {"kind": "scalar", "codecId": "pg/int4@1"}, "nullable": false}, "name": {"many": false, "type": {"kind": "scalar", "codecId": "pg/text@1"}, "nullable": true}, "email": {"many": false, "type": {"kind": "scalar", "codecId": "pg/text@1"}, "nullable": false}}, "storage": {"table": "User", "fields": {"id": {"column": "id"}, "name": {"column": "name"}, "email": {"column": "email"}}, "namespaceId": "public"}, "relations": {}}}}}}, "target": "postgres", "storage": {"namespaces": {"public": {"id": "public", "entries": {"table": {"User": {"columns": {"id": {"many": false, "codecId": "pg/int4@1", "default": {"kind": "function", "expression": "autoincrement()"}, "dataType": "pg/int4", "nullable": false}, "name": {"many": false, "codecId": "pg/text@1", "dataType": "pg/text", "nullable": true}, "email": {"many": false, "codecId": "pg/text@1", "dataType": "pg/text", "nullable": false}}, "indexes": [], "uniques": [{"columns": ["email"]}], "primaryKey": {"columns": ["id"]}, "foreignKeys": []}}}}}, "storageHash": "a62d15a094e80be7b342d75e0538935972b9699c76dabb5339940dd50d17a889"}, "extensions": {}, "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "capabilities": {"sql": {"enums": true, "lockOf": true, "lateral": true, "forShare": true, "forUpdate": true, "returning": true, "lockNowait": true, "scalarList": true, "lockSkipLocked": true, "checkConstraint": true, "defaultInInsert": true, "insertOnConflictSkip": true, "insertOnConflictWithoutTarget": true}, "postgres": {"limit": true, "jsonAgg": true, "lateral": true, "orderBy": true, "returning": true, "distinctOn": true, "forKeyShare": true, "forNoKeyUpdate": true}}, "targetFamily": "sql"}'); +INSERT INTO prisma_contract.ledger VALUES (1, '2026-10-08 14:56:22.624077+00', 'app', '', 'a62d15a094e80be7b342d75e0538935972b9699c76dabb5339940dd50d17a889', '', NULL, 'a62d15a094e80be7b342d75e0538935972b9699c76dabb5339940dd50d17a889', NULL, '[{"id": "table.User", "label": "Create table \"User\"", "target": {"id": "postgres", "details": {"name": "User", "schema": "public", "objectType": "table"}}, "execute": [{"sql": "CREATE TABLE \"public\".\"User\" (\n \"email\" text NOT NULL,\n \"id\" SERIAL NOT NULL,\n \"name\" text,\n PRIMARY KEY (\"id\")\n)", "params": [], "description": "create table \"User\""}], "summary": "Creates table \"User\"", "precheck": [{"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", "params": ["\"public\".\"User\""], "description": "ensure table \"User\" does not exist"}], "postcheck": [{"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"", "params": ["\"public\".\"User\""], "description": "verify table \"User\" exists"}], "operationClass": "additive"}, {"id": "unique.User.User_email_key", "label": "Add unique constraint on \"User\" (email)", "target": {"id": "postgres", "details": {"name": "User_email_key", "table": "User", "schema": "public", "objectType": "unique"}}, "execute": [{"sql": "ALTER TABLE \"public\".\"User\" ADD CONSTRAINT \"User_email_key\" UNIQUE (\"email\")", "description": "add unique constraint \"User_email_key\""}], "precheck": [{"sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", "params": ["User_email_key", "public", "\"public\".\"User\""], "description": "ensure constraint \"User_email_key\" does not exist"}], "postcheck": [{"sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", "params": ["User_email_key", "public", "\"public\".\"User\""], "description": "verify constraint \"User_email_key\" exists"}], "operationClass": "additive"}]'); +INSERT INTO prisma_contract.marker VALUES ('app', 'a62d15a094e80be7b342d75e0538935972b9699c76dabb5339940dd50d17a889', '3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2', NULL, NULL, '2026-10-08 14:56:22.624077+00', NULL, '{}', '{}'); ALTER TABLE ONLY prisma_contract.contract ADD CONSTRAINT contract_pkey PRIMARY KEY (core_hash); ALTER TABLE ONLY prisma_contract.ledger ADD CONSTRAINT ledger_pkey PRIMARY KEY (id); ALTER TABLE ONLY prisma_contract.marker ADD CONSTRAINT marker_pkey PRIMARY KEY (space); -ALTER TABLE ONLY public."user" - ADD CONSTRAINT user_email_key UNIQUE (email); -ALTER TABLE ONLY public."user" - ADD CONSTRAINT user_pkey PRIMARY KEY (id); +ALTER TABLE ONLY public."User" + ADD CONSTRAINT "User_email_key" UNIQUE (email); +ALTER TABLE ONLY public."User" + ADD CONSTRAINT "User_pkey" PRIMARY KEY (id); diff --git a/dev-packages/e2e-tests/test-applications/node-prisma-8/package.json b/dev-packages/e2e-tests/test-applications/node-prisma-8/package.json index a6e8bb692e57..6b42d3ee2e7c 100644 --- a/dev-packages/e2e-tests/test-applications/node-prisma-8/package.json +++ b/dev-packages/e2e-tests/test-applications/node-prisma-8/package.json @@ -12,7 +12,7 @@ "test:assert": "pnpm test" }, "dependencies": { - "@prisma/orm-postgres": "8.0.0-rc.8", + "@prisma/orm-postgres": "8.0.0-rc.17", "@sentry/node": "file:../../packed/sentry-node-packed.tgz", "@types/express": "^4.17.21", "@types/node": "^20.19.0", diff --git a/dev-packages/e2e-tests/test-applications/node-prisma-8/src/prisma/contract.d.ts b/dev-packages/e2e-tests/test-applications/node-prisma-8/src/prisma/contract.d.ts index 010a73846882..ecfad3dac136 100644 --- a/dev-packages/e2e-tests/test-applications/node-prisma-8/src/prisma/contract.d.ts +++ b/dev-packages/e2e-tests/test-applications/node-prisma-8/src/prisma/contract.d.ts @@ -1,26 +1,14 @@ // ⚠️ GENERATED FILE - DO NOT EDIT // This file is automatically generated by 'prisma contract emit'. // To regenerate, run: prisma contract emit -import type { QueryOperationTypes as PgAdapterQueryOps } from '@prisma/orm-postgres/adapter/operation-types'; -import type { - Bit, - Char, - CodecTypes as PgTypes, - Interval, - JsonValue, - Numeric, - Time, - TimeString, - Timestamp, - TimestampString, - Timestamptz, - TimestamptzString, - Timetz, - VarBit, - Varchar, -} from '@prisma/orm-postgres/target/codec-types'; +import type { CodecTypes as PgTypes } from '@prisma/orm-postgres/target/codec-types'; +import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types'; -import type { ContractWithTypeMaps, TypeMaps as TypeMapsType } from '@prisma/orm-postgres/family-contract/types'; +import type { + ContractWithTypeMaps, + RelationKeys, + TypeMaps as TypeMapsType, +} from '@prisma/orm-postgres/family-contract/types'; import type { Contract as ContractType, ExecutionHashBase, @@ -29,13 +17,13 @@ import type { StorageHashBase, } from '@prisma/orm-postgres/contract/types'; -export type StorageHash = StorageHashBase<'f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5'>; +export type StorageHash = StorageHashBase<'a62d15a094e80be7b342d75e0538935972b9699c76dabb5339940dd50d17a889'>; export type ExecutionHash = ExecutionHashBase; export type ProfileHash = ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>; export type CodecTypes = PgTypes; export type LaneCodecTypes = CodecTypes; -export type QueryOperationTypes = PgAdapterQueryOps; +export type QueryOperationTypes = PgTargetQueryOps; export type AggregateTypes = { readonly avg: { readonly byCodec: { @@ -113,6 +101,10 @@ export type AggregateTypes = { readonly output: 'pg/timestamp-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; readonly 'pg/timestamptz-string@1': { readonly output: 'pg/timestamptz-string@1'; readonly nullable: true; @@ -169,6 +161,10 @@ export type AggregateTypes = { readonly output: 'pg/timestamp-temporal@1'; readonly nullable: true; }; + readonly 'pg/timestamptz-date@1': { + readonly output: 'pg/timestamptz-date@1'; + readonly nullable: true; + }; readonly 'pg/timestamptz-string@1': { readonly output: 'pg/timestamptz-string@1'; readonly nullable: true; @@ -236,8 +232,8 @@ type DefaultLiteralValue = CodecId extends keyo export type FieldOutputTypes = { readonly public: { readonly User: { - readonly id: CodecTypes['pg/int4@1']['output']; readonly email: CodecTypes['pg/text@1']['output']; + readonly id: CodecTypes['pg/int4@1']['output']; readonly name: CodecTypes['pg/text@1']['output'] | null; }; }; @@ -245,15 +241,15 @@ export type FieldOutputTypes = { export type FieldInputTypes = { readonly public: { readonly User: { - readonly id: CodecTypes['pg/int4@1']['input']; readonly email: CodecTypes['pg/text@1']['input']; + readonly id: CodecTypes['pg/int4@1']['input']; readonly name: CodecTypes['pg/text@1']['input'] | null; }; }; }; export type StorageColumnTypes = { readonly public: { - readonly user: { + readonly User: { readonly email: CodecTypes['pg/text@1']['output']; readonly id: CodecTypes['pg/int4@1']['output']; readonly name: CodecTypes['pg/text@1']['output'] | null; @@ -262,13 +258,29 @@ export type StorageColumnTypes = { }; export type StorageColumnInputTypes = { readonly public: { - readonly user: { + readonly User: { readonly email: CodecTypes['pg/text@1']['input']; readonly id: CodecTypes['pg/int4@1']['input']; readonly name: CodecTypes['pg/text@1']['input'] | null; }; }; }; + +export namespace Models { + export type public_User = { + email: CodecTypes['pg/text@1']['output']; + id: CodecTypes['pg/int4@1']['output']; + name: CodecTypes['pg/text@1']['output'] | null; + readonly [RelationKeys]?: never; + }; +} + +export declare const models: { + public: { + User: Models.public_User; + }; +}; + export type TypeMaps = TypeMapsType< CodecTypes, QueryOperationTypes, @@ -287,26 +299,29 @@ type ContractBase = Omit< readonly kind: 'postgres-schema'; readonly entries: { readonly table: { - readonly user: { + readonly User: { columns: { + readonly email: { + readonly dataType: 'pg/text'; + readonly codecId: 'pg/text@1'; + readonly nullable: false; + readonly many: false; + }; readonly id: { - readonly nativeType: 'int4'; + readonly dataType: 'pg/int4'; readonly codecId: 'pg/int4@1'; readonly nullable: false; readonly default: { readonly kind: 'function'; readonly expression: 'autoincrement()'; }; - }; - readonly email: { - readonly nativeType: 'text'; - readonly codecId: 'pg/text@1'; - readonly nullable: false; + readonly many: false; }; readonly name: { - readonly nativeType: 'text'; + readonly dataType: 'pg/text'; readonly codecId: 'pg/text@1'; readonly nullable: true; + readonly many: false; }; }; primaryKey: { readonly columns: readonly ['id'] }; @@ -325,7 +340,7 @@ type ContractBase = Omit< readonly target: 'postgres'; readonly targetFamily: 'sql'; readonly roots: { - readonly user: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; + readonly User: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' }; }; readonly domain: { readonly namespaces: { @@ -333,14 +348,14 @@ type ContractBase = Omit< readonly models: { readonly User: { readonly fields: { - readonly id: { - readonly nullable: false; - readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; - }; readonly email: { readonly nullable: false; readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; }; + readonly id: { + readonly nullable: false; + readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' }; + }; readonly name: { readonly nullable: true; readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' }; @@ -348,11 +363,11 @@ type ContractBase = Omit< }; readonly relations: Record; readonly storage: { - readonly table: 'user'; + readonly table: 'User'; readonly namespaceId: 'public'; readonly fields: { - readonly id: { readonly column: 'id' }; readonly email: { readonly column: 'email' }; + readonly id: { readonly column: 'id' }; readonly name: { readonly column: 'name' }; }; }; @@ -364,6 +379,8 @@ type ContractBase = Omit< readonly capabilities: { readonly postgres: { readonly distinctOn: true; + readonly forKeyShare: true; + readonly forNoKeyUpdate: true; readonly jsonAgg: true; readonly lateral: true; readonly limit: true; @@ -374,7 +391,14 @@ type ContractBase = Omit< readonly checkConstraint: true; readonly defaultInInsert: true; readonly enums: true; + readonly forShare: true; + readonly forUpdate: true; + readonly insertOnConflictSkip: true; + readonly insertOnConflictWithoutTarget: true; readonly lateral: true; + readonly lockNowait: true; + readonly lockOf: true; + readonly lockSkipLocked: true; readonly returning: true; readonly scalarList: true; }; diff --git a/dev-packages/e2e-tests/test-applications/node-prisma-8/src/prisma/contract.json b/dev-packages/e2e-tests/test-applications/node-prisma-8/src/prisma/contract.json index 3b9090e28776..51b72b543a0b 100644 --- a/dev-packages/e2e-tests/test-applications/node-prisma-8/src/prisma/contract.json +++ b/dev-packages/e2e-tests/test-applications/node-prisma-8/src/prisma/contract.json @@ -4,7 +4,7 @@ "target": "postgres", "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "roots": { - "user": { + "User": { "model": "User", "namespace": "public" } @@ -51,7 +51,7 @@ } }, "namespaceId": "public", - "table": "user" + "table": "User" } } } @@ -63,25 +63,25 @@ "public": { "entries": { "table": { - "user": { + "User": { "columns": { "email": { "codecId": "pg/text@1", - "nativeType": "text", + "dataType": "pg/text", "nullable": false }, "id": { "codecId": "pg/int4@1", + "dataType": "pg/int4", "default": { "expression": "autoincrement()", "kind": "function" }, - "nativeType": "int4", "nullable": false }, "name": { "codecId": "pg/text@1", - "nativeType": "text", + "dataType": "pg/text", "nullable": true } }, @@ -102,11 +102,13 @@ "kind": "postgres-schema" } }, - "storageHash": "f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5" + "storageHash": "a62d15a094e80be7b342d75e0538935972b9699c76dabb5339940dd50d17a889" }, "capabilities": { "postgres": { "distinctOn": true, + "forKeyShare": true, + "forNoKeyUpdate": true, "jsonAgg": true, "lateral": true, "limit": true, @@ -117,7 +119,14 @@ "checkConstraint": true, "defaultInInsert": true, "enums": true, + "forShare": true, + "forUpdate": true, + "insertOnConflictSkip": true, + "insertOnConflictWithoutTarget": true, "lateral": true, + "lockNowait": true, + "lockOf": true, + "lockSkipLocked": true, "returning": true, "scalarList": true } diff --git a/dev-packages/e2e-tests/test-applications/node-prisma-8/tests/prisma.test.ts b/dev-packages/e2e-tests/test-applications/node-prisma-8/tests/prisma.test.ts index 81cc629a2ff8..4f581ff6d9ca 100644 --- a/dev-packages/e2e-tests/test-applications/node-prisma-8/tests/prisma.test.ts +++ b/dev-packages/e2e-tests/test-applications/node-prisma-8/tests/prisma.test.ts @@ -43,7 +43,7 @@ test('Prisma 8 ORM calls emit operation spans with the pg queries nested underne 'sentry.origin': { value: 'auto.db.prisma', type: 'string' }, 'sentry.op': { value: 'db', type: 'string' }, 'db.operation.name': { value: span.attributes['method']?.value, type: 'string' }, - 'db.collection.name': { value: 'user', type: 'string' }, + 'db.collection.name': { value: 'User', type: 'string' }, model: { value: 'User', type: 'string' }, name: { value: `User.${span.attributes['method']?.value}`, type: 'string' }, }); @@ -62,11 +62,11 @@ test('Prisma 8 ORM calls emit operation spans with the pg queries nested underne .map(span => span.attributes['db.query.text']?.value); }; expect(queriesUnder('create')).toEqual( - expect.arrayContaining([expect.stringMatching(/^INSERT INTO "public"\."user" /)]), + expect.arrayContaining([expect.stringMatching(/^INSERT INTO "public"\."User" /)]), ); - expect(queriesUnder('all')).toEqual([expect.stringMatching(/^SELECT .* FROM "public"\."user"$/)]); + expect(queriesUnder('all')).toEqual([expect.stringMatching(/^SELECT .* FROM "public"\."User"$/)]); expect(queriesUnder('delete')).toEqual( - expect.arrayContaining([expect.stringMatching(/^DELETE FROM "public"\."user" /)]), + expect.arrayContaining([expect.stringMatching(/^DELETE FROM "public"\."User" /)]), ); const dbSpans = spans.filter(span => getSpanOp(span) === 'db' && span.attributes['db.query.text']?.value); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/prisma/contract.json b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/prisma/contract.json index 3b9090e28776..51b72b543a0b 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/prisma/contract.json +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/prisma/contract.json @@ -4,7 +4,7 @@ "target": "postgres", "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "roots": { - "user": { + "User": { "model": "User", "namespace": "public" } @@ -51,7 +51,7 @@ } }, "namespaceId": "public", - "table": "user" + "table": "User" } } } @@ -63,25 +63,25 @@ "public": { "entries": { "table": { - "user": { + "User": { "columns": { "email": { "codecId": "pg/text@1", - "nativeType": "text", + "dataType": "pg/text", "nullable": false }, "id": { "codecId": "pg/int4@1", + "dataType": "pg/int4", "default": { "expression": "autoincrement()", "kind": "function" }, - "nativeType": "int4", "nullable": false }, "name": { "codecId": "pg/text@1", - "nativeType": "text", + "dataType": "pg/text", "nullable": true } }, @@ -102,11 +102,13 @@ "kind": "postgres-schema" } }, - "storageHash": "f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5" + "storageHash": "a62d15a094e80be7b342d75e0538935972b9699c76dabb5339940dd50d17a889" }, "capabilities": { "postgres": { "distinctOn": true, + "forKeyShare": true, + "forNoKeyUpdate": true, "jsonAgg": true, "lateral": true, "limit": true, @@ -117,7 +119,14 @@ "checkConstraint": true, "defaultInInsert": true, "enums": true, + "forShare": true, + "forUpdate": true, + "insertOnConflictSkip": true, + "insertOnConflictWithoutTarget": true, "lateral": true, + "lockNowait": true, + "lockOf": true, + "lockSkipLocked": true, "returning": true, "scalarList": true } diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/prisma/init.sql b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/prisma/init.sql index 655d447ca485..ac93565c65b2 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/prisma/init.sql +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/prisma/init.sql @@ -34,31 +34,31 @@ CREATE TABLE prisma_contract.marker ( meta jsonb DEFAULT '{}'::jsonb NOT NULL, invariants text[] DEFAULT '{}'::text[] NOT NULL ); -CREATE TABLE public."user" ( +CREATE TABLE public."User" ( email text NOT NULL, id integer NOT NULL, name text ); -CREATE SEQUENCE public.user_id_seq +CREATE SEQUENCE public."User_id_seq" AS integer START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1; -ALTER SEQUENCE public.user_id_seq OWNED BY public."user".id; +ALTER SEQUENCE public."User_id_seq" OWNED BY public."User".id; ALTER TABLE ONLY prisma_contract.ledger ALTER COLUMN id SET DEFAULT nextval('prisma_contract.ledger_id_seq'::regclass); -ALTER TABLE ONLY public."user" ALTER COLUMN id SET DEFAULT nextval('public.user_id_seq'::regclass); -INSERT INTO prisma_contract.contract VALUES ('f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5', '2026-09-09 09:15:28.369559+00', '{"meta": {}, "roots": {"user": {"model": "User", "namespace": "public"}}, "domain": {"namespaces": {"public": {"models": {"User": {"fields": {"id": {"type": {"kind": "scalar", "codecId": "pg/int4@1"}, "nullable": false}, "name": {"type": {"kind": "scalar", "codecId": "pg/text@1"}, "nullable": true}, "email": {"type": {"kind": "scalar", "codecId": "pg/text@1"}, "nullable": false}}, "storage": {"table": "user", "fields": {"id": {"column": "id"}, "name": {"column": "name"}, "email": {"column": "email"}}, "namespaceId": "public"}, "relations": {}}}}}}, "target": "postgres", "storage": {"namespaces": {"public": {"id": "public", "entries": {"table": {"user": {"columns": {"id": {"codecId": "pg/int4@1", "default": {"kind": "function", "expression": "autoincrement()"}, "nullable": false, "nativeType": "int4"}, "name": {"codecId": "pg/text@1", "nullable": true, "nativeType": "text"}, "email": {"codecId": "pg/text@1", "nullable": false, "nativeType": "text"}}, "indexes": [], "uniques": [{"columns": ["email"]}], "primaryKey": {"columns": ["id"]}, "foreignKeys": []}}}}}, "storageHash": "f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5"}, "extensions": {}, "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "capabilities": {"sql": {"enums": true, "lateral": true, "returning": true, "scalarList": true, "checkConstraint": true, "defaultInInsert": true}, "postgres": {"limit": true, "jsonAgg": true, "lateral": true, "orderBy": true, "returning": true, "distinctOn": true}}, "targetFamily": "sql"}'); -INSERT INTO prisma_contract.ledger VALUES (1, '2026-09-09 09:15:28.369559+00', 'app', '', 'f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5', '', NULL, 'f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5', NULL, '[{"id": "table.user", "label": "Create table \"user\"", "target": {"id": "postgres", "details": {"name": "user", "schema": "public", "objectType": "table"}}, "execute": [{"sql": "CREATE TABLE \"public\".\"user\" (\n \"email\" text NOT NULL,\n \"id\" SERIAL NOT NULL,\n \"name\" text,\n PRIMARY KEY (\"id\")\n)", "params": [], "description": "create table \"user\""}], "summary": "Creates table \"user\"", "precheck": [{"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", "params": ["\"public\".\"user\""], "description": "ensure table \"user\" does not exist"}], "postcheck": [{"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"", "params": ["\"public\".\"user\""], "description": "verify table \"user\" exists"}], "operationClass": "additive"}, {"id": "unique.user.user_email_key", "label": "Add unique constraint on \"user\" (email)", "target": {"id": "postgres", "details": {"name": "user_email_key", "table": "user", "schema": "public", "objectType": "unique"}}, "execute": [{"sql": "ALTER TABLE \"public\".\"user\" ADD CONSTRAINT \"user_email_key\" UNIQUE (\"email\")", "description": "add unique constraint \"user_email_key\""}], "precheck": [{"sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", "params": ["user_email_key", "public", "\"public\".\"user\""], "description": "ensure constraint \"user_email_key\" does not exist"}], "postcheck": [{"sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", "params": ["user_email_key", "public", "\"public\".\"user\""], "description": "verify constraint \"user_email_key\" exists"}], "operationClass": "additive"}]'); -INSERT INTO prisma_contract.marker VALUES ('app', 'f4e1954fd8bed87828d13c3f1a02164dc9796ef1af76ed6f98184c01263169c5', '3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2', NULL, NULL, '2026-09-09 09:15:28.369559+00', NULL, '{}', '{}'); +ALTER TABLE ONLY public."User" ALTER COLUMN id SET DEFAULT nextval('public."User_id_seq"'::regclass); +INSERT INTO prisma_contract.contract VALUES ('a62d15a094e80be7b342d75e0538935972b9699c76dabb5339940dd50d17a889', '2026-10-08 14:56:22.624077+00', '{"meta": {}, "roots": {"User": {"model": "User", "namespace": "public"}}, "domain": {"namespaces": {"public": {"models": {"User": {"fields": {"id": {"many": false, "type": {"kind": "scalar", "codecId": "pg/int4@1"}, "nullable": false}, "name": {"many": false, "type": {"kind": "scalar", "codecId": "pg/text@1"}, "nullable": true}, "email": {"many": false, "type": {"kind": "scalar", "codecId": "pg/text@1"}, "nullable": false}}, "storage": {"table": "User", "fields": {"id": {"column": "id"}, "name": {"column": "name"}, "email": {"column": "email"}}, "namespaceId": "public"}, "relations": {}}}}}}, "target": "postgres", "storage": {"namespaces": {"public": {"id": "public", "entries": {"table": {"User": {"columns": {"id": {"many": false, "codecId": "pg/int4@1", "default": {"kind": "function", "expression": "autoincrement()"}, "dataType": "pg/int4", "nullable": false}, "name": {"many": false, "codecId": "pg/text@1", "dataType": "pg/text", "nullable": true}, "email": {"many": false, "codecId": "pg/text@1", "dataType": "pg/text", "nullable": false}}, "indexes": [], "uniques": [{"columns": ["email"]}], "primaryKey": {"columns": ["id"]}, "foreignKeys": []}}}}}, "storageHash": "a62d15a094e80be7b342d75e0538935972b9699c76dabb5339940dd50d17a889"}, "extensions": {}, "profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2", "capabilities": {"sql": {"enums": true, "lockOf": true, "lateral": true, "forShare": true, "forUpdate": true, "returning": true, "lockNowait": true, "scalarList": true, "lockSkipLocked": true, "checkConstraint": true, "defaultInInsert": true, "insertOnConflictSkip": true, "insertOnConflictWithoutTarget": true}, "postgres": {"limit": true, "jsonAgg": true, "lateral": true, "orderBy": true, "returning": true, "distinctOn": true, "forKeyShare": true, "forNoKeyUpdate": true}}, "targetFamily": "sql"}'); +INSERT INTO prisma_contract.ledger VALUES (1, '2026-10-08 14:56:22.624077+00', 'app', '', 'a62d15a094e80be7b342d75e0538935972b9699c76dabb5339940dd50d17a889', '', NULL, 'a62d15a094e80be7b342d75e0538935972b9699c76dabb5339940dd50d17a889', NULL, '[{"id": "table.User", "label": "Create table \"User\"", "target": {"id": "postgres", "details": {"name": "User", "schema": "public", "objectType": "table"}}, "execute": [{"sql": "CREATE TABLE \"public\".\"User\" (\n \"email\" text NOT NULL,\n \"id\" SERIAL NOT NULL,\n \"name\" text,\n PRIMARY KEY (\"id\")\n)", "params": [], "description": "create table \"User\""}], "summary": "Creates table \"User\"", "precheck": [{"sql": "SELECT (to_regclass($1)) IS NULL AS \"result\"", "params": ["\"public\".\"User\""], "description": "ensure table \"User\" does not exist"}], "postcheck": [{"sql": "SELECT (to_regclass($1)) IS NOT NULL AS \"result\"", "params": ["\"public\".\"User\""], "description": "verify table \"User\" exists"}], "operationClass": "additive"}, {"id": "unique.User.User_email_key", "label": "Add unique constraint on \"User\" (email)", "target": {"id": "postgres", "details": {"name": "User_email_key", "table": "User", "schema": "public", "objectType": "unique"}}, "execute": [{"sql": "ALTER TABLE \"public\".\"User\" ADD CONSTRAINT \"User_email_key\" UNIQUE (\"email\")", "description": "add unique constraint \"User_email_key\""}], "precheck": [{"sql": "SELECT NOT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", "params": ["User_email_key", "public", "\"public\".\"User\""], "description": "ensure constraint \"User_email_key\" does not exist"}], "postcheck": [{"sql": "SELECT EXISTS (SELECT 1 AS \"one\" FROM \"pg_constraint\" AS \"c\" INNER JOIN \"pg_namespace\" AS \"n\" ON \"n\".\"oid\" = \"c\".\"connamespace\" WHERE (\"c\".\"conname\" = $1 AND \"n\".\"nspname\" = $2 AND \"c\".\"conrelid\" = to_regclass($3))) AS \"result\"", "params": ["User_email_key", "public", "\"public\".\"User\""], "description": "verify constraint \"User_email_key\" exists"}], "operationClass": "additive"}]'); +INSERT INTO prisma_contract.marker VALUES ('app', 'a62d15a094e80be7b342d75e0538935972b9699c76dabb5339940dd50d17a889', '3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2', NULL, NULL, '2026-10-08 14:56:22.624077+00', NULL, '{}', '{}'); ALTER TABLE ONLY prisma_contract.contract ADD CONSTRAINT contract_pkey PRIMARY KEY (core_hash); ALTER TABLE ONLY prisma_contract.ledger ADD CONSTRAINT ledger_pkey PRIMARY KEY (id); ALTER TABLE ONLY prisma_contract.marker ADD CONSTRAINT marker_pkey PRIMARY KEY (space); -ALTER TABLE ONLY public."user" - ADD CONSTRAINT user_email_key UNIQUE (email); -ALTER TABLE ONLY public."user" - ADD CONSTRAINT user_pkey PRIMARY KEY (id); +ALTER TABLE ONLY public."User" + ADD CONSTRAINT "User_email_key" UNIQUE (email); +ALTER TABLE ONLY public."User" + ADD CONSTRAINT "User_pkey" PRIMARY KEY (id); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/test.ts b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/test.ts index e27358581b28..d7adfe06da27 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v8/test.ts @@ -18,7 +18,7 @@ function getQuerySpans(container: SerializedStreamedSpanContainer): SerializedSt item => item.attributes['sentry.origin']?.value === 'auto.db.postgres' && item.attributes['db.query.text']?.value, ); expect(querySpans.map(span => span.name)).toEqual( - expect.arrayContaining(['INSERT "public"."user"', 'SELECT "public"."user"', 'DELETE "public"."user"']), + expect.arrayContaining(['INSERT "public"."User"', 'SELECT "public"."User"', 'DELETE "public"."User"']), ); querySpans.forEach(span => { expect(span.attributes['sentry.op']?.value).toBe('db'); @@ -67,7 +67,7 @@ describe('Prisma ORM v8 Tests', () => { 'sentry.origin': { value: 'auto.db.prisma', type: 'string' }, 'sentry.op': { value: 'db', type: 'string' }, 'db.operation.name': { value: method, type: 'string' }, - 'db.collection.name': { value: 'user', type: 'string' }, + 'db.collection.name': { value: 'User', type: 'string' }, model: { value: 'User', type: 'string' }, name: { value: `User.${method}`, type: 'string' }, }); @@ -80,13 +80,13 @@ describe('Prisma ORM v8 Tests', () => { .map(span => span.attributes['db.query.text']?.value); }; expect(queriesByOperation('create')).toEqual( - expect.arrayContaining([expect.stringMatching(/^INSERT INTO "public"\."user" /)]), + expect.arrayContaining([expect.stringMatching(/^INSERT INTO "public"\."User" /)]), ); expect(queriesByOperation('all')).toEqual([ - expect.stringMatching(/^SELECT .* FROM "public"\."user"$/), + expect.stringMatching(/^SELECT .* FROM "public"\."User"$/), ]); expect(queriesByOperation('delete')).toEqual( - expect.arrayContaining([expect.stringMatching(/^DELETE FROM "public"\."user" /)]), + expect.arrayContaining([expect.stringMatching(/^DELETE FROM "public"\."User" /)]), ); expect(querySpans.filter(span => span.parent_span_id === segment.span_id)).toEqual([]); }, @@ -126,7 +126,7 @@ describe('Prisma ORM v8 Tests', () => { }, { additionalDependencies: { - '@prisma/orm-postgres': '8.0.0-rc.8', + '@prisma/orm-postgres': '8.0.0-rc.17', }, copyPaths: ['prisma'], }, diff --git a/packages/server-utils/src/orchestrion/config/prisma.ts b/packages/server-utils/src/orchestrion/config/prisma.ts index 73d6b1a77607..281a97bfba43 100644 --- a/packages/server-utils/src/orchestrion/config/prisma.ts +++ b/packages/server-utils/src/orchestrion/config/prisma.ts @@ -2,12 +2,11 @@ import type { InstrumentationConfig } from '../apmTypes'; import { getModuleNames } from './module-names'; // Prisma 8 has no tracing surface, so its ORM terminals are wrapped directly. Apps install -// `@prisma/orm-postgres`, but `CollectionImpl` lives in the transitive `@prisma/orm-family-sql`. +// `@prisma/orm-postgres`, but `CollectionBase` lives in the transitive `@prisma/orm-family-sql`. const MODULE_NAME = '@prisma/orm-family-sql'; const ORM_CLIENT_FILE = 'dist/orm-client.mjs'; -// The range matcher admits every `8.0.0-rc.*`, not just rc.8 and later; the terminals were verified on rc.8. -const VERSION_RANGE = '>=8.0.0-rc.8 <9'; +const VERSION_RANGE = '>=8.0.0-rc.15 <9'; export const PRISMA_ASYNC_TERMINALS = [ 'aggregate', @@ -31,7 +30,7 @@ function terminalConfig(methodName: PrismaTerminal, kind: 'Async' | 'Sync'): Ins return { channelName: methodName, module: { name: MODULE_NAME, versionRange: VERSION_RANGE, filePath: ORM_CLIENT_FILE }, - functionQuery: { className: 'CollectionImpl', methodName, kind }, + functionQuery: { className: 'CollectionBase', methodName, kind }, }; }