@prisma-next/adapter-postgres 0.5.0-dev.9 → 0.6.0-dev.1
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 +14 -16
- package/dist/adapter-CBaRvhQF.mjs +52 -0
- package/dist/adapter-CBaRvhQF.mjs.map +1 -0
- package/dist/adapter.d.mts +3 -4
- package/dist/adapter.d.mts.map +1 -1
- package/dist/adapter.mjs +2 -3
- package/dist/column-types.d.mts +19 -24
- package/dist/column-types.d.mts.map +1 -1
- package/dist/column-types.mjs +20 -60
- package/dist/column-types.mjs.map +1 -1
- package/dist/control.d.mts +83 -3
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +117 -29
- package/dist/control.mjs.map +1 -1
- package/dist/{descriptor-meta-RTDzyrae.mjs → descriptor-meta-CyjnYUWG.mjs} +35 -27
- package/dist/descriptor-meta-CyjnYUWG.mjs.map +1 -0
- package/dist/operation-types.d.mts +11 -10
- package/dist/operation-types.d.mts.map +1 -1
- package/dist/operation-types.mjs +1 -1
- package/dist/runtime.d.mts +3 -11
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +19 -81
- package/dist/runtime.mjs.map +1 -1
- package/dist/{sql-renderer-pEaSP82_.mjs → sql-renderer-wTVSEy5H.mjs} +109 -48
- package/dist/sql-renderer-wTVSEy5H.mjs.map +1 -0
- package/dist/{types-CfRPdAk8.d.mts → types-B1eiuBHQ.d.mts} +12 -1
- package/dist/types-B1eiuBHQ.d.mts.map +1 -0
- package/dist/types.d.mts +1 -1
- package/dist/types.mjs +1 -1
- package/package.json +23 -23
- package/src/core/adapter.ts +36 -43
- package/src/core/codec-lookup.ts +19 -0
- package/src/core/control-adapter.ts +252 -98
- package/src/core/control-mutation-defaults.ts +24 -18
- package/src/core/descriptor-meta.ts +27 -20
- package/src/core/sql-renderer.ts +129 -66
- package/src/core/types.ts +11 -0
- package/src/exports/column-types.ts +21 -61
- package/src/exports/control.ts +3 -2
- package/src/exports/runtime.ts +27 -66
- package/src/types/operation-types.ts +19 -9
- package/dist/adapter-hNElNHo4.mjs +0 -60
- package/dist/adapter-hNElNHo4.mjs.map +0 -1
- package/dist/descriptor-meta-RTDzyrae.mjs.map +0 -1
- package/dist/sql-renderer-pEaSP82_.mjs.map +0 -1
- package/dist/types-CfRPdAk8.d.mts.map +0 -1
- package/src/core/json-schema-validator.ts +0 -54
- package/src/core/standard-schema.ts +0 -71
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import type { ContractMarkerRecord } from '@prisma-next/contract/types';
|
|
1
2
|
import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
|
|
3
|
+
import { parseContractMarkerRow } from '@prisma-next/family-sql/verify';
|
|
4
|
+
import type { CodecLookup } from '@prisma-next/framework-components/codec';
|
|
2
5
|
import type { ControlDriverInstance } from '@prisma-next/framework-components/control';
|
|
3
6
|
import type {
|
|
4
7
|
AnyQueryAst,
|
|
@@ -6,7 +9,6 @@ import type {
|
|
|
6
9
|
LowererContext,
|
|
7
10
|
} from '@prisma-next/sql-relational-core/ast';
|
|
8
11
|
import type {
|
|
9
|
-
DependencyIR,
|
|
10
12
|
PrimaryKey,
|
|
11
13
|
SqlColumnIR,
|
|
12
14
|
SqlForeignKeyIR,
|
|
@@ -19,6 +21,7 @@ import type {
|
|
|
19
21
|
import { parsePostgresDefault } from '@prisma-next/target-postgres/default-normalizer';
|
|
20
22
|
import { normalizeSchemaNativeType } from '@prisma-next/target-postgres/native-type-normalizer';
|
|
21
23
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
24
|
+
import { createPostgresBuiltinCodecLookup } from './codec-lookup';
|
|
22
25
|
import { pgEnumControlHooks } from './enum-control-hooks';
|
|
23
26
|
import { renderLoweredSql } from './sql-renderer';
|
|
24
27
|
import type { PostgresContract } from './types';
|
|
@@ -31,6 +34,19 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
31
34
|
readonly familyId = 'sql' as const;
|
|
32
35
|
readonly targetId = 'postgres' as const;
|
|
33
36
|
|
|
37
|
+
private readonly codecLookup: CodecLookup;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param codecLookup - Codec lookup used by the SQL renderer to resolve
|
|
41
|
+
* per-codec metadata at lower-time. Defaults to a Postgres-builtins-only
|
|
42
|
+
* lookup when omitted. Stack-aware callers
|
|
43
|
+
* (`SqlControlAdapterDescriptor.create(stack)`) supply
|
|
44
|
+
* `stack.codecLookup` so extension codecs are visible to the renderer.
|
|
45
|
+
*/
|
|
46
|
+
constructor(codecLookup?: CodecLookup) {
|
|
47
|
+
this.codecLookup = codecLookup ?? createPostgresBuiltinCodecLookup();
|
|
48
|
+
}
|
|
49
|
+
|
|
34
50
|
/**
|
|
35
51
|
* Target-specific normalizer for raw Postgres default expressions.
|
|
36
52
|
* Used by schema verification to normalize raw defaults before comparison.
|
|
@@ -53,7 +69,108 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
53
69
|
* without instantiating the runtime adapter.
|
|
54
70
|
*/
|
|
55
71
|
lower(ast: AnyQueryAst, context: LowererContext<unknown>): LoweredStatement {
|
|
56
|
-
return renderLoweredSql(ast, context.contract as PostgresContract);
|
|
72
|
+
return renderLoweredSql(ast, context.contract as PostgresContract, this.codecLookup);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Reads the contract marker from `prisma_contract.marker`. Probes
|
|
77
|
+
* `information_schema.tables` first so a fresh database (where the
|
|
78
|
+
* `prisma_contract` schema doesn't yet exist) returns `null` instead of a
|
|
79
|
+
* "relation does not exist" error — some Postgres wire-protocol clients
|
|
80
|
+
* (e.g. PGlite's TCP proxy) don't fully recover from extended-protocol
|
|
81
|
+
* parse errors, so we probe before reading.
|
|
82
|
+
*/
|
|
83
|
+
async readMarker(
|
|
84
|
+
driver: ControlDriverInstance<'sql', 'postgres'>,
|
|
85
|
+
space: string,
|
|
86
|
+
): Promise<ContractMarkerRecord | null> {
|
|
87
|
+
const exists = await driver.query(
|
|
88
|
+
`select 1
|
|
89
|
+
from information_schema.tables
|
|
90
|
+
where table_schema = $1 and table_name = $2`,
|
|
91
|
+
['prisma_contract', 'marker'],
|
|
92
|
+
);
|
|
93
|
+
if (exists.rows.length === 0) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const result = await driver.query<{
|
|
98
|
+
core_hash: string;
|
|
99
|
+
profile_hash: string;
|
|
100
|
+
contract_json: unknown | null;
|
|
101
|
+
canonical_version: number | null;
|
|
102
|
+
updated_at: Date | string;
|
|
103
|
+
app_tag: string | null;
|
|
104
|
+
meta: unknown | null;
|
|
105
|
+
invariants: readonly string[];
|
|
106
|
+
}>(
|
|
107
|
+
`select
|
|
108
|
+
core_hash,
|
|
109
|
+
profile_hash,
|
|
110
|
+
contract_json,
|
|
111
|
+
canonical_version,
|
|
112
|
+
updated_at,
|
|
113
|
+
app_tag,
|
|
114
|
+
meta,
|
|
115
|
+
invariants
|
|
116
|
+
from prisma_contract.marker
|
|
117
|
+
where space = $1`,
|
|
118
|
+
[space],
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const row = result.rows[0];
|
|
122
|
+
if (!row) return null;
|
|
123
|
+
return parseContractMarkerRow(row);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Reads every row from `prisma_contract.marker` and returns them keyed
|
|
128
|
+
* by `space`. Mirrors the existence probe in {@link readMarker}: a
|
|
129
|
+
* fresh database without the `prisma_contract` schema returns an empty
|
|
130
|
+
* map rather than raising "relation does not exist".
|
|
131
|
+
*/
|
|
132
|
+
async readAllMarkers(
|
|
133
|
+
driver: ControlDriverInstance<'sql', 'postgres'>,
|
|
134
|
+
): Promise<ReadonlyMap<string, ContractMarkerRecord>> {
|
|
135
|
+
const exists = await driver.query(
|
|
136
|
+
`select 1
|
|
137
|
+
from information_schema.tables
|
|
138
|
+
where table_schema = $1 and table_name = $2`,
|
|
139
|
+
['prisma_contract', 'marker'],
|
|
140
|
+
);
|
|
141
|
+
if (exists.rows.length === 0) {
|
|
142
|
+
return new Map();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const result = await driver.query<{
|
|
146
|
+
space: string;
|
|
147
|
+
core_hash: string;
|
|
148
|
+
profile_hash: string;
|
|
149
|
+
contract_json: unknown | null;
|
|
150
|
+
canonical_version: number | null;
|
|
151
|
+
updated_at: Date | string;
|
|
152
|
+
app_tag: string | null;
|
|
153
|
+
meta: unknown | null;
|
|
154
|
+
invariants: readonly string[];
|
|
155
|
+
}>(
|
|
156
|
+
`select
|
|
157
|
+
space,
|
|
158
|
+
core_hash,
|
|
159
|
+
profile_hash,
|
|
160
|
+
contract_json,
|
|
161
|
+
canonical_version,
|
|
162
|
+
updated_at,
|
|
163
|
+
app_tag,
|
|
164
|
+
meta,
|
|
165
|
+
invariants
|
|
166
|
+
from prisma_contract.marker`,
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const rows = new Map<string, ContractMarkerRecord>();
|
|
170
|
+
for (const row of result.rows) {
|
|
171
|
+
rows.set(row.space, parseContractMarkerRow(row));
|
|
172
|
+
}
|
|
173
|
+
return rows;
|
|
57
174
|
}
|
|
58
175
|
|
|
59
176
|
/**
|
|
@@ -63,7 +180,7 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
63
180
|
* and returns the schema structure without type mapping or contract enrichment.
|
|
64
181
|
* Type mapping and enrichment are handled separately by enrichment helpers.
|
|
65
182
|
*
|
|
66
|
-
* Uses batched queries to minimize database round trips (
|
|
183
|
+
* Uses batched queries to minimize database round trips (6 queries instead of 5T+1).
|
|
67
184
|
*
|
|
68
185
|
* @param driver - ControlDriverInstance<'sql', 'postgres'> instance for executing queries
|
|
69
186
|
* @param contract - Optional contract for contract-guided introspection (filtering, optimization)
|
|
@@ -75,39 +192,32 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
75
192
|
_contract?: unknown,
|
|
76
193
|
schema = 'public',
|
|
77
194
|
): Promise<SqlSchemaIR> {
|
|
78
|
-
// Execute all queries in parallel for efficiency (
|
|
79
|
-
const [
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
uniqueResult,
|
|
85
|
-
indexResult,
|
|
86
|
-
extensionsResult,
|
|
87
|
-
] = await Promise.all([
|
|
88
|
-
// Query all tables
|
|
89
|
-
driver.query<{ table_name: string }>(
|
|
90
|
-
`SELECT table_name
|
|
195
|
+
// Execute all queries in parallel for efficiency (6 queries instead of 5T+1)
|
|
196
|
+
const [tablesResult, columnsResult, pkResult, fkResult, uniqueResult, indexResult] =
|
|
197
|
+
await Promise.all([
|
|
198
|
+
// Query all tables
|
|
199
|
+
driver.query<{ table_name: string }>(
|
|
200
|
+
`SELECT table_name
|
|
91
201
|
FROM information_schema.tables
|
|
92
202
|
WHERE table_schema = $1
|
|
93
203
|
AND table_type = 'BASE TABLE'
|
|
94
204
|
ORDER BY table_name`,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
205
|
+
[schema],
|
|
206
|
+
),
|
|
207
|
+
// Query all columns for all tables in schema
|
|
208
|
+
driver.query<{
|
|
209
|
+
table_name: string;
|
|
210
|
+
column_name: string;
|
|
211
|
+
data_type: string;
|
|
212
|
+
udt_name: string;
|
|
213
|
+
is_nullable: string;
|
|
214
|
+
character_maximum_length: number | null;
|
|
215
|
+
numeric_precision: number | null;
|
|
216
|
+
numeric_scale: number | null;
|
|
217
|
+
column_default: string | null;
|
|
218
|
+
formatted_type: string | null;
|
|
219
|
+
}>(
|
|
220
|
+
`SELECT
|
|
111
221
|
c.table_name,
|
|
112
222
|
column_name,
|
|
113
223
|
data_type,
|
|
@@ -131,16 +241,16 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
131
241
|
AND NOT a.attisdropped
|
|
132
242
|
WHERE c.table_schema = $1
|
|
133
243
|
ORDER BY c.table_name, c.ordinal_position`,
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
244
|
+
[schema],
|
|
245
|
+
),
|
|
246
|
+
// Query all primary keys for all tables in schema
|
|
247
|
+
driver.query<{
|
|
248
|
+
table_name: string;
|
|
249
|
+
constraint_name: string;
|
|
250
|
+
column_name: string;
|
|
251
|
+
ordinal_position: number;
|
|
252
|
+
}>(
|
|
253
|
+
`SELECT
|
|
144
254
|
tc.table_name,
|
|
145
255
|
tc.constraint_name,
|
|
146
256
|
kcu.column_name,
|
|
@@ -153,24 +263,24 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
153
263
|
WHERE tc.table_schema = $1
|
|
154
264
|
AND tc.constraint_type = 'PRIMARY KEY'
|
|
155
265
|
ORDER BY tc.table_name, kcu.ordinal_position`,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
266
|
+
[schema],
|
|
267
|
+
),
|
|
268
|
+
// Query all foreign keys for all tables in schema, including referential actions.
|
|
269
|
+
// Uses pg_catalog for correct positional pairing of composite FK columns
|
|
270
|
+
// (information_schema.constraint_column_usage lacks ordinal_position,
|
|
271
|
+
// which causes Cartesian products for multi-column FKs).
|
|
272
|
+
driver.query<{
|
|
273
|
+
table_name: string;
|
|
274
|
+
constraint_name: string;
|
|
275
|
+
column_name: string;
|
|
276
|
+
ordinal_position: number;
|
|
277
|
+
referenced_table_schema: string;
|
|
278
|
+
referenced_table_name: string;
|
|
279
|
+
referenced_column_name: string;
|
|
280
|
+
delete_rule: string;
|
|
281
|
+
update_rule: string;
|
|
282
|
+
}>(
|
|
283
|
+
`SELECT
|
|
174
284
|
tc.table_name,
|
|
175
285
|
tc.constraint_name,
|
|
176
286
|
kcu.column_name,
|
|
@@ -203,16 +313,16 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
203
313
|
WHERE tc.table_schema = $1
|
|
204
314
|
AND tc.constraint_type = 'FOREIGN KEY'
|
|
205
315
|
ORDER BY tc.table_name, tc.constraint_name, kcu.ordinal_position`,
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
316
|
+
[schema],
|
|
317
|
+
),
|
|
318
|
+
// Query all unique constraints for all tables in schema (excluding PKs)
|
|
319
|
+
driver.query<{
|
|
320
|
+
table_name: string;
|
|
321
|
+
constraint_name: string;
|
|
322
|
+
column_name: string;
|
|
323
|
+
ordinal_position: number;
|
|
324
|
+
}>(
|
|
325
|
+
`SELECT
|
|
216
326
|
tc.table_name,
|
|
217
327
|
tc.constraint_name,
|
|
218
328
|
kcu.column_name,
|
|
@@ -225,26 +335,31 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
225
335
|
WHERE tc.table_schema = $1
|
|
226
336
|
AND tc.constraint_type = 'UNIQUE'
|
|
227
337
|
ORDER BY tc.table_name, tc.constraint_name, kcu.ordinal_position`,
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
338
|
+
[schema],
|
|
339
|
+
),
|
|
340
|
+
// Query all indexes for all tables in schema (excluding constraints)
|
|
341
|
+
driver.query<{
|
|
342
|
+
tablename: string;
|
|
343
|
+
indexname: string;
|
|
344
|
+
indisunique: boolean;
|
|
345
|
+
attname: string;
|
|
346
|
+
attnum: number;
|
|
347
|
+
amname: string | null;
|
|
348
|
+
reloptions: string[] | null;
|
|
349
|
+
}>(
|
|
350
|
+
`SELECT
|
|
239
351
|
i.tablename,
|
|
240
352
|
i.indexname,
|
|
241
353
|
ix.indisunique,
|
|
242
354
|
a.attname,
|
|
243
|
-
a.attnum
|
|
355
|
+
a.attnum,
|
|
356
|
+
am.amname,
|
|
357
|
+
ic.reloptions
|
|
244
358
|
FROM pg_indexes i
|
|
245
359
|
JOIN pg_class ic ON ic.relname = i.indexname
|
|
246
360
|
JOIN pg_namespace ins ON ins.oid = ic.relnamespace AND ins.nspname = $1
|
|
247
361
|
JOIN pg_index ix ON ix.indexrelid = ic.oid
|
|
362
|
+
JOIN pg_am am ON am.oid = ic.relam
|
|
248
363
|
JOIN pg_class t ON t.oid = ix.indrelid
|
|
249
364
|
JOIN pg_namespace tn ON tn.oid = t.relnamespace AND tn.nspname = $1
|
|
250
365
|
LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey) AND a.attnum > 0
|
|
@@ -257,16 +372,9 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
257
372
|
AND tc.constraint_name = i.indexname
|
|
258
373
|
)
|
|
259
374
|
ORDER BY i.tablename, i.indexname, a.attnum`,
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
driver.query<{ extname: string }>(
|
|
264
|
-
`SELECT extname
|
|
265
|
-
FROM pg_extension
|
|
266
|
-
ORDER BY extname`,
|
|
267
|
-
[],
|
|
268
|
-
),
|
|
269
|
-
]);
|
|
375
|
+
[schema],
|
|
376
|
+
),
|
|
377
|
+
]);
|
|
270
378
|
|
|
271
379
|
// Group results by table name for efficient lookup
|
|
272
380
|
const columnsByTable = groupBy(columnsResult.rows, 'table_name');
|
|
@@ -402,7 +510,16 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
402
510
|
}));
|
|
403
511
|
|
|
404
512
|
// Process indexes
|
|
405
|
-
const indexesMap = new Map<
|
|
513
|
+
const indexesMap = new Map<
|
|
514
|
+
string,
|
|
515
|
+
{
|
|
516
|
+
columns: string[];
|
|
517
|
+
name: string;
|
|
518
|
+
unique: boolean;
|
|
519
|
+
type: string | undefined;
|
|
520
|
+
options: Record<string, string> | undefined;
|
|
521
|
+
}
|
|
522
|
+
>();
|
|
406
523
|
for (const idxRow of indexesByTable.get(tableName) ?? []) {
|
|
407
524
|
if (!idxRow.attname) {
|
|
408
525
|
continue;
|
|
@@ -411,10 +528,17 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
411
528
|
if (existing) {
|
|
412
529
|
existing.columns.push(idxRow.attname);
|
|
413
530
|
} else {
|
|
531
|
+
// Drop btree (the Postgres default) so a contract index without an
|
|
532
|
+
// explicit type matches a default-method introspected index without
|
|
533
|
+
// forcing DROP+CREATE on every plan.
|
|
534
|
+
const indexType = idxRow.amname && idxRow.amname !== 'btree' ? idxRow.amname : undefined;
|
|
535
|
+
const indexOptions = parsePgReloptions(idxRow.reloptions, idxRow.indexname);
|
|
414
536
|
indexesMap.set(idxRow.indexname, {
|
|
415
537
|
columns: [idxRow.attname],
|
|
416
538
|
name: idxRow.indexname,
|
|
417
539
|
unique: idxRow.indisunique,
|
|
540
|
+
type: indexType,
|
|
541
|
+
options: indexOptions,
|
|
418
542
|
});
|
|
419
543
|
}
|
|
420
544
|
}
|
|
@@ -422,6 +546,8 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
422
546
|
columns: Object.freeze([...idx.columns]) as readonly string[],
|
|
423
547
|
name: idx.name,
|
|
424
548
|
unique: idx.unique,
|
|
549
|
+
...(idx.type !== undefined && { type: idx.type }),
|
|
550
|
+
...(idx.options !== undefined && { options: idx.options }),
|
|
425
551
|
}));
|
|
426
552
|
|
|
427
553
|
tables[tableName] = {
|
|
@@ -434,10 +560,6 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
434
560
|
};
|
|
435
561
|
}
|
|
436
562
|
|
|
437
|
-
const dependencies: readonly DependencyIR[] = extensionsResult.rows.map((row) => ({
|
|
438
|
-
id: `postgres.extension.${row.extname}`,
|
|
439
|
-
}));
|
|
440
|
-
|
|
441
563
|
const storageTypes =
|
|
442
564
|
(await pgEnumControlHooks.introspectTypes?.({ driver, schemaName: schema })) ?? {};
|
|
443
565
|
|
|
@@ -454,7 +576,6 @@ export class PostgresControlAdapter implements SqlControlAdapter<'postgres'> {
|
|
|
454
576
|
|
|
455
577
|
return {
|
|
456
578
|
tables,
|
|
457
|
-
dependencies,
|
|
458
579
|
annotations,
|
|
459
580
|
};
|
|
460
581
|
}
|
|
@@ -555,6 +676,39 @@ function mapReferentialAction(rule: string): SqlReferentialAction | undefined {
|
|
|
555
676
|
* Groups an array of objects by a specified key.
|
|
556
677
|
* Returns a Map for O(1) lookup by group key.
|
|
557
678
|
*/
|
|
679
|
+
/**
|
|
680
|
+
* Parses a `pg_class.reloptions` array into a `Record<string, string>`.
|
|
681
|
+
*
|
|
682
|
+
* Postgres returns reloptions as a `text[]` whose entries are `key=value`
|
|
683
|
+
* strings; the value side is always a string regardless of the underlying
|
|
684
|
+
* scalar type. The verifier compares contract options to introspected
|
|
685
|
+
* options after coercing both sides to strings, so keeping the raw text
|
|
686
|
+
* here is correct.
|
|
687
|
+
*
|
|
688
|
+
* Returns `undefined` when the input is null/empty (no WITH clause).
|
|
689
|
+
*/
|
|
690
|
+
export function parsePgReloptions(
|
|
691
|
+
reloptions: readonly string[] | null,
|
|
692
|
+
indexName: string,
|
|
693
|
+
): Record<string, string> | undefined {
|
|
694
|
+
if (!reloptions || reloptions.length === 0) {
|
|
695
|
+
return undefined;
|
|
696
|
+
}
|
|
697
|
+
const result: Record<string, string> = {};
|
|
698
|
+
for (const entry of reloptions) {
|
|
699
|
+
const eq = entry.indexOf('=');
|
|
700
|
+
if (eq === -1) {
|
|
701
|
+
throw new Error(
|
|
702
|
+
`Postgres introspection: malformed reloption entry "${entry}" on index "${indexName}" (expected "key=value")`,
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
const key = entry.slice(0, eq);
|
|
706
|
+
const value = entry.slice(eq + 1);
|
|
707
|
+
result[key] = value;
|
|
708
|
+
}
|
|
709
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
710
|
+
}
|
|
711
|
+
|
|
558
712
|
function groupBy<T, K extends keyof T>(items: readonly T[], key: K): Map<T[K], T[]> {
|
|
559
713
|
const map = new Map<T[K], T[]>();
|
|
560
714
|
for (const item of items) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExecutionMutationDefaultValue } from '@prisma-next/contract/types';
|
|
2
|
+
import { timestampNowControlDescriptor } from '@prisma-next/family-sql/control';
|
|
2
3
|
import type {
|
|
3
4
|
ControlMutationDefaultEntry,
|
|
4
5
|
DefaultFunctionLoweringContext,
|
|
@@ -301,25 +302,30 @@ export function createPostgresDefaultFunctionRegistry(): ReadonlyMap<
|
|
|
301
302
|
}
|
|
302
303
|
|
|
303
304
|
export function createPostgresMutationDefaultGeneratorDescriptors(): readonly MutationDefaultGeneratorDescriptor[] {
|
|
304
|
-
return
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
resolveGeneratedColumnDescriptor: ({ generated }) => {
|
|
308
|
-
if (generated.kind !== 'generator' || generated.id !== id) {
|
|
309
|
-
return undefined;
|
|
310
|
-
}
|
|
311
|
-
const descriptor = resolveBuiltinGeneratedColumnDescriptor({
|
|
305
|
+
return [
|
|
306
|
+
...builtinGeneratorRegistryMetadata.map(
|
|
307
|
+
({ id, applicableCodecIds }): MutationDefaultGeneratorDescriptor => ({
|
|
312
308
|
id,
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
309
|
+
applicableCodecIds,
|
|
310
|
+
resolveGeneratedColumnDescriptor: ({ generated }) => {
|
|
311
|
+
if (generated.kind !== 'generator' || generated.id !== id) {
|
|
312
|
+
return undefined;
|
|
313
|
+
}
|
|
314
|
+
const descriptor = resolveBuiltinGeneratedColumnDescriptor({
|
|
315
|
+
id,
|
|
316
|
+
...(generated.params ? { params: generated.params } : {}),
|
|
317
|
+
});
|
|
318
|
+
return {
|
|
319
|
+
codecId: descriptor.type.codecId,
|
|
320
|
+
nativeType: descriptor.type.nativeType,
|
|
321
|
+
...(descriptor.type.typeRef ? { typeRef: descriptor.type.typeRef } : {}),
|
|
322
|
+
...(descriptor.typeParams ? { typeParams: descriptor.typeParams } : {}),
|
|
323
|
+
};
|
|
324
|
+
},
|
|
325
|
+
}),
|
|
326
|
+
),
|
|
327
|
+
timestampNowControlDescriptor(),
|
|
328
|
+
];
|
|
323
329
|
}
|
|
324
330
|
|
|
325
331
|
export function createPostgresScalarTypeDescriptors(): ReadonlyMap<string, string> {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { CodecControlHooks, ExpandNativeTypeInput } from '@prisma-next/family-sql/control';
|
|
2
|
-
import
|
|
2
|
+
import { buildOperation, refsOf, toExpr } from '@prisma-next/sql-relational-core/expression';
|
|
3
3
|
import {
|
|
4
4
|
PG_BIT_CODEC_ID,
|
|
5
5
|
PG_BOOL_CODEC_ID,
|
|
6
|
+
PG_BYTEA_CODEC_ID,
|
|
6
7
|
PG_CHAR_CODEC_ID,
|
|
7
8
|
PG_ENUM_CODEC_ID,
|
|
8
9
|
PG_FLOAT_CODEC_ID,
|
|
@@ -30,12 +31,11 @@ import {
|
|
|
30
31
|
SQL_TIMESTAMP_CODEC_ID,
|
|
31
32
|
SQL_VARCHAR_CODEC_ID,
|
|
32
33
|
} from '@prisma-next/target-postgres/codec-ids';
|
|
33
|
-
import {
|
|
34
|
+
import { postgresCodecRegistry } from '@prisma-next/target-postgres/codecs';
|
|
35
|
+
import type { QueryOperationTypes } from '../types/operation-types';
|
|
34
36
|
import { pgEnumControlHooks } from './enum-control-hooks';
|
|
35
37
|
|
|
36
|
-
// ============================================================================
|
|
37
|
-
// Helper functions for reducing boilerplate
|
|
38
|
-
// ============================================================================
|
|
38
|
+
// ============================================================================ Helper functions for reducing boilerplate ============================================================================
|
|
39
39
|
|
|
40
40
|
/** Creates a type import spec for codec types */
|
|
41
41
|
const codecTypeImport = (named: string) =>
|
|
@@ -124,21 +124,26 @@ const precisionHooks: CodecControlHooks = { expandNativeType: expandPrecision };
|
|
|
124
124
|
const numericHooks: CodecControlHooks = { expandNativeType: expandNumeric };
|
|
125
125
|
const identityHooks: CodecControlHooks = { expandNativeType: ({ nativeType }) => nativeType };
|
|
126
126
|
|
|
127
|
-
// ============================================================================
|
|
128
|
-
// Descriptor metadata
|
|
129
|
-
// ============================================================================
|
|
127
|
+
// ============================================================================ Descriptor metadata ============================================================================
|
|
130
128
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
]
|
|
129
|
+
type CodecTypesBase = Record<string, { readonly input: unknown; readonly output: unknown }>;
|
|
130
|
+
|
|
131
|
+
export function postgresQueryOperations<CT extends CodecTypesBase>(): QueryOperationTypes<CT> {
|
|
132
|
+
return {
|
|
133
|
+
ilike: {
|
|
134
|
+
self: { traits: ['textual'] },
|
|
135
|
+
impl: (self, pattern) => {
|
|
136
|
+
const selfRefs = refsOf(self);
|
|
137
|
+
return buildOperation({
|
|
138
|
+
method: 'ilike',
|
|
139
|
+
args: [toExpr(self), toExpr(pattern, PG_TEXT_CODEC_ID, selfRefs)],
|
|
140
|
+
returns: { codecId: PG_BOOL_CODEC_ID, nullable: false },
|
|
141
|
+
lowering: { targetFamily: 'sql', strategy: 'infix', template: '{{self}} ILIKE {{arg0}}' },
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
142
147
|
|
|
143
148
|
export const postgresAdapterDescriptorMeta = {
|
|
144
149
|
kind: 'adapter',
|
|
@@ -162,7 +167,7 @@ export const postgresAdapterDescriptorMeta = {
|
|
|
162
167
|
},
|
|
163
168
|
types: {
|
|
164
169
|
codecTypes: {
|
|
165
|
-
|
|
170
|
+
codecDescriptors: Array.from(postgresCodecRegistry.values()),
|
|
166
171
|
import: {
|
|
167
172
|
package: '@prisma-next/target-postgres/codec-types',
|
|
168
173
|
named: 'CodecTypes',
|
|
@@ -202,6 +207,7 @@ export const postgresAdapterDescriptorMeta = {
|
|
|
202
207
|
[PG_ENUM_CODEC_ID]: pgEnumControlHooks,
|
|
203
208
|
[PG_JSON_CODEC_ID]: identityHooks,
|
|
204
209
|
[PG_JSONB_CODEC_ID]: identityHooks,
|
|
210
|
+
[PG_BYTEA_CODEC_ID]: identityHooks,
|
|
205
211
|
},
|
|
206
212
|
},
|
|
207
213
|
storage: [
|
|
@@ -267,6 +273,7 @@ export const postgresAdapterDescriptorMeta = {
|
|
|
267
273
|
},
|
|
268
274
|
{ typeId: PG_JSON_CODEC_ID, familyId: 'sql', targetId: 'postgres', nativeType: 'json' },
|
|
269
275
|
{ typeId: PG_JSONB_CODEC_ID, familyId: 'sql', targetId: 'postgres', nativeType: 'jsonb' },
|
|
276
|
+
{ typeId: PG_BYTEA_CODEC_ID, familyId: 'sql', targetId: 'postgres', nativeType: 'bytea' },
|
|
270
277
|
],
|
|
271
278
|
queryOperationTypes: {
|
|
272
279
|
import: {
|