@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
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { JsonValue } from '@prisma-next/contract/types';
|
|
10
|
+
import { postgresError } from './errors';
|
|
10
11
|
|
|
11
12
|
export function renderLength(
|
|
12
13
|
typeName: string,
|
|
@@ -17,8 +18,10 @@ export function renderLength(
|
|
|
17
18
|
return undefined;
|
|
18
19
|
}
|
|
19
20
|
if (typeof length !== 'number' || !Number.isFinite(length) || !Number.isInteger(length)) {
|
|
20
|
-
throw
|
|
21
|
+
throw postgresError(
|
|
22
|
+
'RUNTIME.TYPE_PARAMS_INVALID',
|
|
21
23
|
`renderOutputType: expected integer "length" in typeParams for ${typeName}, got ${String(length)}`,
|
|
24
|
+
{ meta: { nativeType: typeName, param: 'length', received: String(length) } },
|
|
22
25
|
);
|
|
23
26
|
}
|
|
24
27
|
return `${typeName}<${length}>`;
|
|
@@ -34,8 +37,10 @@ export function renderPrecision(typeName: string, typeParams: Record<string, unk
|
|
|
34
37
|
!Number.isFinite(precision) ||
|
|
35
38
|
!Number.isInteger(precision)
|
|
36
39
|
) {
|
|
37
|
-
throw
|
|
40
|
+
throw postgresError(
|
|
41
|
+
'RUNTIME.TYPE_PARAMS_INVALID',
|
|
38
42
|
`renderOutputType: expected integer "precision" in typeParams for ${typeName}, got ${String(precision)}`,
|
|
43
|
+
{ meta: { nativeType: typeName, param: 'precision', received: String(precision) } },
|
|
39
44
|
);
|
|
40
45
|
}
|
|
41
46
|
return `${typeName}<${precision}>`;
|
|
@@ -57,15 +62,19 @@ export const pgNumericRenderOutputType = (typeParams: {
|
|
|
57
62
|
!Number.isFinite(precision) ||
|
|
58
63
|
!Number.isInteger(precision)
|
|
59
64
|
) {
|
|
60
|
-
throw
|
|
65
|
+
throw postgresError(
|
|
66
|
+
'RUNTIME.TYPE_PARAMS_INVALID',
|
|
61
67
|
`renderOutputType: expected integer "precision" in typeParams for Numeric, got ${String(precision)}`,
|
|
68
|
+
{ meta: { nativeType: 'Numeric', param: 'precision', received: String(precision) } },
|
|
62
69
|
);
|
|
63
70
|
}
|
|
64
71
|
const scale = typeParams.scale;
|
|
65
72
|
if (scale === undefined) return `Numeric<${precision}>`;
|
|
66
73
|
if (typeof scale !== 'number' || !Number.isFinite(scale) || !Number.isInteger(scale)) {
|
|
67
|
-
throw
|
|
74
|
+
throw postgresError(
|
|
75
|
+
'RUNTIME.TYPE_PARAMS_INVALID',
|
|
68
76
|
`renderOutputType: expected integer "scale" in typeParams for Numeric, got ${String(scale)}`,
|
|
77
|
+
{ meta: { nativeType: 'Numeric', param: 'scale', received: String(scale) } },
|
|
69
78
|
);
|
|
70
79
|
}
|
|
71
80
|
return `Numeric<${precision}, ${scale}>`;
|
|
@@ -78,14 +87,26 @@ const ISO_8601_TIMESTAMPTZ =
|
|
|
78
87
|
export const pgTimestampEncodeJson = (value: Date): JsonValue => value.toISOString().slice(0, -1);
|
|
79
88
|
export const pgTimestampDecodeJson = (json: JsonValue): Date => {
|
|
80
89
|
if (typeof json !== 'string') {
|
|
81
|
-
throw
|
|
90
|
+
throw postgresError(
|
|
91
|
+
'RUNTIME.DECODE_FAILED',
|
|
92
|
+
`Expected ISO date string for pg/timestamp@1, got ${typeof json}`,
|
|
93
|
+
{ meta: { codecId: 'pg/timestamp@1', received: typeof json } },
|
|
94
|
+
);
|
|
82
95
|
}
|
|
83
96
|
if (!ISO_8601_TIMESTAMP.test(json)) {
|
|
84
|
-
throw
|
|
97
|
+
throw postgresError(
|
|
98
|
+
'RUNTIME.DECODE_FAILED',
|
|
99
|
+
`Invalid ISO date string for pg/timestamp@1: ${json}`,
|
|
100
|
+
{ meta: { codecId: 'pg/timestamp@1', received: json } },
|
|
101
|
+
);
|
|
85
102
|
}
|
|
86
103
|
const date = new Date(`${json}Z`);
|
|
87
104
|
if (Number.isNaN(date.getTime())) {
|
|
88
|
-
throw
|
|
105
|
+
throw postgresError(
|
|
106
|
+
'RUNTIME.DECODE_FAILED',
|
|
107
|
+
`Invalid ISO date string for pg/timestamp@1: ${json}`,
|
|
108
|
+
{ meta: { codecId: 'pg/timestamp@1', received: json } },
|
|
109
|
+
);
|
|
89
110
|
}
|
|
90
111
|
return date;
|
|
91
112
|
};
|
|
@@ -94,14 +115,26 @@ export const pgTimestamptzEncodeJson = (value: Date): JsonValue =>
|
|
|
94
115
|
value.toISOString().replace(/Z$/, '+00:00');
|
|
95
116
|
export const pgTimestamptzDecodeJson = (json: JsonValue): Date => {
|
|
96
117
|
if (typeof json !== 'string') {
|
|
97
|
-
throw
|
|
118
|
+
throw postgresError(
|
|
119
|
+
'RUNTIME.DECODE_FAILED',
|
|
120
|
+
`Expected ISO date string for pg/timestamptz@1, got ${typeof json}`,
|
|
121
|
+
{ meta: { codecId: 'pg/timestamptz@1', received: typeof json } },
|
|
122
|
+
);
|
|
98
123
|
}
|
|
99
124
|
if (!ISO_8601_TIMESTAMPTZ.test(json)) {
|
|
100
|
-
throw
|
|
125
|
+
throw postgresError(
|
|
126
|
+
'RUNTIME.DECODE_FAILED',
|
|
127
|
+
`Invalid ISO date string for pg/timestamptz@1: ${json}`,
|
|
128
|
+
{ meta: { codecId: 'pg/timestamptz@1', received: json } },
|
|
129
|
+
);
|
|
101
130
|
}
|
|
102
131
|
const date = new Date(json);
|
|
103
132
|
if (Number.isNaN(date.getTime())) {
|
|
104
|
-
throw
|
|
133
|
+
throw postgresError(
|
|
134
|
+
'RUNTIME.DECODE_FAILED',
|
|
135
|
+
`Invalid ISO date string for pg/timestamptz@1: ${json}`,
|
|
136
|
+
{ meta: { codecId: 'pg/timestamptz@1', received: json } },
|
|
137
|
+
);
|
|
105
138
|
}
|
|
106
139
|
return date;
|
|
107
140
|
};
|
|
@@ -141,11 +174,17 @@ export const pgDateEncodeJson = (value: Date): JsonValue => pgDateEncode(value);
|
|
|
141
174
|
|
|
142
175
|
export const pgDateDecodeJson = (json: JsonValue): Date => {
|
|
143
176
|
if (typeof json !== 'string') {
|
|
144
|
-
throw
|
|
177
|
+
throw postgresError(
|
|
178
|
+
'RUNTIME.DECODE_FAILED',
|
|
179
|
+
`Expected date string for pg/date@1, got ${typeof json}`,
|
|
180
|
+
{ meta: { codecId: 'pg/date@1', received: typeof json } },
|
|
181
|
+
);
|
|
145
182
|
}
|
|
146
183
|
const match = ISO_8601_DATE.exec(json);
|
|
147
184
|
if (!match) {
|
|
148
|
-
throw
|
|
185
|
+
throw postgresError('RUNTIME.DECODE_FAILED', `Invalid date string for pg/date@1: ${json}`, {
|
|
186
|
+
meta: { codecId: 'pg/date@1', received: json },
|
|
187
|
+
});
|
|
149
188
|
}
|
|
150
189
|
const [, yearText, monthText, dayText] = match;
|
|
151
190
|
const year = Number(yearText);
|
|
@@ -153,7 +192,9 @@ export const pgDateDecodeJson = (json: JsonValue): Date => {
|
|
|
153
192
|
const day = Number(dayText);
|
|
154
193
|
const date = new Date(Date.UTC(year, month, day));
|
|
155
194
|
if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month || date.getUTCDate() !== day) {
|
|
156
|
-
throw
|
|
195
|
+
throw postgresError('RUNTIME.DECODE_FAILED', `Invalid date string for pg/date@1: ${json}`, {
|
|
196
|
+
meta: { codecId: 'pg/date@1', received: json },
|
|
197
|
+
});
|
|
157
198
|
}
|
|
158
199
|
return date;
|
|
159
200
|
};
|
|
@@ -168,19 +209,31 @@ export const pgByteaEncodeJson = (value: Uint8Array): JsonValue =>
|
|
|
168
209
|
|
|
169
210
|
export const pgByteaDecodeJson = (value: JsonValue): Uint8Array => {
|
|
170
211
|
if (typeof value !== 'string' || !value.startsWith('\\x')) {
|
|
171
|
-
throw
|
|
212
|
+
throw postgresError(
|
|
213
|
+
'RUNTIME.DECODE_FAILED',
|
|
214
|
+
`Expected Postgres bytea hex text to start with "\\x"`,
|
|
215
|
+
{ meta: { codecId: 'pg/bytea@1' } },
|
|
216
|
+
);
|
|
172
217
|
}
|
|
173
218
|
|
|
174
219
|
const hex = value.slice(2);
|
|
175
220
|
if (hex.length % 2 !== 0) {
|
|
176
|
-
throw
|
|
221
|
+
throw postgresError(
|
|
222
|
+
'RUNTIME.DECODE_FAILED',
|
|
223
|
+
`Invalid Postgres bytea hex text length: ${hex.length}`,
|
|
224
|
+
{ meta: { codecId: 'pg/bytea@1', received: hex.length } },
|
|
225
|
+
);
|
|
177
226
|
}
|
|
178
227
|
|
|
179
228
|
const bytes = new Uint8Array(hex.length / 2);
|
|
180
229
|
for (let offset = 0; offset < hex.length; offset += 2) {
|
|
181
230
|
const pair = hex.slice(offset, offset + 2);
|
|
182
231
|
if (!/^[0-9a-fA-F]{2}$/.test(pair)) {
|
|
183
|
-
throw
|
|
232
|
+
throw postgresError(
|
|
233
|
+
'RUNTIME.DECODE_FAILED',
|
|
234
|
+
`Invalid Postgres bytea hex pair "${pair}" at offset ${offset}`,
|
|
235
|
+
{ meta: { codecId: 'pg/bytea@1', received: pair } },
|
|
236
|
+
);
|
|
184
237
|
}
|
|
185
238
|
bytes[offset / 2] = Number.parseInt(pair, 16);
|
|
186
239
|
}
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
pgBoolDescriptor,
|
|
22
22
|
pgByteaDescriptor,
|
|
23
23
|
pgCharDescriptor,
|
|
24
|
+
pgDateDescriptor,
|
|
24
25
|
pgEnumDescriptor,
|
|
25
26
|
pgFloat4Descriptor,
|
|
26
27
|
pgFloat8Descriptor,
|
|
@@ -63,6 +64,7 @@ export const codecDescriptorMap = {
|
|
|
63
64
|
float4: pgFloat4Descriptor,
|
|
64
65
|
float8: pgFloat8Descriptor,
|
|
65
66
|
numeric: pgNumericDescriptor,
|
|
67
|
+
date: pgDateDescriptor,
|
|
66
68
|
timestamp: pgTimestampDescriptor,
|
|
67
69
|
timestamptz: pgTimestamptzDescriptor,
|
|
68
70
|
time: pgTimeDescriptor,
|
package/src/core/codecs.ts
CHANGED
|
@@ -92,6 +92,7 @@ import {
|
|
|
92
92
|
PG_VARBIT_CODEC_ID,
|
|
93
93
|
PG_VARCHAR_CODEC_ID,
|
|
94
94
|
} from './codec-ids';
|
|
95
|
+
import { postgresError } from './errors';
|
|
95
96
|
import { DEFAULT_NAMESPACE_ID } from './namespace-ids';
|
|
96
97
|
import { PostgresNativeEnum } from './postgres-native-enum';
|
|
97
98
|
|
|
@@ -218,18 +219,26 @@ export class PgEnumCodec extends CodecImpl<
|
|
|
218
219
|
}
|
|
219
220
|
}
|
|
220
221
|
|
|
221
|
-
type PgEnumParams = { readonly typeName: string };
|
|
222
|
+
export type PgEnumParams = { readonly typeName: string };
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Narrows codec `typeParams` to {@link PgEnumParams} — the shape that binds a
|
|
226
|
+
* column to a named database type (a native enum's `CREATE TYPE` name).
|
|
227
|
+
*/
|
|
228
|
+
export function isPgEnumParams(value: unknown): value is PgEnumParams {
|
|
229
|
+
return (
|
|
230
|
+
typeof value === 'object' &&
|
|
231
|
+
value !== null &&
|
|
232
|
+
!Array.isArray(value) &&
|
|
233
|
+
'typeName' in value &&
|
|
234
|
+
typeof value.typeName === 'string'
|
|
235
|
+
);
|
|
236
|
+
}
|
|
222
237
|
|
|
223
238
|
const pgEnumParamsSchema = arktype({
|
|
224
239
|
typeName: 'string',
|
|
225
240
|
}) satisfies StandardSchemaV1<PgEnumParams>;
|
|
226
241
|
|
|
227
|
-
function isJsonObject(
|
|
228
|
-
value: JsonValue | undefined,
|
|
229
|
-
): value is { readonly [key: string]: JsonValue } {
|
|
230
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
242
|
export class PgEnumDescriptor extends CodecDescriptorImpl<PgEnumParams> {
|
|
234
243
|
override readonly codecId = PG_ENUM_CODEC_ID;
|
|
235
244
|
override readonly traits = ['equality', 'order', 'textual'] as const;
|
|
@@ -240,10 +249,8 @@ export class PgEnumDescriptor extends CodecDescriptorImpl<PgEnumParams> {
|
|
|
240
249
|
return renderTsLiteral(value);
|
|
241
250
|
}
|
|
242
251
|
override metaFor(typeParams: JsonValue | undefined): CodecMeta | undefined {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
return typeof typeName === 'string'
|
|
246
|
-
? { db: { sql: { postgres: { nativeType: typeName } } } }
|
|
252
|
+
return isPgEnumParams(typeParams)
|
|
253
|
+
? { db: { sql: { postgres: { nativeType: typeParams.typeName } } } }
|
|
247
254
|
: this.meta;
|
|
248
255
|
}
|
|
249
256
|
override factory(_params: PgEnumParams): (ctx: CodecInstanceContext) => PgEnumCodec {
|
|
@@ -626,13 +633,21 @@ export class PgNumericCodec extends CodecImpl<
|
|
|
626
633
|
encodeJson(value: string): JsonValue {
|
|
627
634
|
const number = Number(value);
|
|
628
635
|
if (!Number.isFinite(number)) {
|
|
629
|
-
throw
|
|
636
|
+
throw postgresError(
|
|
637
|
+
'RUNTIME.ENCODE_FAILED',
|
|
638
|
+
'pg/numeric@1 database JSON value must be a finite number',
|
|
639
|
+
{ meta: { codecId: 'pg/numeric@1', received: value } },
|
|
640
|
+
);
|
|
630
641
|
}
|
|
631
642
|
return number;
|
|
632
643
|
}
|
|
633
644
|
decodeJson(json: JsonValue): string {
|
|
634
645
|
if (typeof json !== 'number') {
|
|
635
|
-
throw
|
|
646
|
+
throw postgresError(
|
|
647
|
+
'RUNTIME.DECODE_FAILED',
|
|
648
|
+
'pg/numeric@1 database JSON value must be a number',
|
|
649
|
+
{ meta: { codecId: 'pg/numeric@1', received: typeof json } },
|
|
650
|
+
);
|
|
636
651
|
}
|
|
637
652
|
return pgNumericDecode(json);
|
|
638
653
|
}
|
|
@@ -1336,7 +1351,7 @@ export const codecDescriptors: readonly AnyCodecDescriptor[] = [
|
|
|
1336
1351
|
pgFloat4Descriptor,
|
|
1337
1352
|
pgFloat8Descriptor,
|
|
1338
1353
|
pgNumericDescriptor,
|
|
1339
|
-
// PSL
|
|
1354
|
+
// PSL `Date` pins this codec by ID rather than activating a second target-type mapping.
|
|
1340
1355
|
pgDateDescriptor,
|
|
1341
1356
|
pgTimestampDescriptor,
|
|
1342
1357
|
pgTimestamptzDescriptor,
|
package/src/core/ddl/nodes.ts
CHANGED
|
@@ -73,6 +73,9 @@ export interface PostgresDdlVisitor<R> {
|
|
|
73
73
|
createPolicy(node: PostgresCreatePolicy): R;
|
|
74
74
|
dropPolicy(node: PostgresDropPolicy): R;
|
|
75
75
|
alterPolicyRename(node: PostgresAlterPolicyRename): R;
|
|
76
|
+
createIndex(node: PostgresCreateIndex): R;
|
|
77
|
+
dropIndex(node: PostgresDropIndex): R;
|
|
78
|
+
alterIndexRename(node: PostgresAlterIndexRename): R;
|
|
76
79
|
disableRowLevelSecurity(node: PostgresDisableRowLevelSecurity): R;
|
|
77
80
|
}
|
|
78
81
|
|
|
@@ -284,6 +287,104 @@ export class PostgresAlterPolicyRename extends PostgresDdlNode {
|
|
|
284
287
|
}
|
|
285
288
|
}
|
|
286
289
|
|
|
290
|
+
/**
|
|
291
|
+
* The element list between the parens of CREATE INDEX: either a column
|
|
292
|
+
* tuple (each identifier quoted by the renderer) or one opaque expression
|
|
293
|
+
* string covering the entire list, inserted verbatim — the same opaque-SQL
|
|
294
|
+
* stance as RLS policy predicates (ADR 234).
|
|
295
|
+
*/
|
|
296
|
+
export type DdlIndexElements =
|
|
297
|
+
| { readonly columns: readonly string[] }
|
|
298
|
+
| { readonly expression: string };
|
|
299
|
+
|
|
300
|
+
export class PostgresCreateIndex extends PostgresDdlNode {
|
|
301
|
+
readonly kind = 'create-index' as const;
|
|
302
|
+
/** Absent ⇔ unqualified (the unbound namespace — `search_path` decides). */
|
|
303
|
+
readonly schema: string | undefined;
|
|
304
|
+
readonly table: string;
|
|
305
|
+
readonly name: string;
|
|
306
|
+
readonly unique: boolean;
|
|
307
|
+
readonly elements: DdlIndexElements;
|
|
308
|
+
readonly type: string | undefined;
|
|
309
|
+
readonly options: Record<string, unknown> | undefined;
|
|
310
|
+
/**
|
|
311
|
+
* Partial-index predicate (WHERE body, without the keyword). Inserted
|
|
312
|
+
* verbatim, never quoted or escaped.
|
|
313
|
+
*/
|
|
314
|
+
readonly where: string | undefined;
|
|
315
|
+
|
|
316
|
+
constructor(options: {
|
|
317
|
+
readonly schema: string | undefined;
|
|
318
|
+
readonly table: string;
|
|
319
|
+
readonly name: string;
|
|
320
|
+
readonly unique: boolean;
|
|
321
|
+
readonly elements: DdlIndexElements;
|
|
322
|
+
readonly type: string | undefined;
|
|
323
|
+
readonly options: Record<string, unknown> | undefined;
|
|
324
|
+
readonly where: string | undefined;
|
|
325
|
+
}) {
|
|
326
|
+
super();
|
|
327
|
+
this.schema = options.schema;
|
|
328
|
+
this.table = options.table;
|
|
329
|
+
this.name = options.name;
|
|
330
|
+
this.unique = options.unique;
|
|
331
|
+
this.elements =
|
|
332
|
+
'columns' in options.elements
|
|
333
|
+
? { columns: Object.freeze([...options.elements.columns]) }
|
|
334
|
+
: { expression: options.elements.expression };
|
|
335
|
+
this.type = options.type;
|
|
336
|
+
this.options = options.options;
|
|
337
|
+
this.where = options.where;
|
|
338
|
+
this.freeze();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
override accept<R>(visitor: PostgresDdlVisitor<R>): R {
|
|
342
|
+
return visitor.createIndex(this);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export class PostgresDropIndex extends PostgresDdlNode {
|
|
347
|
+
readonly kind = 'drop-index' as const;
|
|
348
|
+
/** Absent ⇔ unqualified (the unbound namespace). */
|
|
349
|
+
readonly schema: string | undefined;
|
|
350
|
+
readonly name: string;
|
|
351
|
+
|
|
352
|
+
constructor(options: { readonly schema: string | undefined; readonly name: string }) {
|
|
353
|
+
super();
|
|
354
|
+
this.schema = options.schema;
|
|
355
|
+
this.name = options.name;
|
|
356
|
+
this.freeze();
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
override accept<R>(visitor: PostgresDdlVisitor<R>): R {
|
|
360
|
+
return visitor.dropIndex(this);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export class PostgresAlterIndexRename extends PostgresDdlNode {
|
|
365
|
+
readonly kind = 'alter-index-rename' as const;
|
|
366
|
+
/** Absent ⇔ unqualified (the unbound namespace). */
|
|
367
|
+
readonly schema: string | undefined;
|
|
368
|
+
readonly from: string;
|
|
369
|
+
readonly to: string;
|
|
370
|
+
|
|
371
|
+
constructor(options: {
|
|
372
|
+
readonly schema: string | undefined;
|
|
373
|
+
readonly from: string;
|
|
374
|
+
readonly to: string;
|
|
375
|
+
}) {
|
|
376
|
+
super();
|
|
377
|
+
this.schema = options.schema;
|
|
378
|
+
this.from = options.from;
|
|
379
|
+
this.to = options.to;
|
|
380
|
+
this.freeze();
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
override accept<R>(visitor: PostgresDdlVisitor<R>): R {
|
|
384
|
+
return visitor.alterIndexRename(this);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
287
388
|
export class PostgresDisableRowLevelSecurity extends PostgresDdlNode {
|
|
288
389
|
readonly kind = 'disable-row-level-security' as const;
|
|
289
390
|
readonly schema: string;
|
|
@@ -310,4 +411,7 @@ export type AnyPostgresDdlNode =
|
|
|
310
411
|
| PostgresCreatePolicy
|
|
311
412
|
| PostgresDropPolicy
|
|
312
413
|
| PostgresAlterPolicyRename
|
|
414
|
+
| PostgresCreateIndex
|
|
415
|
+
| PostgresDropIndex
|
|
416
|
+
| PostgresAlterIndexRename
|
|
313
417
|
| PostgresDisableRowLevelSecurity;
|
package/src/core/errors.ts
CHANGED
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
import { CliStructuredError } from '@prisma-next/errors/control';
|
|
2
|
+
import type { StructuredError, StructuredErrorOptions } from '@prisma-next/utils/structured-error';
|
|
3
|
+
import { structuredError } from '@prisma-next/utils/structured-error';
|
|
4
|
+
|
|
5
|
+
export type PostgresTargetErrorCode =
|
|
6
|
+
| 'CONTRACT.CODEC_DESCRIPTOR_MISSING'
|
|
7
|
+
| 'CONTRACT.DEFAULT_INVALID'
|
|
8
|
+
| 'CONTRACT.ENTITY_KIND_INVALID'
|
|
9
|
+
| 'CONTRACT.IDENTIFIER_INVALID'
|
|
10
|
+
| 'CONTRACT.INDEX_INVALID'
|
|
11
|
+
| 'CONTRACT.INFER_UNSUPPORTED'
|
|
12
|
+
| 'CONTRACT.MODEL_UNKNOWN'
|
|
13
|
+
| 'CONTRACT.NAME_DUPLICATE'
|
|
14
|
+
| 'CONTRACT.NATIVE_TYPE_INVALID'
|
|
15
|
+
| 'CONTRACT.PACK_CONTRIBUTION_INVALID'
|
|
16
|
+
| 'CONTRACT.POLICY_INVALID'
|
|
17
|
+
| 'CONTRACT.ROLE_INVALID'
|
|
18
|
+
| 'MIGRATION.CONTRACT_SPACE_VIOLATION'
|
|
19
|
+
| 'MIGRATION.POSTGRES_CONTROL_STACK_MISSING'
|
|
20
|
+
| 'MIGRATION.TARGET_MISMATCH'
|
|
21
|
+
| 'RUNTIME.DECODE_FAILED'
|
|
22
|
+
| 'RUNTIME.ENCODE_FAILED'
|
|
23
|
+
| 'RUNTIME.TYPE_PARAMS_INVALID';
|
|
24
|
+
|
|
25
|
+
export function postgresError(
|
|
26
|
+
code: PostgresTargetErrorCode,
|
|
27
|
+
message: string,
|
|
28
|
+
options?: StructuredErrorOptions,
|
|
29
|
+
): StructuredError {
|
|
30
|
+
return structuredError(code, message, options);
|
|
31
|
+
}
|
|
2
32
|
|
|
3
33
|
/**
|
|
4
34
|
* A `PostgresMigration` operation that needs the materialized control adapter
|
|
@@ -5,6 +5,7 @@ import * as contractFree from '@prisma-next/sql-relational-core/contract-free';
|
|
|
5
5
|
import type { SqlColumnDefaultIR, SqlColumnIR } from '@prisma-next/sql-schema-ir/types';
|
|
6
6
|
import { blindCast } from '@prisma-next/utils/casts';
|
|
7
7
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
8
|
+
import { InternalError } from '@prisma-next/utils/internal-error';
|
|
8
9
|
import { postgresDefaultToDdlColumnDefault } from './op-factory-call';
|
|
9
10
|
import { buildColumnDefaultSql, buildColumnTypeSql } from './planner-ddl-builders';
|
|
10
11
|
import { resolveIdentityValue } from './planner-identity-values';
|
|
@@ -29,7 +30,7 @@ function columnLike(
|
|
|
29
30
|
'nativeType' | 'codecId' | 'nullable' | 'many' | 'typeParams' | 'typeRef' | 'default'
|
|
30
31
|
> {
|
|
31
32
|
if (column.codecRef === undefined || column.codecBaseNativeType === undefined) {
|
|
32
|
-
throw new
|
|
33
|
+
throw new InternalError(
|
|
33
34
|
`columnLike: expected column "${column.name}" carries no codec identity — the expected tree must be derived via contractToSchemaIR for planning`,
|
|
34
35
|
);
|
|
35
36
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
SqlUniqueIR,
|
|
11
11
|
} from '@prisma-next/sql-schema-ir/types';
|
|
12
12
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
13
|
+
import { postgresError } from '../errors';
|
|
13
14
|
import type { PostgresRlsPolicy } from '../postgres-rls-policy';
|
|
14
15
|
import type { PostgresContract } from '../postgres-schema';
|
|
15
16
|
import { isPostgresSchema } from '../postgres-schema';
|
|
@@ -192,18 +193,30 @@ export function contractToPostgresDatabaseSchemaNode(
|
|
|
192
193
|
dependsOn: columnDependsOn(ddlSchema, tableName, u.columns),
|
|
193
194
|
}),
|
|
194
195
|
);
|
|
195
|
-
const indexes = sqlTable.indexes.map(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
196
|
+
const indexes = sqlTable.indexes.map((i) => {
|
|
197
|
+
const base = {
|
|
198
|
+
name: i.name,
|
|
199
|
+
prefix: i.prefix,
|
|
200
|
+
where: i.where,
|
|
201
|
+
unique: i.unique,
|
|
202
|
+
partial: i.partial,
|
|
203
|
+
type: i.type,
|
|
204
|
+
options: i.options,
|
|
205
|
+
annotations: i.annotations,
|
|
206
|
+
// Expression indexes stamp chains to every column of the table —
|
|
207
|
+
// the same over-approximation the family derivation uses.
|
|
208
|
+
dependsOn: columnDependsOn(
|
|
209
|
+
ddlSchema,
|
|
210
|
+
tableName,
|
|
211
|
+
i.columns ?? Object.keys(sqlTable.columns),
|
|
212
|
+
),
|
|
213
|
+
};
|
|
214
|
+
return new SqlIndexIR(
|
|
215
|
+
i.expression !== undefined
|
|
216
|
+
? { ...base, expression: i.expression }
|
|
217
|
+
: { ...base, columns: i.columns ?? [] },
|
|
218
|
+
);
|
|
219
|
+
});
|
|
207
220
|
const primaryKey =
|
|
208
221
|
sqlTable.primaryKey !== undefined
|
|
209
222
|
? new PrimaryKey({
|
|
@@ -231,14 +244,25 @@ export function contractToPostgresDatabaseSchemaNode(
|
|
|
231
244
|
for (const [tableName, tablePolicies] of policiesByTable) {
|
|
232
245
|
if (!(tableName in tables)) {
|
|
233
246
|
const policyName = tablePolicies[0]?.name ?? '(unknown)';
|
|
234
|
-
throw
|
|
247
|
+
throw postgresError(
|
|
248
|
+
'CONTRACT.POLICY_INVALID',
|
|
235
249
|
`contract-to-postgres-database-schema-node: policy "${policyName}" references table "${tableName}" not present in namespace "${ddlSchema}"`,
|
|
250
|
+
{ meta: { policyName, tableName, namespaceId: ddlSchema, reason: 'table-missing' } },
|
|
236
251
|
);
|
|
237
252
|
}
|
|
238
253
|
if (!Object.hasOwn(ns.rls, tableName)) {
|
|
239
254
|
const policyPrefix = tablePolicies[0]?.prefix ?? '(unknown)';
|
|
240
|
-
throw
|
|
255
|
+
throw postgresError(
|
|
256
|
+
'CONTRACT.POLICY_INVALID',
|
|
241
257
|
`contract-to-postgres-database-schema-node: policy "${policyPrefix}" targets table "${tableName}" in namespace "${ddlSchema}", which is not RLS-controlled. Mark the model with @@rls (entries.rls["${tableName}"]) or remove the policy.`,
|
|
258
|
+
{
|
|
259
|
+
meta: {
|
|
260
|
+
policyName: policyPrefix,
|
|
261
|
+
tableName,
|
|
262
|
+
namespaceId: ddlSchema,
|
|
263
|
+
reason: 'table-not-rls-controlled',
|
|
264
|
+
},
|
|
265
|
+
},
|
|
242
266
|
);
|
|
243
267
|
}
|
|
244
268
|
}
|
|
@@ -30,7 +30,6 @@ import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
|
30
30
|
import type { SqlStorage, StorageTypeInstance } from '@prisma-next/sql-contract/types';
|
|
31
31
|
import type { DdlTableConstraint } from '@prisma-next/sql-relational-core/ast';
|
|
32
32
|
import * as contractFree from '@prisma-next/sql-relational-core/contract-free';
|
|
33
|
-
import { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';
|
|
34
33
|
import {
|
|
35
34
|
RelationalSchemaNodeKind,
|
|
36
35
|
type SqlColumnDefaultIR,
|
|
@@ -200,6 +199,7 @@ function classifyCall(call: PostgresOpFactoryCall): CallCategory {
|
|
|
200
199
|
case 'addUnique':
|
|
201
200
|
return 'unique';
|
|
202
201
|
case 'createIndex':
|
|
202
|
+
case 'renameIndex':
|
|
203
203
|
return 'index';
|
|
204
204
|
case 'addForeignKey':
|
|
205
205
|
return 'foreignKey';
|
|
@@ -248,6 +248,7 @@ function conflictKindForCall(call: PostgresOpFactoryCall): SqlPlannerConflict['k
|
|
|
248
248
|
case 'dropConstraint':
|
|
249
249
|
return 'foreignKeyConflict';
|
|
250
250
|
case 'createIndex':
|
|
251
|
+
case 'renameIndex':
|
|
251
252
|
case 'dropIndex':
|
|
252
253
|
return 'indexIncompatible';
|
|
253
254
|
default:
|
|
@@ -263,6 +264,7 @@ function locationForCall(call: PostgresOpFactoryCall): SqlPlannerConflict['locat
|
|
|
263
264
|
tableName?: string;
|
|
264
265
|
columnName?: string;
|
|
265
266
|
indexName?: string;
|
|
267
|
+
newIndexName?: string;
|
|
266
268
|
constraintName?: string;
|
|
267
269
|
typeName?: string;
|
|
268
270
|
};
|
|
@@ -281,7 +283,10 @@ function locationForCall(call: PostgresOpFactoryCall): SqlPlannerConflict['locat
|
|
|
281
283
|
location.entityName = anyCall.typeName;
|
|
282
284
|
}
|
|
283
285
|
if (anyCall.columnName) location.column = anyCall.columnName;
|
|
286
|
+
// A rename call carries old/new index names; the new name is the index's
|
|
287
|
+
// contract-side identity, so it is the conflict location.
|
|
284
288
|
if (anyCall.indexName) location.index = anyCall.indexName;
|
|
289
|
+
else if (anyCall.newIndexName) location.index = anyCall.newIndexName;
|
|
285
290
|
if (anyCall.constraintName) location.constraint = anyCall.constraintName;
|
|
286
291
|
return Object.keys(location).length > 0 ? (location as SqlPlannerConflictLocation) : undefined;
|
|
287
292
|
}
|
|
@@ -476,11 +481,7 @@ function buildCreateTableCallsFromNode(
|
|
|
476
481
|
),
|
|
477
482
|
];
|
|
478
483
|
for (const index of table.indexes) {
|
|
479
|
-
|
|
480
|
-
const extras: { type?: string; options?: Record<string, unknown> } = {};
|
|
481
|
-
if (index.type !== undefined) extras.type = index.type;
|
|
482
|
-
if (index.options !== undefined) extras.options = index.options;
|
|
483
|
-
calls.push(new CreateIndexCall(schemaName, table.name, indexName, [...index.columns], extras));
|
|
484
|
+
calls.push(createIndexCallFromNode(index, schemaName, table.name));
|
|
484
485
|
}
|
|
485
486
|
for (const fk of table.foreignKeys) {
|
|
486
487
|
calls.push(new AddForeignKeyCall(schemaName, table.name, fkSpecFromNode(fk, table.name)));
|
|
@@ -808,6 +809,37 @@ function mapUniqueNodeIssue(
|
|
|
808
809
|
return notOk(nodeConflict('indexIncompatible', issue.path.join('/')));
|
|
809
810
|
}
|
|
810
811
|
|
|
812
|
+
/**
|
|
813
|
+
* The CreateIndexCall for a name-identified index node: the node's own name
|
|
814
|
+
* verbatim, its element list (column tuple or opaque expression), and the
|
|
815
|
+
* unique/type/options/where attributes carried through.
|
|
816
|
+
*/
|
|
817
|
+
export function createIndexCallFromNode(
|
|
818
|
+
index: SqlIndexIR,
|
|
819
|
+
schemaName: string,
|
|
820
|
+
tableName: string,
|
|
821
|
+
): CreateIndexCall {
|
|
822
|
+
const extras: {
|
|
823
|
+
type?: string;
|
|
824
|
+
options?: Record<string, unknown>;
|
|
825
|
+
where?: string;
|
|
826
|
+
unique?: boolean;
|
|
827
|
+
} = {};
|
|
828
|
+
if (index.type !== undefined) extras.type = index.type;
|
|
829
|
+
if (index.options !== undefined) extras.options = index.options;
|
|
830
|
+
if (index.where !== undefined) extras.where = index.where;
|
|
831
|
+
if (index.unique) extras.unique = true;
|
|
832
|
+
return new CreateIndexCall(
|
|
833
|
+
schemaName,
|
|
834
|
+
tableName,
|
|
835
|
+
index.name,
|
|
836
|
+
index.columns !== undefined
|
|
837
|
+
? { columns: [...index.columns] }
|
|
838
|
+
: { expression: index.expression ?? '' },
|
|
839
|
+
extras,
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
|
|
811
843
|
function mapIndexNodeIssue(
|
|
812
844
|
issue: SchemaDiffIssue,
|
|
813
845
|
schemaName: string,
|
|
@@ -818,19 +850,14 @@ function mapIndexNodeIssue(
|
|
|
818
850
|
SqlIndexIR,
|
|
819
851
|
'a not-found index issue always carries the expected index node'
|
|
820
852
|
>(issue.expected);
|
|
821
|
-
|
|
822
|
-
const extras: { type?: string; options?: Record<string, unknown> } = {};
|
|
823
|
-
if (index.type !== undefined) extras.type = index.type;
|
|
824
|
-
if (index.options !== undefined) extras.options = index.options;
|
|
825
|
-
return ok([new CreateIndexCall(schemaName, tableName, indexName, [...index.columns], extras)]);
|
|
853
|
+
return ok([createIndexCallFromNode(index, schemaName, tableName)]);
|
|
826
854
|
}
|
|
827
855
|
if (issueOutcome(issue) === 'not-expected') {
|
|
828
856
|
const index = blindCast<
|
|
829
857
|
SqlIndexIR,
|
|
830
858
|
'a not-expected index issue always carries the actual index node'
|
|
831
859
|
>(issue.actual);
|
|
832
|
-
|
|
833
|
-
return ok([new DropIndexCall(schemaName, tableName, indexName)]);
|
|
860
|
+
return ok([new DropIndexCall(schemaName, tableName, index.name)]);
|
|
834
861
|
}
|
|
835
862
|
return notOk(nodeConflict('indexIncompatible', issue.path.join('/')));
|
|
836
863
|
}
|