@prisma-next/target-postgres 0.16.0-dev.3 → 0.16.0-dev.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{authoring-34L18kA1.mjs → authoring-DnO1B0N9.mjs} +29 -18
- package/dist/authoring-DnO1B0N9.mjs.map +1 -0
- package/dist/canonicalize-C7zKHw8m.mjs +59 -0
- package/dist/canonicalize-C7zKHw8m.mjs.map +1 -0
- package/dist/{canonicalize-D1F4vUnP.d.mts → canonicalize-CQlxSR4D.d.mts} +3 -18
- package/dist/canonicalize-CQlxSR4D.d.mts.map +1 -0
- package/dist/{codec-types-CQ07nOz-.d.mts → codec-types-DdoPc2dy.d.mts} +3 -2
- package/dist/codec-types-DdoPc2dy.d.mts.map +1 -0
- package/dist/codec-types.d.mts +1 -1
- package/dist/{codecs-G4DTY0F_.d.mts → codecs-CGE5dh94.d.mts} +7 -2
- package/dist/codecs-CGE5dh94.d.mts.map +1 -0
- package/dist/{codecs-TCsGgW-Y.mjs → codecs-Deand7sy.mjs} +84 -26
- package/dist/codecs-Deand7sy.mjs.map +1 -0
- package/dist/codecs.d.mts +2 -2
- package/dist/codecs.mjs +2 -2
- package/dist/contract-free.d.mts +1 -1
- package/dist/contract-free.d.mts.map +1 -1
- package/dist/contract-free.mjs +2 -2
- package/dist/{control-policy-CZKdMztu.mjs → control-policy-DEwdS3oK.mjs} +29 -29
- package/dist/control-policy-DEwdS3oK.mjs.map +1 -0
- package/dist/control.mjs +91 -239
- package/dist/control.mjs.map +1 -1
- package/dist/data-transform-BOWpliq8.mjs.map +1 -1
- package/dist/{ddl-DHXKzQ99.mjs → ddl-BQC6u-L9.mjs} +28 -4
- package/dist/{ddl-DHXKzQ99.mjs.map → ddl-BQC6u-L9.mjs.map} +1 -1
- package/dist/ddl.d.mts +3 -3
- package/dist/ddl.mjs +2 -2
- package/dist/{descriptor-meta-qubYaxQA.mjs → descriptor-meta-DGlJurgA.mjs} +4 -4
- package/dist/{descriptor-meta-qubYaxQA.mjs.map → descriptor-meta-DGlJurgA.mjs.map} +1 -1
- package/dist/{diff-database-schema-C-hnJ5mr.mjs → diff-database-schema-qRPwuYBH.mjs} +37 -15
- package/dist/diff-database-schema-qRPwuYBH.mjs.map +1 -0
- package/dist/diff-database-schema.mjs +1 -1
- package/dist/{errors-ByDS0xJL.mjs → errors-DBSNx7EJ.mjs} +6 -2
- package/dist/errors-DBSNx7EJ.mjs.map +1 -0
- package/dist/errors.d.mts +4 -1
- package/dist/errors.d.mts.map +1 -1
- package/dist/errors.mjs +2 -2
- package/dist/issue-planner.d.mts +2 -2
- package/dist/issue-planner.d.mts.map +1 -1
- package/dist/issue-planner.mjs +1 -1
- package/dist/migration.d.mts +2 -2
- package/dist/migration.mjs +2 -2
- package/dist/{nodes-DJC5B8Id.mjs → nodes-04EILM-z.mjs} +65 -2
- package/dist/nodes-04EILM-z.mjs.map +1 -0
- package/dist/{nodes-CQzM2hR0.d.mts → nodes-DjCsN-uv.d.mts} +69 -4
- package/dist/nodes-DjCsN-uv.d.mts.map +1 -0
- package/dist/{op-factory-call-DT7kdjVP.d.mts → op-factory-call-0TB5XNij.d.mts} +46 -8
- package/dist/op-factory-call-0TB5XNij.d.mts.map +1 -0
- package/dist/{op-factory-call-DuRj7WXX.mjs → op-factory-call-CIaPDDUc.mjs} +147 -64
- package/dist/op-factory-call-CIaPDDUc.mjs.map +1 -0
- package/dist/op-factory-call.d.mts +2 -2
- package/dist/op-factory-call.mjs +2 -2
- package/dist/pack.d.mts +2 -2
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/{planner-DPGxFcXo.mjs → planner-BhnkIJX-.mjs} +121 -13
- package/dist/planner-BhnkIJX-.mjs.map +1 -0
- package/dist/{planner-ddl-builders-BKlmGDJl.mjs → planner-ddl-builders-CVo3L7Nd.mjs} +12 -7
- package/dist/planner-ddl-builders-CVo3L7Nd.mjs.map +1 -0
- package/dist/planner-ddl-builders.d.mts.map +1 -1
- package/dist/planner-ddl-builders.mjs +1 -1
- package/dist/{planner-produced-postgres-migration-CFDpqWWE.mjs → planner-produced-postgres-migration-BE3EJIRK.mjs} +9 -6
- package/dist/planner-produced-postgres-migration-BE3EJIRK.mjs.map +1 -0
- package/dist/{planner-produced-postgres-migration-C-dwGdb2.d.mts → planner-produced-postgres-migration-CRAHl6ak.d.mts} +3 -3
- package/dist/planner-produced-postgres-migration-CRAHl6ak.d.mts.map +1 -0
- package/dist/planner-produced-postgres-migration.d.mts +1 -1
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/{planner-schema-lookup-CiVaAQP-.mjs → planner-schema-lookup-D3Y_OL-C.mjs} +3 -3
- package/dist/planner-schema-lookup-D3Y_OL-C.mjs.map +1 -0
- package/dist/planner-schema-lookup.mjs +1 -1
- package/dist/{planner-sql-checks-BuB877wH.mjs → planner-sql-checks-CmiQ6Nei.mjs} +3 -3
- package/dist/{planner-sql-checks-BuB877wH.mjs.map → planner-sql-checks-CmiQ6Nei.mjs.map} +1 -1
- package/dist/planner-sql-checks.mjs +1 -1
- package/dist/planner.d.mts +27 -1
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +2 -2
- package/dist/{postgres-contract-view-DebHMqGR.mjs → postgres-contract-view-BeIEyZfD.mjs} +3 -3
- package/dist/{postgres-contract-view-DebHMqGR.mjs.map → postgres-contract-view-BeIEyZfD.mjs.map} +1 -1
- package/dist/{postgres-migration-C_OPFhIF.mjs → postgres-migration--BzvpA92.mjs} +9 -6
- package/dist/postgres-migration--BzvpA92.mjs.map +1 -0
- package/dist/{postgres-migration-DLIvzHjX.d.mts → postgres-migration-BXelnqHO.d.mts} +15 -10
- package/dist/postgres-migration-BXelnqHO.d.mts.map +1 -0
- package/dist/{postgres-schema-CewLYHB7.mjs → postgres-schema-CTplQZDK.mjs} +2 -2
- package/dist/{postgres-schema-CewLYHB7.mjs.map → postgres-schema-CTplQZDK.mjs.map} +1 -1
- package/dist/{render-ops-BREh1kHe.mjs → render-ops-TcY9XEkz.mjs} +7 -2
- package/dist/render-ops-TcY9XEkz.mjs.map +1 -0
- package/dist/render-ops.d.mts.map +1 -1
- package/dist/render-ops.mjs +1 -1
- package/dist/{render-typescript-7yqILcwr.mjs → render-typescript-Ta7_6CGf.mjs} +20 -8
- package/dist/render-typescript-Ta7_6CGf.mjs.map +1 -0
- package/dist/render-typescript.d.mts +2 -0
- package/dist/render-typescript.d.mts.map +1 -1
- package/dist/render-typescript.mjs +1 -1
- package/dist/{resolve-ddl-schema-D0wi_P9Q.mjs → resolve-ddl-schema-BAsJEKW8.mjs} +2 -2
- package/dist/{resolve-ddl-schema-D0wi_P9Q.mjs.map → resolve-ddl-schema-BAsJEKW8.mjs.map} +1 -1
- package/dist/rls-canonicalize.d.mts +2 -26
- package/dist/rls-canonicalize.mjs +2 -2
- package/dist/runtime.mjs +1 -1
- package/dist/schema-ir-annotations.mjs +1 -1
- package/dist/{sql-utils-SU4FDvIV.mjs → sql-utils-B2lzkKfP.mjs} +22 -19
- package/dist/sql-utils-B2lzkKfP.mjs.map +1 -0
- package/dist/sql-utils.d.mts +4 -9
- package/dist/sql-utils.d.mts.map +1 -1
- package/dist/sql-utils.mjs +2 -2
- package/dist/types.mjs +1 -1
- package/package.json +20 -20
- package/src/contract-free/ddl.ts +47 -0
- package/src/core/authoring.ts +35 -26
- package/src/core/codec-helpers.ts +69 -16
- package/src/core/codec-type-map.ts +2 -0
- package/src/core/codecs.ts +29 -14
- package/src/core/ddl/nodes.ts +104 -0
- package/src/core/errors.ts +30 -0
- package/src/core/migrations/column-ddl-rendering.ts +2 -1
- package/src/core/migrations/contract-to-postgres-database-schema-node.ts +38 -14
- package/src/core/migrations/issue-planner.ts +40 -13
- package/src/core/migrations/op-factory-call.ts +189 -44
- package/src/core/migrations/operations/constraints.ts +3 -2
- package/src/core/migrations/operations/data-transform.ts +1 -1
- package/src/core/migrations/operations/indexes.ts +80 -32
- package/src/core/migrations/operations/rls.ts +4 -1
- package/src/core/migrations/planner-ddl-builders.ts +16 -6
- package/src/core/migrations/planner-produced-postgres-migration.ts +8 -1
- package/src/core/migrations/planner-schema-lookup.ts +2 -2
- package/src/core/migrations/planner.ts +163 -5
- package/src/core/migrations/postgres-migration.ts +26 -8
- package/src/core/migrations/render-ops.ts +4 -1
- package/src/core/migrations/render-typescript.ts +20 -8
- package/src/core/migrations/runner.ts +7 -3
- package/src/core/psl-infer/infer-psl-contract.ts +36 -185
- package/src/core/psl-infer/postgres-type-map.ts +43 -65
- package/src/core/rls/canonicalize.ts +4 -21
- package/src/core/sql-utils.ts +16 -27
- package/src/exports/codecs.ts +2 -0
- package/src/exports/ddl.ts +4 -0
- package/src/exports/errors.ts +1 -1
- package/src/exports/op-factory-call.ts +1 -0
- package/src/exports/rls-canonicalize.ts +0 -6
- package/src/exports/sql-utils.ts +0 -1
- package/dist/authoring-34L18kA1.mjs.map +0 -1
- package/dist/canonicalize-D1F4vUnP.d.mts.map +0 -1
- package/dist/codec-types-CQ07nOz-.d.mts.map +0 -1
- package/dist/codecs-G4DTY0F_.d.mts.map +0 -1
- package/dist/codecs-TCsGgW-Y.mjs.map +0 -1
- package/dist/control-policy-CZKdMztu.mjs.map +0 -1
- package/dist/diff-database-schema-C-hnJ5mr.mjs.map +0 -1
- package/dist/errors-ByDS0xJL.mjs.map +0 -1
- package/dist/nodes-CQzM2hR0.d.mts.map +0 -1
- package/dist/nodes-DJC5B8Id.mjs.map +0 -1
- package/dist/op-factory-call-DT7kdjVP.d.mts.map +0 -1
- package/dist/op-factory-call-DuRj7WXX.mjs.map +0 -1
- package/dist/planner-DPGxFcXo.mjs.map +0 -1
- package/dist/planner-ddl-builders-BKlmGDJl.mjs.map +0 -1
- package/dist/planner-produced-postgres-migration-C-dwGdb2.d.mts.map +0 -1
- package/dist/planner-produced-postgres-migration-CFDpqWWE.mjs.map +0 -1
- package/dist/planner-schema-lookup-CiVaAQP-.mjs.map +0 -1
- package/dist/postgres-migration-C_OPFhIF.mjs.map +0 -1
- package/dist/postgres-migration-DLIvzHjX.d.mts.map +0 -1
- package/dist/render-ops-BREh1kHe.mjs.map +0 -1
- package/dist/render-typescript-7yqILcwr.mjs.map +0 -1
- package/dist/rls-canonicalize.d.mts.map +0 -1
- package/dist/sql-utils-SU4FDvIV.mjs.map +0 -1
- package/dist/wire-name-DbQ9-2RC.mjs +0 -104
- package/dist/wire-name-DbQ9-2RC.mjs.map +0 -1
- package/src/core/rls/wire-name.ts +0 -33
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-ddl-schema-
|
|
1
|
+
{"version":3,"file":"resolve-ddl-schema-BAsJEKW8.mjs","names":[],"sources":["../src/core/migrations/resolve-ddl-schema.ts"],"sourcesContent":["import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';\nimport type { SqlStorage } from '@prisma-next/sql-contract/types';\nimport { DEFAULT_NAMESPACE_ID } from '../namespace-ids';\nimport { isPostgresSchema } from '../postgres-schema';\n\n/**\n * Given the contract's storage and a namespace id, returns the live Postgres\n * DDL schema name that namespace maps to. A named Postgres namespace dispatches\n * to its `ddlSchemaName(storage)`; the unbound sentinel resolves to\n * `DEFAULT_NAMESPACE_ID` (the search-path default for offline planning); a\n * bare object payload (used by some tests) falls back to the namespace id\n * itself.\n */\nexport function resolveDdlSchemaForNamespaceStorage(\n storage: SqlStorage,\n namespaceId: string,\n): string {\n if (namespaceId === UNBOUND_NAMESPACE_ID) {\n return DEFAULT_NAMESPACE_ID;\n }\n const namespace = storage.namespaces[namespaceId];\n if (namespace && isPostgresSchema(namespace)) {\n return namespace.ddlSchemaName(storage);\n }\n return namespaceId;\n}\n"],"mappings":";;;;;;;;;;;;AAaA,SAAgB,oCACd,SACA,aACQ;CACR,IAAI,gBAAgB,sBAClB,OAAO;CAET,MAAM,YAAY,QAAQ,WAAW;CACrC,IAAI,aAAa,iBAAiB,SAAS,GACzC,OAAO,UAAU,cAAc,OAAO;CAExC,OAAO;AACT"}
|
|
@@ -1,26 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
interface RlsPolicyWireName {
|
|
4
|
-
/** The user-supplied part before the `_<8hex>` suffix. */
|
|
5
|
-
readonly prefix: string;
|
|
6
|
-
/** The 8-lowercase-hex content-hash suffix. */
|
|
7
|
-
readonly hash: string;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Assembles an RLS policy wire name from its user-supplied prefix and its
|
|
11
|
-
* 8-hex content-hash suffix. This module owns the `<prefix>_<hash>` format
|
|
12
|
-
* on both sides — construction here and parsing in
|
|
13
|
-
* {@link parseRlsPolicyWireName} — so the two never drift.
|
|
14
|
-
*/
|
|
15
|
-
declare function formatRlsPolicyWireName(prefix: string, hash: string): string;
|
|
16
|
-
/**
|
|
17
|
-
* Splits an RLS policy wire name (`<prefix>_<8hex>`) into its prefix and
|
|
18
|
-
* content-hash suffix. Returns `undefined` when the name does not follow the
|
|
19
|
-
* wire-name shape (e.g. a policy created outside the toolchain) — callers
|
|
20
|
-
* treat such names as all-prefix. Consumed by introspection (prefix
|
|
21
|
-
* extraction) and by rename pairing (same hash, different prefix).
|
|
22
|
-
*/
|
|
23
|
-
declare function parseRlsPolicyWireName(name: string): RlsPolicyWireName | undefined;
|
|
24
|
-
//#endregion
|
|
25
|
-
export { type ContentHashParts, POLICY_OPERATION_PREDICATES, type RlsPolicyOperation, type RlsPolicyWireName, computeContentHash, formatRlsPolicyWireName, normalizePredicate, parseRlsPolicyWireName };
|
|
26
|
-
//# sourceMappingURL=rls-canonicalize.d.mts.map
|
|
1
|
+
import { i as computeContentHash, n as POLICY_OPERATION_PREDICATES, r as RlsPolicyOperation, t as ContentHashParts } from "./canonicalize-CQlxSR4D.mjs";
|
|
2
|
+
export { type ContentHashParts, POLICY_OPERATION_PREDICATES, type RlsPolicyOperation, computeContentHash };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { POLICY_OPERATION_PREDICATES, computeContentHash
|
|
1
|
+
import { n as computeContentHash, t as POLICY_OPERATION_PREDICATES } from "./canonicalize-C7zKHw8m.mjs";
|
|
2
|
+
export { POLICY_OPERATION_PREDICATES, computeContentHash };
|
package/dist/runtime.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as postgresTargetDescriptorMetaRuntime } from "./descriptor-meta-runtime-UwYdV8fD.mjs";
|
|
2
|
-
import { n as PostgresContractSerializer, t as PostgresContractView } from "./postgres-contract-view-
|
|
2
|
+
import { n as PostgresContractSerializer, t as PostgresContractView } from "./postgres-contract-view-BeIEyZfD.mjs";
|
|
3
3
|
//#region src/exports/runtime.ts
|
|
4
4
|
/**
|
|
5
5
|
* Target-postgres deliberately does NOT import `SqlRuntimeTargetDescriptor` from `@prisma-next/sql-runtime`. The target package is a control-plane residence and must not pull the SQL execution-plane package into its dependency closure. The runtime descriptor here is shaped to satisfy the framework's `RuntimeTargetDescriptor` plus the structural `SqlStaticContributions` (`codecs:` returning a descriptor list) that
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as resolveDdlSchemaForNamespaceStorage } from "./resolve-ddl-schema-
|
|
1
|
+
import { t as resolveDdlSchemaForNamespaceStorage } from "./resolve-ddl-schema-BAsJEKW8.mjs";
|
|
2
2
|
export { resolveDdlSchemaForNamespaceStorage };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { n as postgresError } from "./errors-DBSNx7EJ.mjs";
|
|
1
2
|
//#region src/core/sql-utils.ts
|
|
2
3
|
/**
|
|
3
4
|
* Shared SQL utility functions for the Postgres target.
|
|
@@ -10,16 +11,6 @@
|
|
|
10
11
|
* escaping/validation behavior, and the target package is the natural shared
|
|
11
12
|
* home that both adapters can depend on without crossing planes.
|
|
12
13
|
*/
|
|
13
|
-
var SqlEscapeError = class extends Error {
|
|
14
|
-
value;
|
|
15
|
-
kind;
|
|
16
|
-
constructor(message, value, kind) {
|
|
17
|
-
super(message);
|
|
18
|
-
this.value = value;
|
|
19
|
-
this.kind = kind;
|
|
20
|
-
this.name = "SqlEscapeError";
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
14
|
const MAX_IDENTIFIER_LENGTH = 63;
|
|
24
15
|
/**
|
|
25
16
|
* Validates and quotes a PostgreSQL identifier (table, column, type, schema names).
|
|
@@ -29,11 +20,17 @@ const MAX_IDENTIFIER_LENGTH = 63;
|
|
|
29
20
|
* - Rejects empty identifiers
|
|
30
21
|
* - Warns on identifiers exceeding PostgreSQL's 63-character limit
|
|
31
22
|
*
|
|
32
|
-
* @throws
|
|
23
|
+
* @throws `CONTRACT.IDENTIFIER_INVALID` structured error If the identifier contains null bytes or is empty
|
|
33
24
|
*/
|
|
34
25
|
function quoteIdentifier(identifier) {
|
|
35
|
-
if (identifier.length === 0) throw
|
|
36
|
-
|
|
26
|
+
if (identifier.length === 0) throw postgresError("CONTRACT.IDENTIFIER_INVALID", "Identifier cannot be empty", { meta: {
|
|
27
|
+
value: identifier,
|
|
28
|
+
context: "identifier"
|
|
29
|
+
} });
|
|
30
|
+
if (identifier.includes("\0")) throw postgresError("CONTRACT.IDENTIFIER_INVALID", "Identifier cannot contain null bytes", { meta: {
|
|
31
|
+
value: identifier.replace(/\0/g, "\\0"),
|
|
32
|
+
context: "identifier"
|
|
33
|
+
} });
|
|
37
34
|
if (identifier.length > MAX_IDENTIFIER_LENGTH) console.warn(`Identifier "${identifier.slice(0, 20)}..." exceeds PostgreSQL's ${MAX_IDENTIFIER_LENGTH}-character limit and will be truncated`);
|
|
38
35
|
return `"${identifier.replace(/"/g, "\"\"")}"`;
|
|
39
36
|
}
|
|
@@ -46,10 +43,13 @@ function quoteIdentifier(identifier) {
|
|
|
46
43
|
* Note: This assumes PostgreSQL's `standard_conforming_strings` is ON (default since PG 9.1).
|
|
47
44
|
* Backslashes are treated as literal characters, not escape sequences.
|
|
48
45
|
*
|
|
49
|
-
* @throws
|
|
46
|
+
* @throws `CONTRACT.IDENTIFIER_INVALID` structured error If the value contains null bytes
|
|
50
47
|
*/
|
|
51
48
|
function escapeLiteral(value) {
|
|
52
|
-
if (value.includes("\0")) throw
|
|
49
|
+
if (value.includes("\0")) throw postgresError("CONTRACT.IDENTIFIER_INVALID", "Literal value cannot contain null bytes", { meta: {
|
|
50
|
+
value: value.replace(/\0/g, "\\0"),
|
|
51
|
+
context: "literal"
|
|
52
|
+
} });
|
|
53
53
|
return value.replace(/'/g, "''");
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
@@ -76,12 +76,15 @@ function quoteQualifiedName(name) {
|
|
|
76
76
|
*
|
|
77
77
|
* @param value - The enum value to validate
|
|
78
78
|
* @param enumTypeName - Name of the enum type (for error messages)
|
|
79
|
-
* @throws
|
|
79
|
+
* @throws `CONTRACT.IDENTIFIER_INVALID` structured error If the value exceeds the maximum length
|
|
80
80
|
*/
|
|
81
81
|
function validateEnumValueLength(value, enumTypeName) {
|
|
82
|
-
if (new TextEncoder().encode(value).length > MAX_IDENTIFIER_LENGTH) throw
|
|
82
|
+
if (new TextEncoder().encode(value).length > MAX_IDENTIFIER_LENGTH) throw postgresError("CONTRACT.IDENTIFIER_INVALID", `Enum value "${value.slice(0, 20)}..." for type "${enumTypeName}" exceeds PostgreSQL's ${MAX_IDENTIFIER_LENGTH}-byte label limit`, { meta: {
|
|
83
|
+
value,
|
|
84
|
+
context: "enum-label"
|
|
85
|
+
} });
|
|
83
86
|
}
|
|
84
87
|
//#endregion
|
|
85
|
-
export {
|
|
88
|
+
export { validateEnumValueLength as a, quoteQualifiedName as i, qualifyName as n, quoteIdentifier as r, escapeLiteral as t };
|
|
86
89
|
|
|
87
|
-
//# sourceMappingURL=sql-utils-
|
|
90
|
+
//# sourceMappingURL=sql-utils-B2lzkKfP.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-utils-B2lzkKfP.mjs","names":[],"sources":["../src/core/sql-utils.ts"],"sourcesContent":["/**\n * Shared SQL utility functions for the Postgres target.\n *\n * These functions handle safe SQL identifier and literal escaping\n * with security validations to prevent injection and encoding issues.\n *\n * They live in `target-postgres` because both the control adapter (used at\n * emit time) and the runtime adapter (used at execute time) need the same\n * escaping/validation behavior, and the target package is the natural shared\n * home that both adapters can depend on without crossing planes.\n */\n\nimport { postgresError } from './errors';\n\nconst MAX_IDENTIFIER_LENGTH = 63;\n\n/**\n * Validates and quotes a PostgreSQL identifier (table, column, type, schema names).\n *\n * Security validations:\n * - Rejects null bytes which could cause truncation or unexpected behavior\n * - Rejects empty identifiers\n * - Warns on identifiers exceeding PostgreSQL's 63-character limit\n *\n * @throws `CONTRACT.IDENTIFIER_INVALID` structured error If the identifier contains null bytes or is empty\n */\nexport function quoteIdentifier(identifier: string): string {\n if (identifier.length === 0) {\n throw postgresError('CONTRACT.IDENTIFIER_INVALID', 'Identifier cannot be empty', {\n meta: { value: identifier, context: 'identifier' },\n });\n }\n if (identifier.includes('\\0')) {\n throw postgresError('CONTRACT.IDENTIFIER_INVALID', 'Identifier cannot contain null bytes', {\n meta: { value: identifier.replace(/\\0/g, '\\\\0'), context: 'identifier' },\n });\n }\n if (identifier.length > MAX_IDENTIFIER_LENGTH) {\n console.warn(\n `Identifier \"${identifier.slice(0, 20)}...\" exceeds PostgreSQL's ${MAX_IDENTIFIER_LENGTH}-character limit and will be truncated`,\n );\n }\n return `\"${identifier.replace(/\"/g, '\"\"')}\"`;\n}\n\n/**\n * Escapes a string literal for safe use in SQL statements.\n *\n * Security validations:\n * - Rejects null bytes which could cause truncation or unexpected behavior\n *\n * Note: This assumes PostgreSQL's `standard_conforming_strings` is ON (default since PG 9.1).\n * Backslashes are treated as literal characters, not escape sequences.\n *\n * @throws `CONTRACT.IDENTIFIER_INVALID` structured error If the value contains null bytes\n */\nexport function escapeLiteral(value: string): string {\n if (value.includes('\\0')) {\n throw postgresError('CONTRACT.IDENTIFIER_INVALID', 'Literal value cannot contain null bytes', {\n meta: { value: value.replace(/\\0/g, '\\\\0'), context: 'literal' },\n });\n }\n return value.replace(/'/g, \"''\");\n}\n\n/**\n * Builds a qualified name (schema.object) with proper quoting.\n */\nexport function qualifyName(schemaName: string, objectName: string): string {\n return `${quoteIdentifier(schemaName)}.${quoteIdentifier(objectName)}`;\n}\n\n/**\n * Quotes a possibly dot-qualified name segment-by-segment:\n * `order_status` -> `\"order_status\"`, `auth.aal_level` -> `\"auth\".\"aal_level\"`.\n * A single-segment name round-trips to exactly {@link quoteIdentifier}; whole-\n * string quoting (`\"auth.aal_level\"`) would be one wrong identifier.\n */\nexport function quoteQualifiedName(name: string): string {\n return name\n .split('.')\n .map((segment) => quoteIdentifier(segment))\n .join('.');\n}\n\n/**\n * Validates that an enum value doesn't exceed PostgreSQL's label length limit.\n *\n * PostgreSQL enum labels have a maximum length of NAMEDATALEN-1 (63 bytes by default).\n * Unlike identifiers, enum labels that exceed this limit cause an error rather than\n * silent truncation.\n *\n * @param value - The enum value to validate\n * @param enumTypeName - Name of the enum type (for error messages)\n * @throws `CONTRACT.IDENTIFIER_INVALID` structured error If the value exceeds the maximum length\n */\nexport function validateEnumValueLength(value: string, enumTypeName: string): void {\n if (new TextEncoder().encode(value).length > MAX_IDENTIFIER_LENGTH) {\n throw postgresError(\n 'CONTRACT.IDENTIFIER_INVALID',\n `Enum value \"${value.slice(0, 20)}...\" for type \"${enumTypeName}\" exceeds PostgreSQL's ` +\n `${MAX_IDENTIFIER_LENGTH}-byte label limit`,\n { meta: { value, context: 'enum-label' } },\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAcA,MAAM,wBAAwB;;;;;;;;;;;AAY9B,SAAgB,gBAAgB,YAA4B;CAC1D,IAAI,WAAW,WAAW,GACxB,MAAM,cAAc,+BAA+B,8BAA8B,EAC/E,MAAM;EAAE,OAAO;EAAY,SAAS;CAAa,EACnD,CAAC;CAEH,IAAI,WAAW,SAAS,IAAI,GAC1B,MAAM,cAAc,+BAA+B,wCAAwC,EACzF,MAAM;EAAE,OAAO,WAAW,QAAQ,OAAO,KAAK;EAAG,SAAS;CAAa,EACzE,CAAC;CAEH,IAAI,WAAW,SAAS,uBACtB,QAAQ,KACN,eAAe,WAAW,MAAM,GAAG,EAAE,EAAE,4BAA4B,sBAAsB,uCAC3F;CAEF,OAAO,IAAI,WAAW,QAAQ,MAAM,MAAI,EAAE;AAC5C;;;;;;;;;;;;AAaA,SAAgB,cAAc,OAAuB;CACnD,IAAI,MAAM,SAAS,IAAI,GACrB,MAAM,cAAc,+BAA+B,2CAA2C,EAC5F,MAAM;EAAE,OAAO,MAAM,QAAQ,OAAO,KAAK;EAAG,SAAS;CAAU,EACjE,CAAC;CAEH,OAAO,MAAM,QAAQ,MAAM,IAAI;AACjC;;;;AAKA,SAAgB,YAAY,YAAoB,YAA4B;CAC1E,OAAO,GAAG,gBAAgB,UAAU,EAAE,GAAG,gBAAgB,UAAU;AACrE;;;;;;;AAQA,SAAgB,mBAAmB,MAAsB;CACvD,OAAO,KACJ,MAAM,GAAG,CAAC,CACV,KAAK,YAAY,gBAAgB,OAAO,CAAC,CAAC,CAC1C,KAAK,GAAG;AACb;;;;;;;;;;;;AAaA,SAAgB,wBAAwB,OAAe,cAA4B;CACjF,IAAI,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,SAAS,uBAC3C,MAAM,cACJ,+BACA,eAAe,MAAM,MAAM,GAAG,EAAE,EAAE,iBAAiB,aAAa,yBAC3D,sBAAsB,oBAC3B,EAAE,MAAM;EAAE;EAAO,SAAS;CAAa,EAAE,CAC3C;AAEJ"}
|
package/dist/sql-utils.d.mts
CHANGED
|
@@ -10,11 +10,6 @@
|
|
|
10
10
|
* escaping/validation behavior, and the target package is the natural shared
|
|
11
11
|
* home that both adapters can depend on without crossing planes.
|
|
12
12
|
*/
|
|
13
|
-
declare class SqlEscapeError extends Error {
|
|
14
|
-
readonly value: string;
|
|
15
|
-
readonly kind: 'identifier' | 'literal';
|
|
16
|
-
constructor(message: string, value: string, kind: 'identifier' | 'literal');
|
|
17
|
-
}
|
|
18
13
|
/**
|
|
19
14
|
* Validates and quotes a PostgreSQL identifier (table, column, type, schema names).
|
|
20
15
|
*
|
|
@@ -23,7 +18,7 @@ declare class SqlEscapeError extends Error {
|
|
|
23
18
|
* - Rejects empty identifiers
|
|
24
19
|
* - Warns on identifiers exceeding PostgreSQL's 63-character limit
|
|
25
20
|
*
|
|
26
|
-
* @throws
|
|
21
|
+
* @throws `CONTRACT.IDENTIFIER_INVALID` structured error If the identifier contains null bytes or is empty
|
|
27
22
|
*/
|
|
28
23
|
declare function quoteIdentifier(identifier: string): string;
|
|
29
24
|
/**
|
|
@@ -35,7 +30,7 @@ declare function quoteIdentifier(identifier: string): string;
|
|
|
35
30
|
* Note: This assumes PostgreSQL's `standard_conforming_strings` is ON (default since PG 9.1).
|
|
36
31
|
* Backslashes are treated as literal characters, not escape sequences.
|
|
37
32
|
*
|
|
38
|
-
* @throws
|
|
33
|
+
* @throws `CONTRACT.IDENTIFIER_INVALID` structured error If the value contains null bytes
|
|
39
34
|
*/
|
|
40
35
|
declare function escapeLiteral(value: string): string;
|
|
41
36
|
/**
|
|
@@ -58,9 +53,9 @@ declare function quoteQualifiedName(name: string): string;
|
|
|
58
53
|
*
|
|
59
54
|
* @param value - The enum value to validate
|
|
60
55
|
* @param enumTypeName - Name of the enum type (for error messages)
|
|
61
|
-
* @throws
|
|
56
|
+
* @throws `CONTRACT.IDENTIFIER_INVALID` structured error If the value exceeds the maximum length
|
|
62
57
|
*/
|
|
63
58
|
declare function validateEnumValueLength(value: string, enumTypeName: string): void;
|
|
64
59
|
//#endregion
|
|
65
|
-
export {
|
|
60
|
+
export { escapeLiteral, qualifyName, quoteIdentifier, quoteQualifiedName, validateEnumValueLength };
|
|
66
61
|
//# sourceMappingURL=sql-utils.d.mts.map
|
package/dist/sql-utils.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-utils.d.mts","names":[],"sources":["../src/core/sql-utils.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"sql-utils.d.mts","names":[],"sources":["../src/core/sql-utils.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;iBA0BgB,gBAAgB;;;;;;;;;;;;iBA8BhB,cAAc;;;;iBAYd,YAAY,oBAAoB;;;;;;;iBAUhC,mBAAmB;;;;;;;;;;;;iBAkBnB,wBAAwB,eAAe"}
|
package/dist/sql-utils.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export {
|
|
1
|
+
import { a as validateEnumValueLength, i as quoteQualifiedName, n as qualifyName, r as quoteIdentifier, t as escapeLiteral } from "./sql-utils-B2lzkKfP.mjs";
|
|
2
|
+
export { escapeLiteral, qualifyName, quoteIdentifier, quoteQualifiedName, validateEnumValueLength };
|
package/dist/types.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as PostgresNativeEnum } from "./postgres-native-enum-BwVNf0Ku.mjs";
|
|
2
|
-
import { _ as PostgresRlsEnablement, g as PostgresRlsPolicy, i as postgresCreateNamespace, m as PostgresRole, n as PostgresUnboundSchema, r as isPostgresSchema, t as PostgresSchema } from "./postgres-schema-
|
|
2
|
+
import { _ as PostgresRlsEnablement, g as PostgresRlsPolicy, i as postgresCreateNamespace, m as PostgresRole, n as PostgresUnboundSchema, r as isPostgresSchema, t as PostgresSchema } from "./postgres-schema-CTplQZDK.mjs";
|
|
3
3
|
import { n as PostgresNativeEnumSchemaNode, r as PostgresSchemaNodeKind, t as PostgresTableSchemaNode } from "./postgres-table-schema-node-D6LvInCe.mjs";
|
|
4
4
|
import { i as PostgresDatabaseSchemaNode, n as PostgresPolicySchemaNode, r as PostgresNamespaceSchemaNode, t as PostgresRoleSchemaNode } from "./postgres-role-schema-node-bg32e7I-.mjs";
|
|
5
5
|
export { PostgresDatabaseSchemaNode, PostgresNamespaceSchemaNode, PostgresNativeEnum, PostgresNativeEnumSchemaNode, PostgresPolicySchemaNode, PostgresRlsEnablement, PostgresRlsPolicy, PostgresRole, PostgresRoleSchemaNode, PostgresSchema, PostgresSchemaNodeKind, PostgresTableSchemaNode, PostgresUnboundSchema, isPostgresSchema, postgresCreateNamespace };
|
package/package.json
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/target-postgres",
|
|
3
|
-
"version": "0.16.0-dev.
|
|
3
|
+
"version": "0.16.0-dev.31",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"description": "Postgres target pack for Prisma Next",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@prisma-next/cli": "0.16.0-dev.
|
|
10
|
-
"@prisma-next/contract": "0.16.0-dev.
|
|
11
|
-
"@prisma-next/errors": "0.16.0-dev.
|
|
12
|
-
"@prisma-next/family-sql": "0.16.0-dev.
|
|
13
|
-
"@prisma-next/framework-components": "0.16.0-dev.
|
|
14
|
-
"@prisma-next/migration-tools": "0.16.0-dev.
|
|
15
|
-
"@prisma-next/psl-parser": "0.16.0-dev.
|
|
16
|
-
"@prisma-next/sql-contract": "0.16.0-dev.
|
|
17
|
-
"@prisma-next/sql-errors": "0.16.0-dev.
|
|
18
|
-
"@prisma-next/sql-operations": "0.16.0-dev.
|
|
19
|
-
"@prisma-next/sql-relational-core": "0.16.0-dev.
|
|
20
|
-
"@prisma-next/sql-schema-ir": "0.16.0-dev.
|
|
21
|
-
"@prisma-next/ts-render": "0.16.0-dev.
|
|
22
|
-
"@prisma-next/utils": "0.16.0-dev.
|
|
9
|
+
"@prisma-next/cli": "0.16.0-dev.31",
|
|
10
|
+
"@prisma-next/contract": "0.16.0-dev.31",
|
|
11
|
+
"@prisma-next/errors": "0.16.0-dev.31",
|
|
12
|
+
"@prisma-next/family-sql": "0.16.0-dev.31",
|
|
13
|
+
"@prisma-next/framework-components": "0.16.0-dev.31",
|
|
14
|
+
"@prisma-next/migration-tools": "0.16.0-dev.31",
|
|
15
|
+
"@prisma-next/psl-parser": "0.16.0-dev.31",
|
|
16
|
+
"@prisma-next/sql-contract": "0.16.0-dev.31",
|
|
17
|
+
"@prisma-next/sql-errors": "0.16.0-dev.31",
|
|
18
|
+
"@prisma-next/sql-operations": "0.16.0-dev.31",
|
|
19
|
+
"@prisma-next/sql-relational-core": "0.16.0-dev.31",
|
|
20
|
+
"@prisma-next/sql-schema-ir": "0.16.0-dev.31",
|
|
21
|
+
"@prisma-next/ts-render": "0.16.0-dev.31",
|
|
22
|
+
"@prisma-next/utils": "0.16.0-dev.31",
|
|
23
23
|
"@standard-schema/spec": "^1.1.0",
|
|
24
24
|
"arktype": "^2.2.2",
|
|
25
25
|
"pathe": "^2.0.3"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@prisma-next/psl-printer": "0.16.0-dev.
|
|
29
|
-
"@prisma-next/sql-contract-psl": "0.16.0-dev.
|
|
30
|
-
"@prisma-next/test-utils": "0.16.0-dev.
|
|
31
|
-
"@prisma-next/tsconfig": "0.16.0-dev.
|
|
32
|
-
"@prisma-next/tsdown": "0.16.0-dev.
|
|
28
|
+
"@prisma-next/psl-printer": "0.16.0-dev.31",
|
|
29
|
+
"@prisma-next/sql-contract-psl": "0.16.0-dev.31",
|
|
30
|
+
"@prisma-next/test-utils": "0.16.0-dev.31",
|
|
31
|
+
"@prisma-next/tsconfig": "0.16.0-dev.31",
|
|
32
|
+
"@prisma-next/tsdown": "0.16.0-dev.31",
|
|
33
33
|
"tsdown": "0.22.8",
|
|
34
34
|
"typescript": "5.9.3",
|
|
35
35
|
"vitest": "4.1.10"
|
package/src/contract-free/ddl.ts
CHANGED
|
@@ -2,14 +2,18 @@ import type { DdlColumn, DdlTableConstraint } from '@prisma-next/sql-relational-
|
|
|
2
2
|
import {
|
|
3
3
|
AddColumnAction,
|
|
4
4
|
type AnyAlterTableAction,
|
|
5
|
+
type DdlIndexElements,
|
|
5
6
|
DropDefaultAction,
|
|
7
|
+
PostgresAlterIndexRename,
|
|
6
8
|
PostgresAlterPolicyRename,
|
|
7
9
|
PostgresAlterTable,
|
|
10
|
+
PostgresCreateIndex,
|
|
8
11
|
PostgresCreatePolicy,
|
|
9
12
|
PostgresCreateSchema,
|
|
10
13
|
PostgresCreateTable,
|
|
11
14
|
PostgresCreateType,
|
|
12
15
|
PostgresDisableRowLevelSecurity,
|
|
16
|
+
PostgresDropIndex,
|
|
13
17
|
PostgresDropPolicy,
|
|
14
18
|
PostgresDropType,
|
|
15
19
|
type RlsPolicyOperation,
|
|
@@ -149,6 +153,49 @@ export function alterPolicyRename(options: {
|
|
|
149
153
|
return new PostgresAlterPolicyRename(options);
|
|
150
154
|
}
|
|
151
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Build a Postgres `CREATE [UNIQUE] INDEX` DDL node. Identifiers (`schema`,
|
|
158
|
+
* `table`, `name`, column elements, `type`, option keys) are quoted by the
|
|
159
|
+
* renderer; the `expression` element list and the `where` predicate are
|
|
160
|
+
* inserted verbatim (opaque SQL). An absent `schema` renders the object
|
|
161
|
+
* names unqualified (the unbound namespace).
|
|
162
|
+
*/
|
|
163
|
+
export function createIndex(options: {
|
|
164
|
+
readonly schema: string | undefined;
|
|
165
|
+
readonly table: string;
|
|
166
|
+
readonly name: string;
|
|
167
|
+
readonly unique: boolean;
|
|
168
|
+
readonly elements: DdlIndexElements;
|
|
169
|
+
readonly type: string | undefined;
|
|
170
|
+
readonly options: Record<string, unknown> | undefined;
|
|
171
|
+
readonly where: string | undefined;
|
|
172
|
+
}): PostgresCreateIndex {
|
|
173
|
+
return new PostgresCreateIndex(options);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Build a Postgres `DROP INDEX` DDL node. The index name (and optional
|
|
178
|
+
* schema) are quoted by the renderer.
|
|
179
|
+
*/
|
|
180
|
+
export function dropIndex(options: {
|
|
181
|
+
readonly schema: string | undefined;
|
|
182
|
+
readonly name: string;
|
|
183
|
+
}): PostgresDropIndex {
|
|
184
|
+
return new PostgresDropIndex(options);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Build a Postgres `ALTER INDEX … RENAME TO` DDL node. Identifiers are
|
|
189
|
+
* quoted by the renderer.
|
|
190
|
+
*/
|
|
191
|
+
export function alterIndexRename(options: {
|
|
192
|
+
readonly schema: string | undefined;
|
|
193
|
+
readonly from: string;
|
|
194
|
+
readonly to: string;
|
|
195
|
+
}): PostgresAlterIndexRename {
|
|
196
|
+
return new PostgresAlterIndexRename(options);
|
|
197
|
+
}
|
|
198
|
+
|
|
152
199
|
/**
|
|
153
200
|
* Build a Postgres `ALTER TABLE … DISABLE ROW LEVEL SECURITY` DDL node.
|
|
154
201
|
* Identifiers (`schema`, `table`) are quoted by the renderer.
|
package/src/core/authoring.ts
CHANGED
|
@@ -22,10 +22,16 @@ import type {
|
|
|
22
22
|
ResolvedPslModelRefs,
|
|
23
23
|
} from '@prisma-next/sql-contract/entity-handle-lowering-hook';
|
|
24
24
|
import type { SqlValueSetDerivingEntityTypeOutput } from '@prisma-next/sql-contract/value-set-derivation-hook';
|
|
25
|
+
import {
|
|
26
|
+
assertWireNamePrefixLength,
|
|
27
|
+
formatWireName,
|
|
28
|
+
normalizeSqlBody,
|
|
29
|
+
} from '@prisma-next/sql-schema-ir/naming';
|
|
25
30
|
import { assertDefined } from '@prisma-next/utils/assertions';
|
|
26
31
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
27
32
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
28
33
|
import { PG_ENUM_CODEC_ID } from './codec-ids';
|
|
34
|
+
import { postgresError } from './errors';
|
|
29
35
|
import { PostgresNativeEnum } from './postgres-native-enum';
|
|
30
36
|
import { PostgresRlsEnablement, type PostgresRlsEnablementInput } from './postgres-rls-enablement';
|
|
31
37
|
import { PostgresRlsPolicy, type RlsPolicyOperation } from './postgres-rls-policy';
|
|
@@ -36,12 +42,7 @@ import {
|
|
|
36
42
|
PostgresRlsPolicySchema,
|
|
37
43
|
PostgresRoleSchema,
|
|
38
44
|
} from './postgres-validators';
|
|
39
|
-
import {
|
|
40
|
-
computeContentHash,
|
|
41
|
-
normalizePredicate,
|
|
42
|
-
POLICY_OPERATION_PREDICATES,
|
|
43
|
-
} from './rls/canonicalize';
|
|
44
|
-
import { formatRlsPolicyWireName } from './rls/wire-name';
|
|
45
|
+
import { computeContentHash, POLICY_OPERATION_PREDICATES } from './rls/canonicalize';
|
|
45
46
|
|
|
46
47
|
/**
|
|
47
48
|
* `pg.enum(<ref>)` registers as an ordinary type constructor whose sole
|
|
@@ -153,10 +154,10 @@ function buildRlsPolicyEntity(input: {
|
|
|
153
154
|
readonly withCheck?: string;
|
|
154
155
|
}): PostgresRlsPolicy {
|
|
155
156
|
const wireHash = computeContentHash({
|
|
156
|
-
...ifDefined('using', input.using !== undefined ?
|
|
157
|
+
...ifDefined('using', input.using !== undefined ? normalizeSqlBody(input.using) : undefined),
|
|
157
158
|
...ifDefined(
|
|
158
159
|
'withCheck',
|
|
159
|
-
input.withCheck !== undefined ?
|
|
160
|
+
input.withCheck !== undefined ? normalizeSqlBody(input.withCheck) : undefined,
|
|
160
161
|
),
|
|
161
162
|
roles: input.roles,
|
|
162
163
|
operation: input.operation,
|
|
@@ -164,7 +165,7 @@ function buildRlsPolicyEntity(input: {
|
|
|
164
165
|
});
|
|
165
166
|
|
|
166
167
|
return new PostgresRlsPolicy({
|
|
167
|
-
name:
|
|
168
|
+
name: formatWireName(input.prefix, wireHash),
|
|
168
169
|
prefix: input.prefix,
|
|
169
170
|
tableName: input.tableName,
|
|
170
171
|
namespaceId: input.namespaceId,
|
|
@@ -683,12 +684,6 @@ export const postgresAuthoringFieldPresets = {
|
|
|
683
684
|
},
|
|
684
685
|
} as const satisfies AuthoringFieldNamespace;
|
|
685
686
|
|
|
686
|
-
/**
|
|
687
|
-
* Postgres identifiers cap at 63 characters and the wire name appends a
|
|
688
|
-
* 9-character `_<8hex>` suffix, so the authored prefix is bounded at 54.
|
|
689
|
-
*/
|
|
690
|
-
const RLS_POLICY_PREFIX_MAX_LENGTH = 54;
|
|
691
|
-
|
|
692
687
|
interface RlsRoleHandleShape {
|
|
693
688
|
readonly entityKind: 'role';
|
|
694
689
|
readonly name: string;
|
|
@@ -723,12 +718,16 @@ function requireLocalTarget(
|
|
|
723
718
|
return { namespaceId: target.namespaceId, tableName: target.tableName };
|
|
724
719
|
}
|
|
725
720
|
if (target !== undefined && target.kind === 'cross-space') {
|
|
726
|
-
throw
|
|
721
|
+
throw postgresError(
|
|
722
|
+
'CONTRACT.POLICY_INVALID',
|
|
727
723
|
`defineContract: ${subject} targets model "${target.modelName ?? target.tableName}", which lives in another contract space. Policies and rlsEnabled entries must target a model declared in this contract.`,
|
|
724
|
+
{ meta: { model: target.modelName ?? target.tableName, reason: 'cross-space-target' } },
|
|
728
725
|
);
|
|
729
726
|
}
|
|
730
|
-
throw
|
|
727
|
+
throw postgresError(
|
|
728
|
+
'CONTRACT.MODEL_UNKNOWN',
|
|
731
729
|
`defineContract: ${subject} targets model "${target?.modelName ?? '<anonymous>'}", which is not in the contract's models. Add the model to \`models\`.`,
|
|
730
|
+
{ meta: { model: target?.modelName ?? '<anonymous>' } },
|
|
732
731
|
);
|
|
733
732
|
}
|
|
734
733
|
|
|
@@ -780,8 +779,10 @@ export function postgresLowerEntityHandles(
|
|
|
780
779
|
'role handles are constructed only by the postgres contract-builder role() constructor, which enforces this shape'
|
|
781
780
|
>(handle);
|
|
782
781
|
if (roles.has(roleHandle.name)) {
|
|
783
|
-
throw
|
|
782
|
+
throw postgresError(
|
|
783
|
+
'CONTRACT.ROLE_INVALID',
|
|
784
784
|
`defineContract: role "${roleHandle.name}" is declared more than once in the entities list.`,
|
|
785
|
+
{ meta: { role: roleHandle.name, reason: 'duplicate' } },
|
|
785
786
|
);
|
|
786
787
|
}
|
|
787
788
|
// Roles are cluster-scoped in Postgres, so they always land in the
|
|
@@ -804,8 +805,10 @@ export function postgresLowerEntityHandles(
|
|
|
804
805
|
break;
|
|
805
806
|
}
|
|
806
807
|
default:
|
|
807
|
-
throw
|
|
808
|
+
throw postgresError(
|
|
809
|
+
'CONTRACT.ENTITY_KIND_INVALID',
|
|
808
810
|
`defineContract: the postgres pack does not lower "${handle.entityKind}" handles from the entities list.`,
|
|
811
|
+
{ meta: { entityKind: handle.entityKind } },
|
|
809
812
|
);
|
|
810
813
|
}
|
|
811
814
|
}
|
|
@@ -815,22 +818,28 @@ export function postgresLowerEntityHandles(
|
|
|
815
818
|
|
|
816
819
|
for (const { handle: policy, refs } of policies) {
|
|
817
820
|
const prefix = policy.name;
|
|
818
|
-
|
|
819
|
-
throw new Error(
|
|
820
|
-
`defineContract: policy prefix "${prefix}" exceeds the ${RLS_POLICY_PREFIX_MAX_LENGTH}-character maximum (Postgres identifiers cap at 63 characters and the wire name appends a 9-character hash suffix).`,
|
|
821
|
-
);
|
|
822
|
-
}
|
|
821
|
+
assertWireNamePrefixLength(prefix, 'defineContract: policy prefix');
|
|
823
822
|
const coordinate = requireLocalTarget(refs, `policy "${prefix}"`);
|
|
824
823
|
if (!enablements.has(coordinateKey(coordinate))) {
|
|
825
824
|
const target = refs['target'];
|
|
826
|
-
throw
|
|
825
|
+
throw postgresError(
|
|
826
|
+
'CONTRACT.POLICY_INVALID',
|
|
827
827
|
`defineContract: policy "${prefix}" targets model "${target?.modelName ?? coordinate.tableName}", whose table is not RLS-enabled. Add rlsEnabled(<model>) to the entities list.`,
|
|
828
|
+
{
|
|
829
|
+
meta: {
|
|
830
|
+
prefix,
|
|
831
|
+
model: target?.modelName ?? coordinate.tableName,
|
|
832
|
+
reason: 'target-not-rls-enabled',
|
|
833
|
+
},
|
|
834
|
+
},
|
|
828
835
|
);
|
|
829
836
|
}
|
|
830
837
|
const prefixKey = `${coordinate.namespaceId} ${prefix}`;
|
|
831
838
|
if (seenPrefixes.has(prefixKey)) {
|
|
832
|
-
throw
|
|
839
|
+
throw postgresError(
|
|
840
|
+
'CONTRACT.POLICY_INVALID',
|
|
833
841
|
`defineContract: policy prefix "${prefix}" is declared more than once in namespace "${coordinate.namespaceId}". Policy prefixes must be unique per namespace.`,
|
|
842
|
+
{ meta: { prefix, namespaceId: coordinate.namespaceId, reason: 'duplicate-prefix' } },
|
|
834
843
|
);
|
|
835
844
|
}
|
|
836
845
|
seenPrefixes.add(prefixKey);
|