@prisma-next/adapter-postgres 0.12.0 → 0.13.0-dev.2
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 +13 -20
- package/dist/{adapter-H8BiuXdq.mjs → adapter-CAlWA4ug.mjs} +13 -13
- package/dist/adapter-CAlWA4ug.mjs.map +1 -0
- package/dist/adapter.d.mts +3 -2
- package/dist/adapter.d.mts.map +1 -1
- package/dist/adapter.mjs +1 -1
- package/dist/column-types.d.mts.map +1 -1
- package/dist/control-adapter-ZWrjGBq7.mjs +1480 -0
- package/dist/control-adapter-ZWrjGBq7.mjs.map +1 -0
- package/dist/control.d.mts +60 -10
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +4 -660
- package/dist/control.mjs.map +1 -1
- package/dist/{descriptor-meta-C1wNCHkd.mjs → descriptor-meta-NBwpqHS7.mjs} +1 -1
- package/dist/{descriptor-meta-C1wNCHkd.mjs.map → descriptor-meta-NBwpqHS7.mjs.map} +1 -1
- package/dist/operation-types.d.mts +1 -1
- package/dist/runtime.d.mts +2 -2
- package/dist/runtime.mjs +2 -2
- package/dist/{types-B1eiuBHQ.d.mts → types-Dv7M8jx8.d.mts} +1 -1
- package/dist/{types-B1eiuBHQ.d.mts.map → types-Dv7M8jx8.d.mts.map} +1 -1
- package/dist/types.d.mts +2 -2
- package/package.json +24 -24
- package/src/core/adapter.ts +28 -25
- package/src/core/control-adapter.ts +626 -205
- package/src/core/ddl-renderer.ts +155 -0
- package/src/core/enum-control-hooks.ts +2 -2
- package/src/core/marker-ledger.ts +124 -0
- package/src/core/sql-renderer.ts +66 -23
- package/dist/adapter-H8BiuXdq.mjs.map +0 -1
- package/dist/sql-renderer-DlZhVI9B.mjs +0 -457
- package/dist/sql-renderer-DlZhVI9B.mjs.map +0 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { REFERENTIAL_ACTION_SQL } from '@prisma-next/sql-contract/referential-action-sql';
|
|
2
|
+
import type {
|
|
3
|
+
DdlColumn,
|
|
4
|
+
DdlColumnDefaultVisitor,
|
|
5
|
+
DdlTableConstraint,
|
|
6
|
+
ForeignKeyConstraint,
|
|
7
|
+
FunctionColumnDefault,
|
|
8
|
+
LiteralColumnDefault,
|
|
9
|
+
PrimaryKeyConstraint,
|
|
10
|
+
UniqueConstraint,
|
|
11
|
+
} from '@prisma-next/sql-relational-core/ast';
|
|
12
|
+
import type {
|
|
13
|
+
PostgresCreateSchema,
|
|
14
|
+
PostgresCreateTable,
|
|
15
|
+
PostgresDdlNode,
|
|
16
|
+
PostgresDdlVisitor,
|
|
17
|
+
} from '@prisma-next/target-postgres/ddl';
|
|
18
|
+
import { escapeLiteral, quoteIdentifier } from '@prisma-next/target-postgres/sql-utils';
|
|
19
|
+
import type { PostgresLoweredStatement } from './types';
|
|
20
|
+
|
|
21
|
+
function quoteQualifiedIdentifier(name: string): string {
|
|
22
|
+
return name.split('.').map(quoteIdentifier).join('.');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function renderPrimaryKeyConstraint(constraint: PrimaryKeyConstraint): string {
|
|
26
|
+
const cols = constraint.columns.map(quoteIdentifier).join(', ');
|
|
27
|
+
if (constraint.name !== undefined) {
|
|
28
|
+
return `CONSTRAINT ${quoteIdentifier(constraint.name)} PRIMARY KEY (${cols})`;
|
|
29
|
+
}
|
|
30
|
+
return `PRIMARY KEY (${cols})`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function renderForeignKeyConstraint(constraint: ForeignKeyConstraint): string {
|
|
34
|
+
const cols = constraint.columns.map(quoteIdentifier).join(', ');
|
|
35
|
+
const refCols = constraint.refColumns.map(quoteIdentifier).join(', ');
|
|
36
|
+
let sql = `FOREIGN KEY (${cols}) REFERENCES ${quoteQualifiedIdentifier(constraint.refTable)} (${refCols})`;
|
|
37
|
+
if (constraint.onDelete !== undefined) {
|
|
38
|
+
sql += ` ON DELETE ${REFERENTIAL_ACTION_SQL[constraint.onDelete]}`;
|
|
39
|
+
}
|
|
40
|
+
if (constraint.onUpdate !== undefined) {
|
|
41
|
+
sql += ` ON UPDATE ${REFERENTIAL_ACTION_SQL[constraint.onUpdate]}`;
|
|
42
|
+
}
|
|
43
|
+
if (constraint.name !== undefined) {
|
|
44
|
+
sql = `CONSTRAINT ${quoteIdentifier(constraint.name)} ${sql}`;
|
|
45
|
+
}
|
|
46
|
+
return sql;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function renderUniqueConstraint(constraint: UniqueConstraint): string {
|
|
50
|
+
const cols = constraint.columns.map(quoteIdentifier).join(', ');
|
|
51
|
+
if (constraint.name !== undefined) {
|
|
52
|
+
return `CONSTRAINT ${quoteIdentifier(constraint.name)} UNIQUE (${cols})`;
|
|
53
|
+
}
|
|
54
|
+
return `UNIQUE (${cols})`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function renderTableConstraint(constraint: DdlTableConstraint): string {
|
|
58
|
+
switch (constraint.kind) {
|
|
59
|
+
case 'primary-key':
|
|
60
|
+
return renderPrimaryKeyConstraint(constraint);
|
|
61
|
+
case 'foreign-key':
|
|
62
|
+
return renderForeignKeyConstraint(constraint);
|
|
63
|
+
case 'unique':
|
|
64
|
+
return renderUniqueConstraint(constraint);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class PostgresDdlVisitorImpl implements PostgresDdlVisitor<string> {
|
|
69
|
+
createTable(node: PostgresCreateTable): string {
|
|
70
|
+
const ifNotExists = node.ifNotExists ? 'IF NOT EXISTS ' : '';
|
|
71
|
+
const tableRef = node.schema
|
|
72
|
+
? `${quoteIdentifier(node.schema)}.${quoteIdentifier(node.table)}`
|
|
73
|
+
: quoteIdentifier(node.table);
|
|
74
|
+
const columnDefs = node.columns.map((column) => renderColumn(column));
|
|
75
|
+
const constraintDefs =
|
|
76
|
+
node.constraints !== undefined ? node.constraints.map(renderTableConstraint) : [];
|
|
77
|
+
const allDefs = [...columnDefs, ...constraintDefs].join(',\n ');
|
|
78
|
+
return `CREATE TABLE ${ifNotExists}${tableRef} (\n ${allDefs}\n)`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
createSchema(node: PostgresCreateSchema): string {
|
|
82
|
+
const ifNotExists = node.ifNotExists ? 'IF NOT EXISTS ' : '';
|
|
83
|
+
return `CREATE SCHEMA ${ifNotExists}${quoteIdentifier(node.schema)}`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Postgres infers a quoted-literal default's type as `text` at parse time
|
|
88
|
+
// and applies an implicit text → target coercion at default-evaluation
|
|
89
|
+
// time. That coercion exists for some target types (text, jsonb, json)
|
|
90
|
+
// and not others (uuid, inet, timestamptz, citext, enums, user-defined
|
|
91
|
+
// types, geometry/geography from PostGIS, array types). Quoted-literal
|
|
92
|
+
// defaults on any non-text column therefore need an explicit
|
|
93
|
+
// `::<nativeType>` cast in the emitted DDL — both to state the default's
|
|
94
|
+
// intent (the literal IS that type, not text that happens to coerce) and
|
|
95
|
+
// to keep emitted migrations correct for types where no implicit cast
|
|
96
|
+
// exists. Numeric, boolean, and null literals are typed by Postgres
|
|
97
|
+
// directly (no `text` indirection) so they need no cast.
|
|
98
|
+
function isTextLikeNativeType(nativeType: string): boolean {
|
|
99
|
+
return (
|
|
100
|
+
nativeType === 'text' ||
|
|
101
|
+
nativeType === 'varchar' ||
|
|
102
|
+
nativeType.startsWith('varchar(') ||
|
|
103
|
+
nativeType === 'character varying' ||
|
|
104
|
+
nativeType.startsWith('character varying(') ||
|
|
105
|
+
nativeType === 'char' ||
|
|
106
|
+
nativeType.startsWith('char(') ||
|
|
107
|
+
nativeType === 'character' ||
|
|
108
|
+
nativeType.startsWith('character(')
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const defaultVisitor: DdlColumnDefaultVisitor<string> = {
|
|
113
|
+
literal(node: LiteralColumnDefault, ctx): string {
|
|
114
|
+
const { value } = node;
|
|
115
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
116
|
+
return `DEFAULT ${String(value)}`;
|
|
117
|
+
}
|
|
118
|
+
if (value === null) {
|
|
119
|
+
return 'DEFAULT NULL';
|
|
120
|
+
}
|
|
121
|
+
const serialized = typeof value === 'string' ? value : JSON.stringify(value);
|
|
122
|
+
const literal = `'${escapeLiteral(serialized)}'`;
|
|
123
|
+
return isTextLikeNativeType(ctx.nativeType)
|
|
124
|
+
? `DEFAULT ${literal}`
|
|
125
|
+
: `DEFAULT ${literal}::${ctx.nativeType}`;
|
|
126
|
+
},
|
|
127
|
+
function(node: FunctionColumnDefault, _ctx): string {
|
|
128
|
+
if (node.expression === 'autoincrement()') {
|
|
129
|
+
return '';
|
|
130
|
+
}
|
|
131
|
+
return `DEFAULT (${node.expression})`;
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
function renderColumn(column: DdlColumn): string {
|
|
136
|
+
const parts = [quoteIdentifier(column.name), column.type];
|
|
137
|
+
if (column.notNull) {
|
|
138
|
+
parts.push('NOT NULL');
|
|
139
|
+
}
|
|
140
|
+
if (column.primaryKey) {
|
|
141
|
+
parts.push('PRIMARY KEY');
|
|
142
|
+
}
|
|
143
|
+
const defaultClause = column.default
|
|
144
|
+
? column.default.accept(defaultVisitor, { nativeType: column.type })
|
|
145
|
+
: '';
|
|
146
|
+
if (defaultClause.length > 0) {
|
|
147
|
+
parts.push(defaultClause);
|
|
148
|
+
}
|
|
149
|
+
return parts.join(' ');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function renderLoweredDdl(ast: PostgresDdlNode): PostgresLoweredStatement {
|
|
153
|
+
const sql = ast.accept(new PostgresDdlVisitorImpl());
|
|
154
|
+
return Object.freeze({ sql, params: Object.freeze([]) });
|
|
155
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SqlControlDriverInstance } from '@prisma-next/sql-contract/types';
|
|
2
2
|
import { PG_ENUM_CODEC_ID } from '@prisma-next/target-postgres/codec-ids';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -117,7 +117,7 @@ function parseArrayElements(input: string): string[] {
|
|
|
117
117
|
* (which writes them under `schema.annotations.pg.storageTypes`).
|
|
118
118
|
*/
|
|
119
119
|
export async function introspectPostgresEnumTypes(options: {
|
|
120
|
-
readonly driver:
|
|
120
|
+
readonly driver: SqlControlDriverInstance<'postgres'>;
|
|
121
121
|
readonly schemaName?: string;
|
|
122
122
|
}): Promise<Record<string, PostgresEnumStorageTypeAnnotation>> {
|
|
123
123
|
const namespace = options.schemaName ?? 'public';
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { SqlControlDriverInstance } from '@prisma-next/sql-contract/types';
|
|
2
|
+
import {
|
|
3
|
+
type AnyQueryAst,
|
|
4
|
+
type LoweredStatement,
|
|
5
|
+
RawExpr,
|
|
6
|
+
} from '@prisma-next/sql-relational-core/ast';
|
|
7
|
+
import {
|
|
8
|
+
createAstCodecRegistry,
|
|
9
|
+
deriveParamMetadata,
|
|
10
|
+
encodeParamsWithMetadata,
|
|
11
|
+
} from '@prisma-next/sql-runtime';
|
|
12
|
+
import { PG_TIMESTAMPTZ_CODEC_ID } from '@prisma-next/target-postgres/codec-ids';
|
|
13
|
+
import { postgresCodecRegistry } from '@prisma-next/target-postgres/codecs';
|
|
14
|
+
import {
|
|
15
|
+
int4,
|
|
16
|
+
int8,
|
|
17
|
+
jsonb,
|
|
18
|
+
pgTable,
|
|
19
|
+
text,
|
|
20
|
+
textArray,
|
|
21
|
+
timestamptz,
|
|
22
|
+
} from '@prisma-next/target-postgres/contract-free';
|
|
23
|
+
|
|
24
|
+
const CONTROL_CODECS = createAstCodecRegistry(postgresCodecRegistry);
|
|
25
|
+
|
|
26
|
+
export const marker = pgTable(
|
|
27
|
+
{ name: 'marker', schema: 'prisma_contract' },
|
|
28
|
+
{
|
|
29
|
+
space: text(),
|
|
30
|
+
core_hash: text(),
|
|
31
|
+
profile_hash: text(),
|
|
32
|
+
contract_json: jsonb({ nullable: true }),
|
|
33
|
+
canonical_version: int4({ nullable: true }),
|
|
34
|
+
updated_at: timestamptz(),
|
|
35
|
+
app_tag: text({ nullable: true }),
|
|
36
|
+
meta: jsonb({ nullable: true }),
|
|
37
|
+
invariants: textArray(),
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Writeable subset of the `prisma_contract.ledger` table. Omits the
|
|
43
|
+
* DB-generated `id` (bigserial) and `created_at` (default `now()`) so the
|
|
44
|
+
* insert path doesn't have to pass them.
|
|
45
|
+
*/
|
|
46
|
+
export const ledger = pgTable(
|
|
47
|
+
{ name: 'ledger', schema: 'prisma_contract' },
|
|
48
|
+
{
|
|
49
|
+
space: text(),
|
|
50
|
+
migration_name: text(),
|
|
51
|
+
migration_hash: text(),
|
|
52
|
+
origin_core_hash: text({ nullable: true }),
|
|
53
|
+
destination_core_hash: text(),
|
|
54
|
+
operations: jsonb(),
|
|
55
|
+
},
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Read-side handle covering every column of `prisma_contract.ledger`,
|
|
60
|
+
* including the DB-generated `id` (for ORDER BY) and `created_at`.
|
|
61
|
+
*/
|
|
62
|
+
export const ledgerReadShape = pgTable(
|
|
63
|
+
{ name: 'ledger', schema: 'prisma_contract' },
|
|
64
|
+
{
|
|
65
|
+
id: int8(),
|
|
66
|
+
space: text(),
|
|
67
|
+
migration_name: text(),
|
|
68
|
+
migration_hash: text(),
|
|
69
|
+
origin_core_hash: text({ nullable: true }),
|
|
70
|
+
destination_core_hash: text(),
|
|
71
|
+
operations: jsonb(),
|
|
72
|
+
created_at: timestamptz(),
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
export const infoSchemaTables = pgTable(
|
|
77
|
+
{ name: 'tables', schema: 'information_schema' },
|
|
78
|
+
{ table_schema: text(), table_name: text() },
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
export const NOW = new RawExpr({
|
|
82
|
+
parts: ['now()'],
|
|
83
|
+
returns: { codecId: PG_TIMESTAMPTZ_CODEC_ID, nullable: false },
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
type Lower = (query: AnyQueryAst) => LoweredStatement;
|
|
87
|
+
|
|
88
|
+
type MarkerDriver = {
|
|
89
|
+
query<Row = Record<string, unknown>>(
|
|
90
|
+
sql: string,
|
|
91
|
+
params?: readonly unknown[],
|
|
92
|
+
): Promise<{ readonly rows: ReadonlyArray<Row> }>;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export function mergeInvariants(
|
|
96
|
+
current: readonly string[],
|
|
97
|
+
incoming: readonly string[],
|
|
98
|
+
): readonly string[] {
|
|
99
|
+
return [...new Set([...current, ...incoming])].sort();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function execute(
|
|
103
|
+
lower: Lower,
|
|
104
|
+
driver: MarkerDriver,
|
|
105
|
+
query: AnyQueryAst,
|
|
106
|
+
): Promise<readonly Record<string, unknown>[]> {
|
|
107
|
+
const lowered = lower(query);
|
|
108
|
+
const values = lowered.params.map((slot) => {
|
|
109
|
+
if (slot.kind === 'literal') return slot.value;
|
|
110
|
+
throw new Error('Postgres control DML lowered to a bind parameter, which is unsupported');
|
|
111
|
+
});
|
|
112
|
+
const encoded = await encodeParamsWithMetadata(
|
|
113
|
+
values,
|
|
114
|
+
deriveParamMetadata(query),
|
|
115
|
+
{},
|
|
116
|
+
CONTROL_CODECS,
|
|
117
|
+
);
|
|
118
|
+
const result = await driver.query(lowered.sql, encoded);
|
|
119
|
+
return result.rows;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export type PostgresMarkerDriver = MarkerDriver;
|
|
123
|
+
export type PostgresMarkerLower = Lower;
|
|
124
|
+
export type PostgresMarkerWriteDriver = SqlControlDriverInstance<'postgres'>;
|
package/src/core/sql-renderer.ts
CHANGED
|
@@ -27,9 +27,11 @@ import {
|
|
|
27
27
|
type RawSqlExpr,
|
|
28
28
|
type SelectAst,
|
|
29
29
|
type SubqueryExpr,
|
|
30
|
+
type TableSource,
|
|
30
31
|
type UpdateAst,
|
|
31
32
|
type WindowFuncExpr,
|
|
32
33
|
} from '@prisma-next/sql-relational-core/ast';
|
|
34
|
+
import { PostgresTableSource } from '@prisma-next/target-postgres/contract-free';
|
|
33
35
|
import { escapeLiteral, quoteIdentifier } from '@prisma-next/target-postgres/sql-utils';
|
|
34
36
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
35
37
|
import type { PostgresContract } from './types';
|
|
@@ -269,6 +271,39 @@ function renderDistinctPrefix(
|
|
|
269
271
|
return '';
|
|
270
272
|
}
|
|
271
273
|
|
|
274
|
+
function qualifyTableFromNamespaceCoordinate(
|
|
275
|
+
table: Pick<TableSource, 'name' | 'namespaceId'>,
|
|
276
|
+
contract: PostgresContract,
|
|
277
|
+
): string {
|
|
278
|
+
if (table instanceof PostgresTableSource && table.schema !== undefined) {
|
|
279
|
+
return `${quoteIdentifier(table.schema)}.${quoteIdentifier(table.name)}`;
|
|
280
|
+
}
|
|
281
|
+
if (table.namespaceId === undefined) {
|
|
282
|
+
return quoteIdentifier(table.name);
|
|
283
|
+
}
|
|
284
|
+
const namespace = contract.storage.namespaces[table.namespaceId];
|
|
285
|
+
if (namespace === undefined) {
|
|
286
|
+
throw new Error(
|
|
287
|
+
`Table "${table.name}" references namespace "${table.namespaceId}" which is not present as a Postgres schema on the contract`,
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
const qualifyTable = namespace.qualifyTable;
|
|
291
|
+
if (qualifyTable === undefined) {
|
|
292
|
+
throw new Error(
|
|
293
|
+
`Table "${table.name}" references namespace "${table.namespaceId}" which is not materialised as a Postgres schema on the contract`,
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
return qualifyTable.call(namespace, table.name);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function renderTableSource(source: TableSource, contract: PostgresContract): string {
|
|
300
|
+
const qualified = qualifyTableFromNamespaceCoordinate(source, contract);
|
|
301
|
+
if (!source.alias) {
|
|
302
|
+
return qualified;
|
|
303
|
+
}
|
|
304
|
+
return `${qualified} AS ${quoteIdentifier(source.alias)}`;
|
|
305
|
+
}
|
|
306
|
+
|
|
272
307
|
function renderSource(
|
|
273
308
|
source: AnyFromSource,
|
|
274
309
|
contract: PostgresContract,
|
|
@@ -276,13 +311,8 @@ function renderSource(
|
|
|
276
311
|
): string {
|
|
277
312
|
const node = source;
|
|
278
313
|
switch (node.kind) {
|
|
279
|
-
case 'table-source':
|
|
280
|
-
|
|
281
|
-
if (!node.alias) {
|
|
282
|
-
return table;
|
|
283
|
-
}
|
|
284
|
-
return `${table} AS ${quoteIdentifier(node.alias)}`;
|
|
285
|
-
}
|
|
314
|
+
case 'table-source':
|
|
315
|
+
return renderTableSource(node, contract);
|
|
286
316
|
case 'derived-table-source':
|
|
287
317
|
return `(${renderSelect(node.query, contract, pim)}) AS ${quoteIdentifier(node.alias)}`;
|
|
288
318
|
// v8 ignore next 4
|
|
@@ -659,8 +689,9 @@ function renderJoinOn(on: JoinOnExpr, contract: PostgresContract, pim: ParamInde
|
|
|
659
689
|
function getInsertColumnOrder(
|
|
660
690
|
rows: ReadonlyArray<Record<string, InsertValue>>,
|
|
661
691
|
contract: PostgresContract,
|
|
662
|
-
|
|
692
|
+
tableRef: Pick<TableSource, 'name' | 'namespaceId'>,
|
|
663
693
|
): string[] {
|
|
694
|
+
const tableName = tableRef.name;
|
|
664
695
|
const orderedColumns: string[] = [];
|
|
665
696
|
const seenColumns = new Set<string>();
|
|
666
697
|
|
|
@@ -678,14 +709,18 @@ function getInsertColumnOrder(
|
|
|
678
709
|
return orderedColumns;
|
|
679
710
|
}
|
|
680
711
|
|
|
681
|
-
let table: { columns: Record<string, unknown
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
712
|
+
let table: { columns: Readonly<Record<string, unknown>> } | undefined;
|
|
713
|
+
if (tableRef.namespaceId !== undefined) {
|
|
714
|
+
const ns = contract.storage.namespaces[tableRef.namespaceId];
|
|
715
|
+
table = ns?.entries.table[tableName];
|
|
716
|
+
}
|
|
717
|
+
if (table === undefined) {
|
|
718
|
+
for (const ns of Object.values(contract.storage.namespaces)) {
|
|
719
|
+
const found = ns.entries.table[tableName];
|
|
720
|
+
if (found !== undefined) {
|
|
721
|
+
table = found;
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
689
724
|
}
|
|
690
725
|
}
|
|
691
726
|
if (!table) {
|
|
@@ -694,7 +729,11 @@ function getInsertColumnOrder(
|
|
|
694
729
|
return Object.keys(table.columns);
|
|
695
730
|
}
|
|
696
731
|
|
|
697
|
-
function renderInsertValue(
|
|
732
|
+
function renderInsertValue(
|
|
733
|
+
value: InsertValue | undefined,
|
|
734
|
+
contract: PostgresContract,
|
|
735
|
+
pim: ParamIndexMap,
|
|
736
|
+
): string {
|
|
698
737
|
if (!value || value.kind === 'default-value') {
|
|
699
738
|
return 'DEFAULT';
|
|
700
739
|
}
|
|
@@ -705,6 +744,8 @@ function renderInsertValue(value: InsertValue | undefined, pim: ParamIndexMap):
|
|
|
705
744
|
return renderParamRef(value, pim);
|
|
706
745
|
case 'column-ref':
|
|
707
746
|
return renderColumn(value);
|
|
747
|
+
case 'raw-expr':
|
|
748
|
+
return renderExpr(value, contract, pim);
|
|
708
749
|
// v8 ignore next 4
|
|
709
750
|
default:
|
|
710
751
|
throw new Error(
|
|
@@ -714,7 +755,7 @@ function renderInsertValue(value: InsertValue | undefined, pim: ParamIndexMap):
|
|
|
714
755
|
}
|
|
715
756
|
|
|
716
757
|
function renderInsert(ast: InsertAst, contract: PostgresContract, pim: ParamIndexMap): string {
|
|
717
|
-
const table =
|
|
758
|
+
const table = qualifyTableFromNamespaceCoordinate(ast.table, contract);
|
|
718
759
|
const rows = ast.rows;
|
|
719
760
|
if (rows.length === 0) {
|
|
720
761
|
throw new Error('INSERT requires at least one row');
|
|
@@ -726,7 +767,7 @@ function renderInsert(ast: InsertAst, contract: PostgresContract, pim: ParamInde
|
|
|
726
767
|
return `INSERT INTO ${table} DEFAULT VALUES`;
|
|
727
768
|
}
|
|
728
769
|
|
|
729
|
-
const defaultColumns = getInsertColumnOrder(rows, contract, ast.table
|
|
770
|
+
const defaultColumns = getInsertColumnOrder(rows, contract, ast.table);
|
|
730
771
|
if (defaultColumns.length === 0) {
|
|
731
772
|
return `INSERT INTO ${table} VALUES ${rows.map(() => '()').join(', ')}`;
|
|
732
773
|
}
|
|
@@ -738,11 +779,13 @@ function renderInsert(ast: InsertAst, contract: PostgresContract, pim: ParamInde
|
|
|
738
779
|
.join(', ')}`;
|
|
739
780
|
}
|
|
740
781
|
|
|
741
|
-
const columnOrder = getInsertColumnOrder(rows, contract, ast.table
|
|
782
|
+
const columnOrder = getInsertColumnOrder(rows, contract, ast.table);
|
|
742
783
|
const columns = columnOrder.map((column) => quoteIdentifier(column));
|
|
743
784
|
const values = rows
|
|
744
785
|
.map((row) => {
|
|
745
|
-
const renderedRow = columnOrder.map((column) =>
|
|
786
|
+
const renderedRow = columnOrder.map((column) =>
|
|
787
|
+
renderInsertValue(row[column], contract, pim),
|
|
788
|
+
);
|
|
746
789
|
return `(${renderedRow.join(', ')})`;
|
|
747
790
|
})
|
|
748
791
|
.join(', ');
|
|
@@ -786,7 +829,7 @@ function renderInsert(ast: InsertAst, contract: PostgresContract, pim: ParamInde
|
|
|
786
829
|
}
|
|
787
830
|
|
|
788
831
|
function renderUpdate(ast: UpdateAst, contract: PostgresContract, pim: ParamIndexMap): string {
|
|
789
|
-
const table =
|
|
832
|
+
const table = qualifyTableFromNamespaceCoordinate(ast.table, contract);
|
|
790
833
|
const setEntries = Object.entries(ast.set);
|
|
791
834
|
if (setEntries.length === 0) {
|
|
792
835
|
throw new Error('UPDATE requires at least one SET assignment');
|
|
@@ -805,7 +848,7 @@ function renderUpdate(ast: UpdateAst, contract: PostgresContract, pim: ParamInde
|
|
|
805
848
|
}
|
|
806
849
|
|
|
807
850
|
function renderDelete(ast: DeleteAst, contract: PostgresContract, pim: ParamIndexMap): string {
|
|
808
|
-
const table =
|
|
851
|
+
const table = qualifyTableFromNamespaceCoordinate(ast.table, contract);
|
|
809
852
|
const whereClause = ast.where ? ` WHERE ${renderWhere(ast.where, contract, pim)}` : '';
|
|
810
853
|
const returningClause = ast.returning?.length
|
|
811
854
|
? ` RETURNING ${renderReturning(ast.returning, contract, pim)}`
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter-H8BiuXdq.mjs","names":[],"sources":["../src/core/adapter.ts"],"sourcesContent":["import type { CodecLookup } from '@prisma-next/framework-components/codec';\nimport { APP_SPACE_ID } from '@prisma-next/framework-components/control';\nimport type {\n Adapter,\n AdapterProfile,\n AnyQueryAst,\n LowererContext,\n MarkerReadResult,\n RawSqlLiteral,\n SqlQueryable,\n} from '@prisma-next/sql-relational-core/ast';\nimport type { RawCodecInferer } from '@prisma-next/sql-relational-core/expression';\nimport { parseContractMarkerRow } from '@prisma-next/sql-runtime';\nimport { createPostgresBuiltinCodecLookup } from './codec-lookup';\nimport { renderLoweredSql } from './sql-renderer';\nimport type { PostgresAdapterOptions, PostgresContract, PostgresLoweredStatement } from './types';\n\nconst defaultCapabilities = Object.freeze({\n postgres: {\n orderBy: true,\n limit: true,\n lateral: true,\n jsonAgg: true,\n returning: true,\n distinctOn: true,\n },\n sql: {\n enums: true,\n returning: true,\n defaultInInsert: true,\n lateral: true,\n },\n});\n\nclass PostgresAdapterImpl\n implements Adapter<AnyQueryAst, PostgresContract, PostgresLoweredStatement>\n{\n // These fields make the adapter instance structurally compatible with RuntimeAdapterInstance<'sql', 'postgres'> without introducing a runtime-plane dependency.\n readonly familyId = 'sql' as const;\n readonly targetId = 'postgres' as const;\n\n readonly profile: AdapterProfile<'postgres'>;\n private readonly codecLookup: CodecLookup;\n\n constructor(options?: PostgresAdapterOptions) {\n this.codecLookup = options?.codecLookup ?? createPostgresBuiltinCodecLookup();\n this.profile = Object.freeze({\n id: options?.profileId ?? 'postgres/default@1',\n target: 'postgres',\n capabilities: defaultCapabilities,\n readMarker: (queryable: SqlQueryable) => readPostgresMarker(queryable),\n });\n }\n\n lower(ast: AnyQueryAst, context: LowererContext<PostgresContract>): PostgresLoweredStatement {\n return renderLoweredSql(ast, context.contract, this.codecLookup);\n }\n}\n\n/** Codec-id lookup for bare-literal interpolations used by `fns.raw` on a postgres client. Contributed as the descriptor's static `rawCodecInferer` slot. */\nexport const postgresRawCodecInferer: RawCodecInferer = {\n inferCodec(value: RawSqlLiteral): string {\n switch (typeof value) {\n case 'number':\n return Number.isSafeInteger(value) && value % 1 === 0 ? 'pg/int4' : 'pg/float8';\n case 'bigint':\n return 'pg/int8';\n case 'string':\n return 'pg/text';\n case 'boolean':\n return 'pg/bool';\n case 'object':\n if (value instanceof Uint8Array) return 'pg/bytea';\n }\n throw new Error(\n 'unsupported JS value type for raw-SQL interpolation: wrap this value in `param(...)` with an explicit codec',\n );\n },\n};\n\nasync function readPostgresMarker(queryable: SqlQueryable): Promise<MarkerReadResult> {\n const exists = await queryable.query(\n 'select 1 from information_schema.tables where table_schema = $1 and table_name = $2',\n ['prisma_contract', 'marker'],\n );\n if (exists.rows.length === 0) {\n return { kind: 'no-table' };\n }\n\n const result = await queryable.query(\n 'select core_hash, profile_hash, contract_json, canonical_version, updated_at, app_tag, meta, invariants from prisma_contract.marker where space = $1',\n [APP_SPACE_ID],\n );\n const row = result.rows[0];\n if (!row) {\n return { kind: 'absent' };\n }\n // Postgres' driver hydrates `text[]` columns as native JS arrays, so the row is already in the shape the shared parser expects.\n return { kind: 'present', record: parseContractMarkerRow(row) };\n}\n\nexport function createPostgresAdapter(options?: PostgresAdapterOptions) {\n return Object.freeze(new PostgresAdapterImpl(options));\n}\n"],"mappings":";;;;AAiBA,MAAM,sBAAsB,OAAO,OAAO;CACxC,UAAU;EACR,SAAS;EACT,OAAO;EACP,SAAS;EACT,SAAS;EACT,WAAW;EACX,YAAY;CACd;CACA,KAAK;EACH,OAAO;EACP,WAAW;EACX,iBAAiB;EACjB,SAAS;CACX;AACF,CAAC;AAED,IAAM,sBAAN,MAEA;CAEE,WAAoB;CACpB,WAAoB;CAEpB;CACA;CAEA,YAAY,SAAkC;EAC5C,KAAK,cAAc,SAAS,eAAe,iCAAiC;EAC5E,KAAK,UAAU,OAAO,OAAO;GAC3B,IAAI,SAAS,aAAa;GAC1B,QAAQ;GACR,cAAc;GACd,aAAa,cAA4B,mBAAmB,SAAS;EACvE,CAAC;CACH;CAEA,MAAM,KAAkB,SAAqE;EAC3F,OAAO,iBAAiB,KAAK,QAAQ,UAAU,KAAK,WAAW;CACjE;AACF;;AAGA,MAAa,0BAA2C,EACtD,WAAW,OAA8B;CACvC,QAAQ,OAAO,OAAf;EACE,KAAK,UACH,OAAO,OAAO,cAAc,KAAK,KAAK,QAAQ,MAAM,IAAI,YAAY;EACtE,KAAK,UACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,KAAK,UACH,IAAI,iBAAiB,YAAY,OAAO;CAC5C;CACA,MAAM,IAAI,MACR,6GACF;AACF,EACF;AAEA,eAAe,mBAAmB,WAAoD;CAKpF,KAAI,MAJiB,UAAU,MAC7B,uFACA,CAAC,mBAAmB,QAAQ,CAC9B,GACW,KAAK,WAAW,GACzB,OAAO,EAAE,MAAM,WAAW;CAO5B,MAAM,OAAM,MAJS,UAAU,MAC7B,wJACA,CAAC,YAAY,CACf,GACmB,KAAK;CACxB,IAAI,CAAC,KACH,OAAO,EAAE,MAAM,SAAS;CAG1B,OAAO;EAAE,MAAM;EAAW,QAAQ,uBAAuB,GAAG;CAAE;AAChE;AAEA,SAAgB,sBAAsB,SAAkC;CACtE,OAAO,OAAO,OAAO,IAAI,oBAAoB,OAAO,CAAC;AACvD"}
|