@prisma-next/target-postgres 0.14.0-dev.61 → 0.14.0-dev.62
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/dist/{authoring-BLkL4fFG.mjs → authoring-CdIZ0yvD.mjs} +2 -2
- package/dist/{authoring-BLkL4fFG.mjs.map → authoring-CdIZ0yvD.mjs.map} +1 -1
- 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-BpmChlAK.mjs → control-policy-DKp0HKpY.mjs} +180 -45
- package/dist/control-policy-DKp0HKpY.mjs.map +1 -0
- package/dist/control.mjs +21 -25
- package/dist/control.mjs.map +1 -1
- package/dist/{ddl-BovUVFVi.mjs → ddl-gmxHf68z.mjs} +20 -4
- package/dist/{ddl-BovUVFVi.mjs.map → ddl-gmxHf68z.mjs.map} +1 -1
- package/dist/ddl.d.mts +2 -2
- package/dist/ddl.mjs +2 -2
- package/dist/{descriptor-meta-C1T4BXJ8.mjs → descriptor-meta-DEuXJfpR.mjs} +3 -3
- package/dist/{descriptor-meta-C1T4BXJ8.mjs.map → descriptor-meta-DEuXJfpR.mjs.map} +1 -1
- package/dist/{diff-database-schema-RMwnSOnz.mjs → diff-database-schema-D2sjvMw9.mjs} +14 -10
- package/dist/diff-database-schema-D2sjvMw9.mjs.map +1 -0
- package/dist/diff-database-schema.d.mts +1 -1
- package/dist/diff-database-schema.d.mts.map +1 -1
- package/dist/diff-database-schema.mjs +1 -1
- package/dist/issue-planner.d.mts +1 -1
- 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-BtQyZha6.d.mts → nodes-Bc2N3tMy.d.mts} +27 -3
- package/dist/nodes-Bc2N3tMy.d.mts.map +1 -0
- package/dist/{nodes-B815pj_X.mjs → nodes-DJC5B8Id.mjs} +32 -2
- package/dist/nodes-DJC5B8Id.mjs.map +1 -0
- package/dist/{op-factory-call-CI00nS4z.mjs → op-factory-call-C4flTtTG.mjs} +105 -6
- package/dist/op-factory-call-C4flTtTG.mjs.map +1 -0
- package/dist/{op-factory-call-D6_7-7Vc.d.mts → op-factory-call-CSIg5XDu.d.mts} +26 -3
- package/dist/{op-factory-call-D6_7-7Vc.d.mts.map → op-factory-call-CSIg5XDu.d.mts.map} +1 -1
- package/dist/op-factory-call.d.mts +2 -2
- package/dist/op-factory-call.mjs +2 -2
- package/dist/pack.mjs +1 -1
- package/dist/{planner-CAHZN1Ez.mjs → planner-BtEd5LMr.mjs} +32 -64
- package/dist/planner-BtEd5LMr.mjs.map +1 -0
- package/dist/{planner-ddl-builders-B8Nn6nHN.mjs → planner-ddl-builders-BKlmGDJl.mjs} +7 -3
- package/dist/planner-ddl-builders-BKlmGDJl.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-a-tjfKsA.d.mts → planner-produced-postgres-migration-CXue-6M9.d.mts} +2 -2
- package/dist/{planner-produced-postgres-migration-a-tjfKsA.d.mts.map → planner-produced-postgres-migration-CXue-6M9.d.mts.map} +1 -1
- package/dist/{planner-produced-postgres-migration-flkG4Y7n.mjs → planner-produced-postgres-migration-Jat3hg4u.mjs} +2 -2
- package/dist/{planner-produced-postgres-migration-flkG4Y7n.mjs.map → planner-produced-postgres-migration-Jat3hg4u.mjs.map} +1 -1
- package/dist/planner-produced-postgres-migration.d.mts +1 -1
- package/dist/planner-produced-postgres-migration.mjs +1 -1
- package/dist/{planner-sql-checks-C1-Kk3Eu.mjs → planner-sql-checks-Br1c_QsH.mjs} +3 -3
- package/dist/{planner-sql-checks-C1-Kk3Eu.mjs.map → planner-sql-checks-Br1c_QsH.mjs.map} +1 -1
- package/dist/planner-sql-checks.mjs +1 -1
- package/dist/planner.d.mts +2 -2
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +2 -2
- package/dist/{postgres-contract-view-DWLNQbdR.mjs → postgres-contract-view-BAPW0ec1.mjs} +3 -3
- package/dist/{postgres-contract-view-DWLNQbdR.mjs.map → postgres-contract-view-BAPW0ec1.mjs.map} +1 -1
- package/dist/{postgres-database-schema-node-BVdOffjC.d.mts → postgres-database-schema-node-CJv5bfbs.d.mts} +62 -21
- package/dist/postgres-database-schema-node-CJv5bfbs.d.mts.map +1 -0
- package/dist/{postgres-migration-C92swCzd.mjs → postgres-migration-ChLMQRd-.mjs} +19 -3
- package/dist/postgres-migration-ChLMQRd-.mjs.map +1 -0
- package/dist/{postgres-migration-Bzzi0Asr.d.mts → postgres-migration-DkmgGNcv.d.mts} +21 -2
- package/dist/postgres-migration-DkmgGNcv.d.mts.map +1 -0
- package/dist/{postgres-role-schema-node-BmCboEa_.mjs → postgres-role-schema-node-Cm-j0ZGM.mjs} +11 -19
- package/dist/postgres-role-schema-node-Cm-j0ZGM.mjs.map +1 -0
- package/dist/{postgres-schema-COEca21H.mjs → postgres-schema-DWQ0-CPb.mjs} +13 -2
- package/dist/postgres-schema-DWQ0-CPb.mjs.map +1 -0
- package/dist/postgres-schema-DsrtYp2v.d.mts.map +1 -1
- package/dist/{postgres-table-schema-node-9FCtCUz2.mjs → postgres-table-schema-node-D6LvInCe.mjs} +71 -8
- package/dist/postgres-table-schema-node-D6LvInCe.mjs.map +1 -0
- package/dist/{resolve-ddl-schema-ZkKxt2Fm.mjs → resolve-ddl-schema-CAza65Iy.mjs} +2 -2
- package/dist/{resolve-ddl-schema-ZkKxt2Fm.mjs.map → resolve-ddl-schema-CAza65Iy.mjs.map} +1 -1
- package/dist/runtime.mjs +1 -1
- package/dist/schema-ir-annotations.mjs +1 -1
- package/dist/{sql-utils-DcfMz4MQ.mjs → sql-utils-SU4FDvIV.mjs} +12 -3
- package/dist/{sql-utils-DcfMz4MQ.mjs.map → sql-utils-SU4FDvIV.mjs.map} +1 -1
- package/dist/sql-utils.d.mts +8 -1
- package/dist/sql-utils.d.mts.map +1 -1
- package/dist/sql-utils.mjs +2 -2
- package/dist/types.d.mts +2 -2
- package/dist/types.mjs +4 -4
- package/package.json +20 -20
- package/src/contract-free/ddl.ts +27 -0
- package/src/core/ddl/nodes.ts +44 -0
- package/src/core/migrations/contract-to-postgres-database-schema-node.ts +12 -1
- package/src/core/migrations/control-policy.ts +143 -45
- package/src/core/migrations/diff-database-schema.ts +1 -2
- package/src/core/migrations/issue-planner.ts +77 -4
- package/src/core/migrations/op-factory-call.ts +84 -0
- package/src/core/migrations/operations/native-enum-types.ts +62 -0
- package/src/core/migrations/planner-ddl-builders.ts +12 -2
- package/src/core/migrations/planner.ts +25 -62
- package/src/core/migrations/postgres-migration.ts +31 -0
- package/src/core/migrations/verify-postgres-namespaces.ts +0 -1
- package/src/core/postgres-schema.ts +22 -1
- package/src/core/psl-infer/infer-psl-contract.ts +6 -16
- package/src/core/schema-ir/node-storage-coordinate.ts +26 -0
- package/src/core/schema-ir/postgres-namespace-schema-node.ts +18 -27
- package/src/core/schema-ir/postgres-native-enum-schema-node.ts +83 -0
- package/src/core/schema-ir/schema-node-kinds.ts +12 -3
- package/src/core/sql-utils.ts +15 -2
- package/src/exports/ddl.ts +2 -0
- package/src/exports/op-factory-call.ts +2 -0
- package/src/exports/sql-utils.ts +1 -0
- package/src/exports/types.ts +4 -1
- package/dist/control-policy-BpmChlAK.mjs.map +0 -1
- package/dist/diff-database-schema-RMwnSOnz.mjs.map +0 -1
- package/dist/nodes-B815pj_X.mjs.map +0 -1
- package/dist/nodes-BtQyZha6.d.mts.map +0 -1
- package/dist/op-factory-call-CI00nS4z.mjs.map +0 -1
- package/dist/planner-CAHZN1Ez.mjs.map +0 -1
- package/dist/planner-ddl-builders-B8Nn6nHN.mjs.map +0 -1
- package/dist/postgres-database-schema-node-BVdOffjC.d.mts.map +0 -1
- package/dist/postgres-migration-Bzzi0Asr.d.mts.map +0 -1
- package/dist/postgres-migration-C92swCzd.mjs.map +0 -1
- package/dist/postgres-role-schema-node-BmCboEa_.mjs.map +0 -1
- package/dist/postgres-schema-COEca21H.mjs.map +0 -1
- package/dist/postgres-table-schema-node-9FCtCUz2.mjs.map +0 -1
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import type { Contract } from '@prisma-next/contract/types';
|
|
2
|
-
import type {
|
|
1
|
+
import type { Contract, ControlPolicy } from '@prisma-next/contract/types';
|
|
2
|
+
import type {
|
|
3
|
+
ControlPolicySubject,
|
|
4
|
+
SqlPlannerConflict,
|
|
5
|
+
SuppressionRecord,
|
|
6
|
+
} from '@prisma-next/family-sql/control';
|
|
3
7
|
import type { SchemaDiffIssue } from '@prisma-next/framework-components/control';
|
|
4
8
|
import { entityAt, UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
5
9
|
import type { SqlStorage, StorageTable } from '@prisma-next/sql-contract/types';
|
|
@@ -7,6 +11,7 @@ import { blindCast } from '@prisma-next/utils/casts';
|
|
|
7
11
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
8
12
|
import { type PostgresRole, ROLE_DEFAULT_CONTROL_POLICY } from '../postgres-role';
|
|
9
13
|
import { isPostgresSchema } from '../postgres-schema';
|
|
14
|
+
import { postgresNodeStorageCoordinate } from '../schema-ir/node-storage-coordinate';
|
|
10
15
|
import type { PostgresNamespaceSchemaNode } from '../schema-ir/postgres-namespace-schema-node';
|
|
11
16
|
import { PostgresTableSchemaNode } from '../schema-ir/postgres-table-schema-node';
|
|
12
17
|
import type { SqlSchemaDiffNode } from '../schema-ir/schema-node-kinds';
|
|
@@ -24,13 +29,16 @@ import type { PostgresOpFactoryCall } from './op-factory-call';
|
|
|
24
29
|
* and is fail-closed. Any call not listed here — including future or
|
|
25
30
|
* extension-contributed factories — is treated as NOT object-creation, so it
|
|
26
31
|
* is suppressed under `tolerated` rather than permissively emitted.
|
|
32
|
+
*
|
|
33
|
+
* Lists the creation factories that actually reach the call-side resolver
|
|
34
|
+
* (`resolvePostgresCallControlPolicySubject`) — today only RLS policy
|
|
35
|
+
* creation, from `planPostgresSchemaDiff`. Every other whole-object create
|
|
36
|
+
* (table, schema, native enum, and RLS enablement) is constructed inside
|
|
37
|
+
* `planIssues` and graded on the node side by
|
|
38
|
+
* `resolvePostgresNodeIssueCreationFactoryName`, so none of them are listed
|
|
39
|
+
* here.
|
|
27
40
|
*/
|
|
28
|
-
const OBJECT_CREATION_FACTORIES: ReadonlySet<string> = new Set<string>([
|
|
29
|
-
'createTable',
|
|
30
|
-
'createSchema',
|
|
31
|
-
'createRlsPolicy',
|
|
32
|
-
'enableRowLevelSecurity',
|
|
33
|
-
]);
|
|
41
|
+
const OBJECT_CREATION_FACTORIES: ReadonlySet<string> = new Set<string>(['createRlsPolicy']);
|
|
34
42
|
|
|
35
43
|
function createsNewTopLevelObject(call: PostgresOpFactoryCall): boolean {
|
|
36
44
|
return OBJECT_CREATION_FACTORIES.has(call.factoryName);
|
|
@@ -41,18 +49,25 @@ function ddlSchemaNameForNamespace(contract: Contract<SqlStorage>, namespaceId:
|
|
|
41
49
|
return isPostgresSchema(namespace) ? namespace.ddlSchemaName(contract.storage) : namespaceId;
|
|
42
50
|
}
|
|
43
51
|
|
|
44
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the namespace a declared storage entity lives in by walking every
|
|
54
|
+
* namespace for one whose `entries` map actually contains the coordinate —
|
|
55
|
+
* a table by its name, a native enum by its physical type name (see
|
|
56
|
+
* {@link postgresNodeStorageCoordinate}). When `ddlSchemaName` is given, the
|
|
57
|
+
* match must also land in that DDL schema (disambiguates same-named entities
|
|
58
|
+
* declared under different namespaces); when omitted, the first namespace
|
|
59
|
+
* that declares the entity wins. Falls back to {@link UNBOUND_NAMESPACE_ID}
|
|
60
|
+
* when no namespace declares it — e.g. an extra/dropped entity the contract
|
|
61
|
+
* doesn't claim at all.
|
|
62
|
+
*/
|
|
63
|
+
function resolveNamespaceIdForEntity(
|
|
45
64
|
contract: Contract<SqlStorage>,
|
|
46
|
-
|
|
65
|
+
coordinate: { readonly entityKind: string; readonly entityName: string },
|
|
47
66
|
ddlSchemaName: string | undefined,
|
|
48
67
|
): string {
|
|
49
68
|
for (const namespaceId of Object.keys(contract.storage.namespaces)) {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
entityKind: 'table',
|
|
53
|
-
entityName: tableName,
|
|
54
|
-
});
|
|
55
|
-
if (!table) continue;
|
|
69
|
+
const entity = entityAt(contract.storage, { namespaceId, ...coordinate });
|
|
70
|
+
if (!entity) continue;
|
|
56
71
|
if (
|
|
57
72
|
ddlSchemaName === undefined ||
|
|
58
73
|
ddlSchemaNameForNamespace(contract, namespaceId) === ddlSchemaName
|
|
@@ -63,6 +78,18 @@ function resolveNamespaceIdForTable(
|
|
|
63
78
|
return UNBOUND_NAMESPACE_ID;
|
|
64
79
|
}
|
|
65
80
|
|
|
81
|
+
function resolveNamespaceIdForTable(
|
|
82
|
+
contract: Contract<SqlStorage>,
|
|
83
|
+
tableName: string,
|
|
84
|
+
ddlSchemaName: string | undefined,
|
|
85
|
+
): string {
|
|
86
|
+
return resolveNamespaceIdForEntity(
|
|
87
|
+
contract,
|
|
88
|
+
{ entityKind: 'table', entityName: tableName },
|
|
89
|
+
ddlSchemaName,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
66
93
|
export function resolveNamespaceIdForDdlSchema(
|
|
67
94
|
contract: Contract<SqlStorage>,
|
|
68
95
|
ddlSchemaName: string,
|
|
@@ -93,16 +120,60 @@ function postgresCallFields(call: PostgresOpFactoryCall): PostgresCallFields {
|
|
|
93
120
|
};
|
|
94
121
|
}
|
|
95
122
|
|
|
96
|
-
|
|
97
|
-
factoryName: string,
|
|
123
|
+
function formatSuppressionSubjectLabel(
|
|
98
124
|
subject: ControlPolicySubject | undefined,
|
|
99
125
|
contract: Contract<SqlStorage>,
|
|
100
126
|
): string {
|
|
101
|
-
if (subject
|
|
102
|
-
|
|
103
|
-
|
|
127
|
+
if (subject === undefined) return 'unknown';
|
|
128
|
+
const ddlSchema = ddlSchemaNameForNamespace(contract, subject.namespaceId);
|
|
129
|
+
if (subject.entityKind !== undefined && subject.entityName !== undefined) {
|
|
130
|
+
return `${subject.entityKind} "${ddlSchema}.${subject.entityName}"`;
|
|
104
131
|
}
|
|
105
|
-
return
|
|
132
|
+
return `namespace "${ddlSchema}"`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function postgresSuppressionSummary(
|
|
136
|
+
subjectLabel: string,
|
|
137
|
+
subject: ControlPolicySubject | undefined,
|
|
138
|
+
policy: string,
|
|
139
|
+
): string {
|
|
140
|
+
const namespace = subject?.namespaceId ?? 'unknown';
|
|
141
|
+
const declared = subject?.explicitNodeControlPolicy;
|
|
142
|
+
if (policy === 'external' && declared === 'managed') {
|
|
143
|
+
return `control policy suppressed: ${subjectLabel} — namespace '${namespace}' has effective control 'external' but declared 'managed'`;
|
|
144
|
+
}
|
|
145
|
+
const declaredSuffix = declared ? ` but declared '${declared}'` : '';
|
|
146
|
+
return `control policy suppressed: ${subjectLabel} — namespace '${namespace}' has effective control '${policy}'${declaredSuffix}`;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Render one family {@link SuppressionRecord} into a target `SqlPlannerConflict`.
|
|
151
|
+
* The family decides *that* a subject is suppressed and hands over the raw
|
|
152
|
+
* coordinate + policy; the label, message, and location are rendered here,
|
|
153
|
+
* driven entirely by the subject's own `(entityKind, entityName)` coordinate
|
|
154
|
+
* — no target-owned table-vs-enum vocabulary.
|
|
155
|
+
*/
|
|
156
|
+
export function renderPostgresSuppression(
|
|
157
|
+
record: SuppressionRecord,
|
|
158
|
+
contract: Contract<SqlStorage>,
|
|
159
|
+
): SqlPlannerConflict {
|
|
160
|
+
const subject = record.subject;
|
|
161
|
+
const subjectLabel = formatSuppressionSubjectLabel(subject, contract);
|
|
162
|
+
return {
|
|
163
|
+
kind: 'controlPolicySuppressedCall',
|
|
164
|
+
summary: postgresSuppressionSummary(subjectLabel, subject, record.policy),
|
|
165
|
+
location: {
|
|
166
|
+
...ifDefined('namespaceId', subject?.namespaceId),
|
|
167
|
+
...ifDefined('entityKind', subject?.entityKind),
|
|
168
|
+
...ifDefined('entityName', subject?.entityName),
|
|
169
|
+
...ifDefined('column', subject?.column),
|
|
170
|
+
},
|
|
171
|
+
meta: {
|
|
172
|
+
controlPolicy: record.policy,
|
|
173
|
+
...ifDefined('factoryName', record.factoryName),
|
|
174
|
+
...ifDefined('declaredControlPolicy', subject?.explicitNodeControlPolicy),
|
|
175
|
+
},
|
|
176
|
+
};
|
|
106
177
|
}
|
|
107
178
|
|
|
108
179
|
export function resolvePostgresCallControlPolicySubject(
|
|
@@ -132,9 +203,10 @@ export function resolvePostgresCallControlPolicySubject(
|
|
|
132
203
|
})?.control;
|
|
133
204
|
return {
|
|
134
205
|
namespaceId,
|
|
135
|
-
|
|
136
|
-
|
|
206
|
+
entityKind: 'table',
|
|
207
|
+
entityName: callFields.tableName,
|
|
137
208
|
...ifDefined('column', callFields.columnName),
|
|
209
|
+
...ifDefined('explicitNodeControlPolicy', tableControlPolicy),
|
|
138
210
|
createsNewObject,
|
|
139
211
|
};
|
|
140
212
|
}
|
|
@@ -150,10 +222,10 @@ export function resolvePostgresCallControlPolicySubject(
|
|
|
150
222
|
}
|
|
151
223
|
|
|
152
224
|
/**
|
|
153
|
-
* Node kinds
|
|
154
|
-
* object
|
|
155
|
-
*
|
|
156
|
-
*
|
|
225
|
+
* Node kinds whose *absence* is the creation of a whole, top-level Postgres
|
|
226
|
+
* object: a namespace, a table, or a native enum. Used by
|
|
227
|
+
* {@link resolvePostgresNodeIssueCreationFactoryName} to decide whether a
|
|
228
|
+
* `tolerated` subject permits the issue to flow into the planner
|
|
157
229
|
* (create-if-absent) and to seed the suppressed-subject warning's
|
|
158
230
|
* `factoryName` when the planner is skipped. RLS policy creation is not
|
|
159
231
|
* listed here — policy issues never reach this issue-based partition (they
|
|
@@ -163,13 +235,15 @@ export function resolvePostgresCallControlPolicySubject(
|
|
|
163
235
|
const POSTGRES_NODE_CREATION_FACTORY: Readonly<Record<string, string>> = Object.freeze({
|
|
164
236
|
[PostgresSchemaNodeKind.namespace]: 'createSchema',
|
|
165
237
|
[PostgresSchemaNodeKind.table]: 'createTable',
|
|
238
|
+
[PostgresSchemaNodeKind.nativeEnum]: 'createNativeEnumType',
|
|
166
239
|
});
|
|
167
240
|
|
|
168
241
|
/**
|
|
169
242
|
* A table `not-equal` issue whose `rlsEnabled` flips OFF→ON (expected on,
|
|
170
|
-
* actual off) is enablement toward `ENABLE ROW LEVEL SECURITY
|
|
171
|
-
* creation-class
|
|
172
|
-
*
|
|
243
|
+
* actual off) is enablement toward `ENABLE ROW LEVEL SECURITY`. It is
|
|
244
|
+
* creation-class on the node side because `isEnablementCreationIssue` and
|
|
245
|
+
* {@link resolvePostgresNodeIssueCreationFactoryName} treat this OFF→ON delta
|
|
246
|
+
* as a creation: enabling RLS establishes the fail-closed guard the declared
|
|
173
247
|
* policy set attaches to, the same grant `tolerated` extends to creating the
|
|
174
248
|
* policies themselves. The opposite direction (`DISABLE`) is a modification
|
|
175
249
|
* and stays managed-only. Keying on the actual delta (not just the expected
|
|
@@ -207,15 +281,25 @@ export function resolvePostgresNodeIssueCreationFactoryName(
|
|
|
207
281
|
* {@link SchemaDiffIssue}. Mirrors the resolution
|
|
208
282
|
* `resolvePostgresCallControlPolicySubject` performs for a generated DDL
|
|
209
283
|
* call, but works *off the issue* — so the planner can partition issues by
|
|
210
|
-
* effective policy before the diff engine runs.
|
|
211
|
-
*
|
|
212
|
-
*
|
|
284
|
+
* effective policy before the diff engine runs.
|
|
285
|
+
*
|
|
286
|
+
* A whole storage entity (table or native enum) resolves generically off
|
|
287
|
+
* {@link postgresNodeStorageCoordinate} — the node self-describes its
|
|
288
|
+
* `(entityKind, entityName)`, so table and enum issues share one code path
|
|
289
|
+
* with no per-kind branch. A sub-entity issue (a column/constraint drift
|
|
290
|
+
* inside an owned table) carries no such coordinate on its own node, so it
|
|
291
|
+
* falls back to reading the enclosing table name off the issue path.
|
|
292
|
+
*
|
|
293
|
+
* `createsNewObject` is delegated to {@link resolvePostgresNodeIssueCreationFactoryName}
|
|
294
|
+
* in every branch below — that function already encodes exactly which
|
|
295
|
+
* node-kind/reason combinations describe a brand-new top-level object
|
|
296
|
+
* (a `not-found` table/enum, or an RLS-enablement `not-equal`).
|
|
213
297
|
*
|
|
214
|
-
* A `not-expected`
|
|
215
|
-
*
|
|
216
|
-
* `namespaceId` falls back to {@link UNBOUND_NAMESPACE_ID} via
|
|
217
|
-
* `
|
|
218
|
-
*
|
|
298
|
+
* A `not-expected` (extra/dropped) entity carries no contract namespace
|
|
299
|
+
* coordinate — the live object isn't claimed by any contract namespace — so
|
|
300
|
+
* the subject's `namespaceId` falls back to {@link UNBOUND_NAMESPACE_ID} via
|
|
301
|
+
* `resolveNamespaceIdForEntity`'s own fallback, matching how the call-side
|
|
302
|
+
* resolver treats the `DropTableCall`/`DropNativeEnumTypeCall` it produces.
|
|
219
303
|
*
|
|
220
304
|
* A role node resolves against the `PostgresRole` contract entity at that
|
|
221
305
|
* role's name (roles are root-level diff subjects, so the name is the
|
|
@@ -263,6 +347,22 @@ export function resolvePostgresNodeIssueControlPolicySubject(
|
|
|
263
347
|
};
|
|
264
348
|
}
|
|
265
349
|
|
|
350
|
+
const coordinate = postgresNodeStorageCoordinate(node);
|
|
351
|
+
if (coordinate !== undefined) {
|
|
352
|
+
const namespaceId = resolveNamespaceIdForEntity(contract, coordinate, issue.path[1]);
|
|
353
|
+
const entityControl = entityAt<{ readonly control?: ControlPolicy }>(contract.storage, {
|
|
354
|
+
namespaceId,
|
|
355
|
+
...coordinate,
|
|
356
|
+
})?.control;
|
|
357
|
+
return {
|
|
358
|
+
namespaceId,
|
|
359
|
+
...coordinate,
|
|
360
|
+
...ifDefined('column', issueColumnName(issue)),
|
|
361
|
+
...ifDefined('explicitNodeControlPolicy', entityControl),
|
|
362
|
+
createsNewObject: resolvePostgresNodeIssueCreationFactoryName(issue) !== undefined,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
266
366
|
const tableName = issue.path[2];
|
|
267
367
|
if (tableName === undefined) return undefined;
|
|
268
368
|
const ddlSchemaName = issue.path[1];
|
|
@@ -272,15 +372,13 @@ export function resolvePostgresNodeIssueControlPolicySubject(
|
|
|
272
372
|
entityKind: 'table',
|
|
273
373
|
entityName: tableName,
|
|
274
374
|
});
|
|
275
|
-
const createsNewObject =
|
|
276
|
-
(node.nodeKind === PostgresSchemaNodeKind.table && issue.reason === 'not-found') ||
|
|
277
|
-
isEnablementCreationIssue(issue);
|
|
278
375
|
|
|
279
376
|
return {
|
|
280
377
|
namespaceId,
|
|
281
|
-
|
|
282
|
-
|
|
378
|
+
entityKind: 'table',
|
|
379
|
+
entityName: tableName,
|
|
283
380
|
...ifDefined('column', issueColumnName(issue)),
|
|
284
|
-
|
|
381
|
+
...ifDefined('explicitNodeControlPolicy', table?.control),
|
|
382
|
+
createsNewObject: resolvePostgresNodeIssueCreationFactoryName(issue) !== undefined,
|
|
285
383
|
};
|
|
286
384
|
}
|
|
@@ -118,7 +118,7 @@ function resolveControlPolicy(
|
|
|
118
118
|
* shrink because a namespace declares no tables). The codec `verifyType`
|
|
119
119
|
* hooks run once per contract namespace with tables against that
|
|
120
120
|
* namespace's paired actual node (the hooks read namespace-scoped state
|
|
121
|
-
* such as `
|
|
121
|
+
* such as `nativeEnums`).
|
|
122
122
|
*/
|
|
123
123
|
export function diffPostgresSchema(input: {
|
|
124
124
|
readonly contract: Contract<SqlStorage>;
|
|
@@ -181,7 +181,6 @@ function padActualNamespaces(
|
|
|
181
181
|
namespaces[schemaName] = new PostgresNamespaceSchemaNode({
|
|
182
182
|
schemaName,
|
|
183
183
|
tables: {},
|
|
184
|
-
nativeEnumTypeNames: [],
|
|
185
184
|
});
|
|
186
185
|
padded = true;
|
|
187
186
|
}
|
|
@@ -45,6 +45,7 @@ import { ifDefined } from '@prisma-next/utils/defined';
|
|
|
45
45
|
import type { Result } from '@prisma-next/utils/result';
|
|
46
46
|
import { notOk, ok } from '@prisma-next/utils/result';
|
|
47
47
|
import type { PostgresNamespaceSchemaNode } from '../schema-ir/postgres-namespace-schema-node';
|
|
48
|
+
import type { PostgresNativeEnumSchemaNode } from '../schema-ir/postgres-native-enum-schema-node';
|
|
48
49
|
import type { PostgresTableSchemaNode } from '../schema-ir/postgres-table-schema-node';
|
|
49
50
|
import { PostgresSchemaNodeKind } from '../schema-ir/schema-node-kinds';
|
|
50
51
|
import { quoteIdentifier } from '../sql-utils';
|
|
@@ -61,6 +62,7 @@ import {
|
|
|
61
62
|
AddUniqueCall,
|
|
62
63
|
AlterColumnTypeCall,
|
|
63
64
|
CreateIndexCall,
|
|
65
|
+
CreateNativeEnumTypeCall,
|
|
64
66
|
CreateSchemaCall,
|
|
65
67
|
CreateTableCall,
|
|
66
68
|
DisableRowLevelSecurityCall,
|
|
@@ -69,6 +71,7 @@ import {
|
|
|
69
71
|
DropConstraintCall,
|
|
70
72
|
DropDefaultCall,
|
|
71
73
|
DropIndexCall,
|
|
74
|
+
DropNativeEnumTypeCall,
|
|
72
75
|
DropNotNullCall,
|
|
73
76
|
DropTableCall,
|
|
74
77
|
EnableRowLevelSecurityCall,
|
|
@@ -152,8 +155,10 @@ function classifyCall(call: PostgresOpFactoryCall): CallCategory {
|
|
|
152
155
|
switch (call.factoryName) {
|
|
153
156
|
case 'createExtension':
|
|
154
157
|
case 'createSchema':
|
|
158
|
+
case 'createNativeEnumType':
|
|
155
159
|
return 'dep';
|
|
156
160
|
case 'dropTable':
|
|
161
|
+
case 'dropNativeEnumType':
|
|
157
162
|
case 'dropColumn':
|
|
158
163
|
case 'dropConstraint':
|
|
159
164
|
case 'dropCheckConstraint':
|
|
@@ -250,17 +255,22 @@ function locationForCall(call: PostgresOpFactoryCall): SqlPlannerConflict['locat
|
|
|
250
255
|
typeName?: string;
|
|
251
256
|
};
|
|
252
257
|
const location: {
|
|
253
|
-
|
|
258
|
+
entityKind?: string;
|
|
259
|
+
entityName?: string;
|
|
254
260
|
column?: string;
|
|
255
261
|
index?: string;
|
|
256
262
|
constraint?: string;
|
|
257
|
-
type?: string;
|
|
258
263
|
} = {};
|
|
259
|
-
if (anyCall.tableName)
|
|
264
|
+
if (anyCall.tableName) {
|
|
265
|
+
location.entityKind = 'table';
|
|
266
|
+
location.entityName = anyCall.tableName;
|
|
267
|
+
} else if (anyCall.typeName) {
|
|
268
|
+
location.entityKind = 'native_enum';
|
|
269
|
+
location.entityName = anyCall.typeName;
|
|
270
|
+
}
|
|
260
271
|
if (anyCall.columnName) location.column = anyCall.columnName;
|
|
261
272
|
if (anyCall.indexName) location.index = anyCall.indexName;
|
|
262
273
|
if (anyCall.constraintName) location.constraint = anyCall.constraintName;
|
|
263
|
-
if (anyCall.typeName) location.type = anyCall.typeName;
|
|
264
274
|
return Object.keys(location).length > 0 ? (location as SqlPlannerConflictLocation) : undefined;
|
|
265
275
|
}
|
|
266
276
|
|
|
@@ -374,6 +384,11 @@ export function nodeIssueOrder(issue: SchemaDiffIssue): number {
|
|
|
374
384
|
switch (node.nodeKind) {
|
|
375
385
|
case PostgresSchemaNodeKind.namespace:
|
|
376
386
|
return 1;
|
|
387
|
+
case PostgresSchemaNodeKind.nativeEnum:
|
|
388
|
+
// Creates order right after namespace creates within the 'dep' bucket
|
|
389
|
+
// (CREATE SCHEMA before CREATE TYPE); drops order after table drops
|
|
390
|
+
// within the 'drop' bucket (DROP TYPE only after its dependents left).
|
|
391
|
+
return issue.reason === 'not-expected' ? 17 : 2;
|
|
377
392
|
case RelationalSchemaNodeKind.foreignKey:
|
|
378
393
|
return issue.reason === 'not-expected' ? 10 : 60;
|
|
379
394
|
case RelationalSchemaNodeKind.unique:
|
|
@@ -527,6 +542,60 @@ function nodeConflict(kind: SqlPlannerConflict['kind'], message: string): SqlPla
|
|
|
527
542
|
return issueConflict(kind, message);
|
|
528
543
|
}
|
|
529
544
|
|
|
545
|
+
/**
|
|
546
|
+
* Managed native-enum issue -> op lowering. A missing declared type creates
|
|
547
|
+
* it; an unclaimed live type drops it (ownership-scoped upstream by
|
|
548
|
+
* `retainUnownedExtras`, destructiveness gated by the operation-class
|
|
549
|
+
* policy); a paired member-value mismatch is a NAMED unsupported diagnostic
|
|
550
|
+
* — never a silent no-op and never a drop-and-recreate. Slice B replaces the
|
|
551
|
+
* diagnostic with order-aware `ALTER TYPE ... ADD VALUE` / refusal semantics.
|
|
552
|
+
*/
|
|
553
|
+
function mapNativeEnumNodeIssue(
|
|
554
|
+
issue: SchemaDiffIssue,
|
|
555
|
+
ctx: StrategyContext,
|
|
556
|
+
): Result<readonly PostgresOpFactoryCall[], SqlPlannerConflict> {
|
|
557
|
+
const ddlSchemaName = issueSchemaName(issue);
|
|
558
|
+
if (ddlSchemaName === undefined) {
|
|
559
|
+
return notOk(
|
|
560
|
+
nodeConflict(
|
|
561
|
+
'unsupportedOperation',
|
|
562
|
+
`Enum issue has no schema in its path: ${issue.path.join('/')}`,
|
|
563
|
+
),
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
const schemaName = emissionSchemaName(ctx, ddlSchemaName);
|
|
567
|
+
if (issue.reason === 'not-found') {
|
|
568
|
+
const expected = blindCast<
|
|
569
|
+
PostgresNativeEnumSchemaNode,
|
|
570
|
+
'a not-found native-enum issue always carries the expected PostgresNativeEnumSchemaNode'
|
|
571
|
+
>(issue.expected);
|
|
572
|
+
return ok([new CreateNativeEnumTypeCall(schemaName, expected.typeName, expected.members)]);
|
|
573
|
+
}
|
|
574
|
+
if (issue.reason === 'not-expected') {
|
|
575
|
+
const actual = blindCast<
|
|
576
|
+
PostgresNativeEnumSchemaNode,
|
|
577
|
+
'a not-expected native-enum issue always carries the actual PostgresNativeEnumSchemaNode'
|
|
578
|
+
>(issue.actual);
|
|
579
|
+
return ok([new DropNativeEnumTypeCall(schemaName, actual.typeName)]);
|
|
580
|
+
}
|
|
581
|
+
const expected = blindCast<
|
|
582
|
+
PostgresNativeEnumSchemaNode,
|
|
583
|
+
'a not-equal native-enum issue carries both sides; the expected node names the type'
|
|
584
|
+
>(issue.expected);
|
|
585
|
+
const actualMembers = blindCast<
|
|
586
|
+
PostgresNativeEnumSchemaNode,
|
|
587
|
+
'a not-equal native-enum issue carries both sides'
|
|
588
|
+
>(issue.actual).members;
|
|
589
|
+
return notOk(
|
|
590
|
+
nodeConflict(
|
|
591
|
+
'unsupportedOperation',
|
|
592
|
+
`Native enum type "${ddlSchemaName}"."${expected.typeName}" member values changed ` +
|
|
593
|
+
`(contract declares [${expected.members.join(', ')}], database has [${actualMembers.join(', ')}]); ` +
|
|
594
|
+
'enum value changes are not auto-migrated yet. Author the change manually with `migration new`.',
|
|
595
|
+
),
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
|
|
530
599
|
function mapTableNodeIssue(
|
|
531
600
|
issue: SchemaDiffIssue,
|
|
532
601
|
schemaName: string,
|
|
@@ -814,6 +883,10 @@ export function mapNodeIssueToCall(
|
|
|
814
883
|
return ok([new CreateSchemaCall(namespace.schemaName)]);
|
|
815
884
|
}
|
|
816
885
|
|
|
886
|
+
if (node.nodeKind === PostgresSchemaNodeKind.nativeEnum) {
|
|
887
|
+
return mapNativeEnumNodeIssue(issue, ctx);
|
|
888
|
+
}
|
|
889
|
+
|
|
817
890
|
const ddlSchemaName = issueSchemaName(issue);
|
|
818
891
|
const tableName = issueTableName(issue);
|
|
819
892
|
if (ddlSchemaName === undefined || tableName === undefined) {
|
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
} from './operations/constraints';
|
|
64
64
|
import { createExtension } from './operations/dependencies';
|
|
65
65
|
import { createIndex, dropIndex } from './operations/indexes';
|
|
66
|
+
import { createNativeEnumType, dropNativeEnumType } from './operations/native-enum-types';
|
|
66
67
|
import {
|
|
67
68
|
createRlsPolicy,
|
|
68
69
|
disableRowLevelSecurity,
|
|
@@ -1318,6 +1319,87 @@ export class CreateSchemaCall extends PostgresOpFactoryCallNode {
|
|
|
1318
1319
|
}
|
|
1319
1320
|
}
|
|
1320
1321
|
|
|
1322
|
+
// ============================================================================
|
|
1323
|
+
// Native enum types
|
|
1324
|
+
// ============================================================================
|
|
1325
|
+
|
|
1326
|
+
export class CreateNativeEnumTypeCall extends PostgresOpFactoryCallNode {
|
|
1327
|
+
readonly factoryName = 'createNativeEnumType' as const;
|
|
1328
|
+
readonly operationClass = 'additive' as const;
|
|
1329
|
+
readonly schemaName: string;
|
|
1330
|
+
readonly typeName: string;
|
|
1331
|
+
readonly members: readonly string[];
|
|
1332
|
+
readonly label: string;
|
|
1333
|
+
|
|
1334
|
+
constructor(schemaName: string, typeName: string, members: readonly string[]) {
|
|
1335
|
+
super();
|
|
1336
|
+
this.schemaName = schemaName;
|
|
1337
|
+
this.typeName = typeName;
|
|
1338
|
+
this.members = Object.freeze([...members]);
|
|
1339
|
+
this.label = `Create enum type "${typeName}"`;
|
|
1340
|
+
this.freeze();
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
1344
|
+
if (lowerer === undefined) {
|
|
1345
|
+
throw new Error(
|
|
1346
|
+
`CreateNativeEnumTypeCall.toOp: a DDL lowerer is required on the Postgres planner path (type "${this.typeName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
return createNativeEnumType(this.schemaName, this.typeName, this.members, lowerer);
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
renderTypeScript(): string {
|
|
1353
|
+
const opts = [
|
|
1354
|
+
`schema: ${jsonToTsSource(this.schemaName)}`,
|
|
1355
|
+
`typeName: ${jsonToTsSource(this.typeName)}`,
|
|
1356
|
+
`members: ${jsonToTsSource(this.members)}`,
|
|
1357
|
+
];
|
|
1358
|
+
return `this.createNativeEnumType({ ${opts.join(', ')} })`;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
override importRequirements(): readonly ImportRequirement[] {
|
|
1362
|
+
return [];
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
export class DropNativeEnumTypeCall extends PostgresOpFactoryCallNode {
|
|
1367
|
+
readonly factoryName = 'dropNativeEnumType' as const;
|
|
1368
|
+
readonly operationClass = 'destructive' as const;
|
|
1369
|
+
readonly schemaName: string;
|
|
1370
|
+
readonly typeName: string;
|
|
1371
|
+
readonly label: string;
|
|
1372
|
+
|
|
1373
|
+
constructor(schemaName: string, typeName: string) {
|
|
1374
|
+
super();
|
|
1375
|
+
this.schemaName = schemaName;
|
|
1376
|
+
this.typeName = typeName;
|
|
1377
|
+
this.label = `Drop enum type "${typeName}"`;
|
|
1378
|
+
this.freeze();
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
async toOp(lowerer?: ExecuteRequestLowerer): Promise<Op> {
|
|
1382
|
+
if (lowerer === undefined) {
|
|
1383
|
+
throw new Error(
|
|
1384
|
+
`DropNativeEnumTypeCall.toOp: a DDL lowerer is required on the Postgres planner path (type "${this.typeName}"). Pass the control adapter to createPostgresMigrationPlanner.`,
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
return dropNativeEnumType(this.schemaName, this.typeName, lowerer);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
renderTypeScript(): string {
|
|
1391
|
+
const opts = [
|
|
1392
|
+
`schema: ${jsonToTsSource(this.schemaName)}`,
|
|
1393
|
+
`typeName: ${jsonToTsSource(this.typeName)}`,
|
|
1394
|
+
];
|
|
1395
|
+
return `this.dropNativeEnumType({ ${opts.join(', ')} })`;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
override importRequirements(): readonly ImportRequirement[] {
|
|
1399
|
+
return [];
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1321
1403
|
// ============================================================================
|
|
1322
1404
|
// Data transform
|
|
1323
1405
|
// ============================================================================
|
|
@@ -1562,6 +1644,8 @@ export type PostgresOpFactoryCall =
|
|
|
1562
1644
|
| RawSqlCall
|
|
1563
1645
|
| CreateExtensionCall
|
|
1564
1646
|
| CreateSchemaCall
|
|
1647
|
+
| CreateNativeEnumTypeCall
|
|
1648
|
+
| DropNativeEnumTypeCall
|
|
1565
1649
|
| CreatePostgresRlsPolicyCall
|
|
1566
1650
|
| DropPostgresRlsPolicyCall
|
|
1567
1651
|
| EnableRowLevelSecurityCall
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ExecuteRequestLowerer } from '@prisma-next/family-sql/control-adapter';
|
|
2
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
3
|
+
import { createType, dropType } from '../../../contract-free/ddl';
|
|
4
|
+
import { validateEnumValueLength } from '../../sql-utils';
|
|
5
|
+
import { boundSchema } from '../bound-schema';
|
|
6
|
+
import { type Op, step, targetDetails } from './shared';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* `CREATE TYPE <qualified> AS ENUM (…)` for a managed native enum, built as a
|
|
10
|
+
* typed DDL node and lowered by the control adapter (the type name is quoted,
|
|
11
|
+
* member values render as escaped string literals in declaration order —
|
|
12
|
+
* Postgres enum sort order is semantic). `boundSchema` maps the unbound sentinel
|
|
13
|
+
* to an absent schema so the type name renders unqualified and the connection's
|
|
14
|
+
* `search_path` resolves the schema at runtime.
|
|
15
|
+
*/
|
|
16
|
+
export async function createNativeEnumType(
|
|
17
|
+
schemaName: string,
|
|
18
|
+
typeName: string,
|
|
19
|
+
members: readonly string[],
|
|
20
|
+
lowerer: ExecuteRequestLowerer,
|
|
21
|
+
): Promise<Op> {
|
|
22
|
+
for (const member of members) {
|
|
23
|
+
validateEnumValueLength(member, typeName);
|
|
24
|
+
}
|
|
25
|
+
const ddlNode = createType({
|
|
26
|
+
...ifDefined('schema', boundSchema(schemaName)),
|
|
27
|
+
name: typeName,
|
|
28
|
+
values: members,
|
|
29
|
+
});
|
|
30
|
+
const statement = await lowerer.lowerToExecuteRequest(ddlNode);
|
|
31
|
+
return {
|
|
32
|
+
id: `createNativeEnumType.${typeName}`,
|
|
33
|
+
label: `Create enum type "${typeName}"`,
|
|
34
|
+
operationClass: 'additive',
|
|
35
|
+
target: targetDetails('type', typeName, schemaName),
|
|
36
|
+
precheck: [],
|
|
37
|
+
execute: [step(`create enum type "${typeName}"`, statement.sql, statement.params)],
|
|
38
|
+
postcheck: [],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** `DROP TYPE <qualified>` for an unclaimed managed native enum, via typed DDL. */
|
|
43
|
+
export async function dropNativeEnumType(
|
|
44
|
+
schemaName: string,
|
|
45
|
+
typeName: string,
|
|
46
|
+
lowerer: ExecuteRequestLowerer,
|
|
47
|
+
): Promise<Op> {
|
|
48
|
+
const ddlNode = dropType({
|
|
49
|
+
...ifDefined('schema', boundSchema(schemaName)),
|
|
50
|
+
name: typeName,
|
|
51
|
+
});
|
|
52
|
+
const statement = await lowerer.lowerToExecuteRequest(ddlNode);
|
|
53
|
+
return {
|
|
54
|
+
id: `dropNativeEnumType.${typeName}`,
|
|
55
|
+
label: `Drop enum type "${typeName}"`,
|
|
56
|
+
operationClass: 'destructive',
|
|
57
|
+
target: targetDetails('type', typeName, schemaName),
|
|
58
|
+
precheck: [],
|
|
59
|
+
execute: [step(`drop enum type "${typeName}"`, statement.sql, statement.params)],
|
|
60
|
+
postcheck: [],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CodecControlHooks } from '@prisma-next/family-sql/control';
|
|
2
2
|
import type { StorageColumn, StorageTypeInstance } from '@prisma-next/sql-contract/types';
|
|
3
3
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
4
|
-
import { escapeLiteral, quoteIdentifier } from '../sql-utils';
|
|
4
|
+
import { escapeLiteral, quoteIdentifier, quoteQualifiedName } from '../sql-utils';
|
|
5
5
|
import type { PostgresColumnDefault } from '../types';
|
|
6
6
|
import { resolveColumnTypeMetadata } from './planner-type-resolution';
|
|
7
7
|
|
|
@@ -65,13 +65,23 @@ export function buildColumnTypeSql(
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
// A column whose codec supplied a `typeParams.typeName` references a named
|
|
69
|
+
// database type (e.g. a native enum), not a parameterized builtin: render it
|
|
70
|
+
// as its quoted, schema-qualified type-name identifier. DDL-render only — the
|
|
71
|
+
// verify comparison value (`resolvedNativeType`) stays the bare name the
|
|
72
|
+
// family expander produces, matching introspection.
|
|
73
|
+
if (typeof resolved.typeParams?.['typeName'] === 'string') {
|
|
74
|
+
const quoted = quoteQualifiedName(resolved.nativeType);
|
|
75
|
+
return column.many ? `${quoted}[]` : quoted;
|
|
76
|
+
}
|
|
77
|
+
|
|
68
78
|
const expanded = expandParameterizedTypeSql(resolved, codecHooks);
|
|
69
79
|
if (expanded !== null) {
|
|
70
80
|
return column.many ? `${expanded}[]` : expanded;
|
|
71
81
|
}
|
|
72
82
|
|
|
73
83
|
if (column.typeRef) {
|
|
74
|
-
const base =
|
|
84
|
+
const base = quoteQualifiedName(resolved.nativeType);
|
|
75
85
|
return column.many ? `${base}[]` : base;
|
|
76
86
|
}
|
|
77
87
|
|