@prisma-next/family-sql 0.4.0-dev.8 → 0.4.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/dist/control.d.mts +2 -2
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +21 -9
- package/dist/control.mjs.map +1 -1
- package/dist/migration.d.mts +36 -0
- package/dist/migration.d.mts.map +1 -0
- package/dist/migration.mjs +35 -0
- package/dist/migration.mjs.map +1 -0
- package/dist/schema-verify.d.mts +1 -1
- package/dist/{types-CH9zsNrU.d.mts → types-C6K4mxDM.d.mts} +33 -3
- package/dist/types-C6K4mxDM.d.mts.map +1 -0
- package/dist/{verify-DZHtfcmj.mjs → verify-4GshvY4p.mjs} +25 -11
- package/dist/verify-4GshvY4p.mjs.map +1 -0
- package/dist/verify-sql-schema-Ovz7RXR5.mjs.map +1 -1
- package/dist/verify.d.mts.map +1 -1
- package/dist/verify.mjs +1 -1
- package/package.json +19 -18
- package/src/core/migrations/contract-to-schema-ir.ts +45 -5
- package/src/core/migrations/types.ts +35 -0
- package/src/core/sql-migration.ts +33 -0
- package/src/core/verify.ts +50 -32
- package/src/exports/control.ts +2 -0
- package/src/exports/migration.ts +1 -0
- package/dist/operation-descriptors.d.mts +0 -380
- package/dist/operation-descriptors.d.mts.map +0 -1
- package/dist/operation-descriptors.mjs +0 -294
- package/dist/operation-descriptors.mjs.map +0 -1
- package/dist/types-CH9zsNrU.d.mts.map +0 -1
- package/dist/verify-DZHtfcmj.mjs.map +0 -1
- package/src/core/migrations/descriptor-schemas.ts +0 -172
- package/src/core/migrations/operation-descriptors.ts +0 -213
- package/src/exports/operation-descriptors.ts +0 -52
package/package.json
CHANGED
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/family-sql",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"description": "SQL family descriptor for Prisma Next",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"arktype": "^2.0.0",
|
|
9
|
-
"@prisma-next/
|
|
10
|
-
"@prisma-next/
|
|
11
|
-
"@prisma-next/framework-components": "0.4.
|
|
12
|
-
"@prisma-next/operations": "0.4.
|
|
13
|
-
"@prisma-next/
|
|
14
|
-
"@prisma-next/sql-contract-
|
|
15
|
-
"@prisma-next/sql-
|
|
16
|
-
"@prisma-next/sql-
|
|
17
|
-
"@prisma-next/sql-
|
|
18
|
-
"@prisma-next/sql-
|
|
19
|
-
"@prisma-next/
|
|
20
|
-
"@prisma-next/sql-
|
|
21
|
-
"@prisma-next/utils": "0.4.
|
|
9
|
+
"@prisma-next/emitter": "0.4.1",
|
|
10
|
+
"@prisma-next/contract": "0.4.1",
|
|
11
|
+
"@prisma-next/framework-components": "0.4.1",
|
|
12
|
+
"@prisma-next/operations": "0.4.1",
|
|
13
|
+
"@prisma-next/migration-tools": "0.4.1",
|
|
14
|
+
"@prisma-next/sql-contract-ts": "0.4.1",
|
|
15
|
+
"@prisma-next/sql-operations": "0.4.1",
|
|
16
|
+
"@prisma-next/sql-runtime": "0.4.1",
|
|
17
|
+
"@prisma-next/sql-contract": "0.4.1",
|
|
18
|
+
"@prisma-next/sql-relational-core": "0.4.1",
|
|
19
|
+
"@prisma-next/sql-schema-ir": "0.4.1",
|
|
20
|
+
"@prisma-next/sql-contract-emitter": "0.4.1",
|
|
21
|
+
"@prisma-next/utils": "0.4.1",
|
|
22
|
+
"@prisma-next/runtime-executor": "0.4.1"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"tsdown": "0.18.4",
|
|
25
26
|
"typescript": "5.9.3",
|
|
26
27
|
"vitest": "4.0.17",
|
|
27
|
-
"@prisma-next/driver-postgres": "0.4.
|
|
28
|
-
"@prisma-next/psl-parser": "0.4.
|
|
28
|
+
"@prisma-next/driver-postgres": "0.4.1",
|
|
29
|
+
"@prisma-next/psl-parser": "0.4.1",
|
|
30
|
+
"@prisma-next/sql-contract-psl": "0.4.1",
|
|
29
31
|
"@prisma-next/test-utils": "0.0.1",
|
|
30
|
-
"@prisma-next/sql-contract-psl": "0.4.0-dev.8",
|
|
31
32
|
"@prisma-next/tsdown": "0.0.0",
|
|
32
33
|
"@prisma-next/tsconfig": "0.0.0"
|
|
33
34
|
},
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
"exports": {
|
|
39
40
|
"./control": "./dist/control.mjs",
|
|
40
41
|
"./control-adapter": "./dist/control-adapter.mjs",
|
|
41
|
-
"./
|
|
42
|
+
"./migration": "./dist/migration.mjs",
|
|
42
43
|
"./pack": "./dist/pack.mjs",
|
|
43
44
|
"./runtime": "./dist/runtime.mjs",
|
|
44
45
|
"./schema-verify": "./dist/schema-verify.mjs",
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
SqlStorage,
|
|
8
8
|
StorageColumn,
|
|
9
9
|
StorageTable,
|
|
10
|
+
StorageTypeInstance,
|
|
10
11
|
UniqueConstraint,
|
|
11
12
|
} from '@prisma-next/sql-contract/types';
|
|
12
13
|
import { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';
|
|
@@ -53,16 +54,26 @@ export type DefaultRenderer = (def: ColumnDefault, column: StorageColumn) => str
|
|
|
53
54
|
function convertColumn(
|
|
54
55
|
name: string,
|
|
55
56
|
column: StorageColumn,
|
|
57
|
+
storageTypes: Record<string, StorageTypeInstance>,
|
|
56
58
|
expandNativeType: NativeTypeExpander | undefined,
|
|
57
59
|
renderDefault: DefaultRenderer | undefined,
|
|
58
60
|
): SqlColumnIR {
|
|
61
|
+
// Resolve `typeRef` so columns that delegate their `nativeType`/`codecId`/
|
|
62
|
+
// `typeParams` to a named `storage.types` entry expand the same way as
|
|
63
|
+
// columns that inline those fields. Without this resolution, a
|
|
64
|
+
// `typeRef`-based column like `post.embedding → Embedding1536` would
|
|
65
|
+
// render as the bare `"vector"` (dropping the `length` parameter), while
|
|
66
|
+
// `verify-sql-schema.ts`'s `renderExpectedNativeType` resolves the
|
|
67
|
+
// typeRef and produces `"vector(1536)"` — making diffs on the same
|
|
68
|
+
// contract falsely report a `type_mismatch`.
|
|
69
|
+
const resolved = resolveColumnTypeMetadata(column, storageTypes);
|
|
59
70
|
const nativeType = expandNativeType
|
|
60
71
|
? expandNativeType({
|
|
61
|
-
nativeType:
|
|
62
|
-
codecId:
|
|
63
|
-
...ifDefined('typeParams',
|
|
72
|
+
nativeType: resolved.nativeType,
|
|
73
|
+
codecId: resolved.codecId,
|
|
74
|
+
...ifDefined('typeParams', resolved.typeParams),
|
|
64
75
|
})
|
|
65
|
-
:
|
|
76
|
+
: resolved.nativeType;
|
|
66
77
|
return {
|
|
67
78
|
name,
|
|
68
79
|
nativeType,
|
|
@@ -74,6 +85,26 @@ function convertColumn(
|
|
|
74
85
|
};
|
|
75
86
|
}
|
|
76
87
|
|
|
88
|
+
function resolveColumnTypeMetadata(
|
|
89
|
+
column: StorageColumn,
|
|
90
|
+
storageTypes: Record<string, StorageTypeInstance>,
|
|
91
|
+
): Pick<StorageColumn, 'codecId' | 'nativeType' | 'typeParams'> {
|
|
92
|
+
if (!column.typeRef) {
|
|
93
|
+
return column;
|
|
94
|
+
}
|
|
95
|
+
const referenced = storageTypes[column.typeRef];
|
|
96
|
+
if (!referenced) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`Column references storage type "${column.typeRef}" but it is not defined in storage.types.`,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
codecId: referenced.codecId,
|
|
103
|
+
nativeType: referenced.nativeType,
|
|
104
|
+
typeParams: referenced.typeParams,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
77
108
|
function convertUnique(unique: UniqueConstraint): SqlUniqueIR {
|
|
78
109
|
return {
|
|
79
110
|
columns: unique.columns,
|
|
@@ -101,12 +132,19 @@ function convertForeignKey(fk: ForeignKey): SqlForeignKeyIR {
|
|
|
101
132
|
function convertTable(
|
|
102
133
|
name: string,
|
|
103
134
|
table: StorageTable,
|
|
135
|
+
storageTypes: Record<string, StorageTypeInstance>,
|
|
104
136
|
expandNativeType: NativeTypeExpander | undefined,
|
|
105
137
|
renderDefault: DefaultRenderer | undefined,
|
|
106
138
|
): SqlTableIR {
|
|
107
139
|
const columns: Record<string, SqlColumnIR> = {};
|
|
108
140
|
for (const [colName, colDef] of Object.entries(table.columns)) {
|
|
109
|
-
columns[colName] = convertColumn(
|
|
141
|
+
columns[colName] = convertColumn(
|
|
142
|
+
colName,
|
|
143
|
+
colDef,
|
|
144
|
+
storageTypes,
|
|
145
|
+
expandNativeType,
|
|
146
|
+
renderDefault,
|
|
147
|
+
);
|
|
110
148
|
}
|
|
111
149
|
|
|
112
150
|
const satisfiedIndexColumns = new Set([
|
|
@@ -217,11 +255,13 @@ export function contractToSchemaIR(
|
|
|
217
255
|
}
|
|
218
256
|
|
|
219
257
|
const storage = contract.storage;
|
|
258
|
+
const storageTypes = storage.types ?? {};
|
|
220
259
|
const tables: Record<string, SqlTableIR> = {};
|
|
221
260
|
for (const [tableName, tableDef] of Object.entries(storage.tables)) {
|
|
222
261
|
tables[tableName] = convertTable(
|
|
223
262
|
tableName,
|
|
224
263
|
tableDef,
|
|
264
|
+
storageTypes,
|
|
225
265
|
options.expandNativeType,
|
|
226
266
|
options.renderDefault,
|
|
227
267
|
);
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
ControlAdapterDescriptor,
|
|
5
5
|
ControlDriverInstance,
|
|
6
6
|
ControlExtensionDescriptor,
|
|
7
|
+
DataTransformOperation,
|
|
7
8
|
MigratableTargetDescriptor,
|
|
8
9
|
MigrationOperationPolicy,
|
|
9
10
|
MigrationPlan,
|
|
@@ -141,6 +142,18 @@ export interface SqlMigrationPlanOperationStep {
|
|
|
141
142
|
readonly meta?: AnyRecord;
|
|
142
143
|
}
|
|
143
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Minimal shape every SQL-family target must conform to for its per-operation
|
|
147
|
+
* `target.details` payload. Each SQL operation addresses a named database
|
|
148
|
+
* object in some schema; targets (Postgres, MySQL, SQLite, …) extend this
|
|
149
|
+
* shape with their own fields (e.g. Postgres adds `objectType` and optional
|
|
150
|
+
* `table`).
|
|
151
|
+
*/
|
|
152
|
+
export interface SqlPlanTargetDetails {
|
|
153
|
+
readonly schema: string;
|
|
154
|
+
readonly name: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
144
157
|
export interface SqlMigrationPlanOperationTarget<TTargetDetails> {
|
|
145
158
|
readonly id: string;
|
|
146
159
|
readonly details?: TTargetDetails;
|
|
@@ -155,6 +168,19 @@ export interface SqlMigrationPlanOperation<TTargetDetails> extends MigrationPlan
|
|
|
155
168
|
readonly meta?: AnyRecord;
|
|
156
169
|
}
|
|
157
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Union of all operation shapes a SQL-family migration may emit: schema-facing
|
|
173
|
+
* `SqlMigrationPlanOperation`s and family-agnostic `DataTransformOperation`s.
|
|
174
|
+
*
|
|
175
|
+
* Mirrors `AnyMongoMigrationOperation` in shape — the runner already handles
|
|
176
|
+
* both branches via `isDataTransformOperation`, and authored `migration.ts`
|
|
177
|
+
* files must be able to intermix `dataTransform(endContract, …)` calls with
|
|
178
|
+
* DDL factory calls (e.g. `setNotNull(…)`) in a single `operations` array.
|
|
179
|
+
*/
|
|
180
|
+
export type AnySqlMigrationOperation<TTargetDetails> =
|
|
181
|
+
| SqlMigrationPlanOperation<TTargetDetails>
|
|
182
|
+
| DataTransformOperation;
|
|
183
|
+
|
|
158
184
|
export interface SqlMigrationPlanContractInfo {
|
|
159
185
|
readonly storageHash: string;
|
|
160
186
|
readonly profileHash?: string;
|
|
@@ -216,6 +242,15 @@ export interface SqlMigrationPlannerPlanOptions {
|
|
|
216
242
|
readonly schema: SqlSchemaIR;
|
|
217
243
|
readonly policy: MigrationOperationPolicy;
|
|
218
244
|
readonly schemaName?: string;
|
|
245
|
+
/**
|
|
246
|
+
* The "from" contract (state the planner assumes the database starts at).
|
|
247
|
+
* Only `migration plan` supplies this; `db update` / `db init` reconcile
|
|
248
|
+
* against the live schema with no old contract. Strategies that need
|
|
249
|
+
* from/to column-shape comparisons (unsafe type change, nullability
|
|
250
|
+
* tightening) use this to decide whether to emit `dataTransform`
|
|
251
|
+
* placeholders.
|
|
252
|
+
*/
|
|
253
|
+
readonly fromContract?: Contract<SqlStorage> | null;
|
|
219
254
|
/**
|
|
220
255
|
* Active framework components participating in this composition.
|
|
221
256
|
* SQL targets can interpret this list to derive database dependencies.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Migration } from '@prisma-next/migration-tools/migration';
|
|
2
|
+
import type { AnySqlMigrationOperation, SqlPlanTargetDetails } from './migrations/types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Family-owned base class for SQL migrations.
|
|
6
|
+
*
|
|
7
|
+
* Parameterized on the target-details shape because SQL-family targets
|
|
8
|
+
* (Postgres, MySQL, SQLite, …) each carry their own `target.details` payload
|
|
9
|
+
* on `SqlMigrationPlanOperation`. The type parameter is narrowed to
|
|
10
|
+
* `SqlPlanTargetDetails` so every target-specific shape must at minimum
|
|
11
|
+
* identify the object being targeted (schema + name); concrete targets
|
|
12
|
+
* extend the shape with their own fields.
|
|
13
|
+
*
|
|
14
|
+
* Each concrete target-side subclass (e.g. Postgres's
|
|
15
|
+
* `TypeScriptRenderablePostgresMigration`) fixes `targetId` to its own
|
|
16
|
+
* target-id string literal, since SQL can't hardcode a single `targetId`:
|
|
17
|
+
* `targetId` is a target-level identity, not a family-level one, and
|
|
18
|
+
* belongs on the subclass.
|
|
19
|
+
*
|
|
20
|
+
* `familyId` is intentionally not declared here. The SQL family has no
|
|
21
|
+
* family-scoped runtime identity today — consumers reach the family via
|
|
22
|
+
* target descriptors rather than by family-id lookup, so adding one would
|
|
23
|
+
* be purely decorative. Introducing it later is a non-breaking superset.
|
|
24
|
+
*
|
|
25
|
+
* The operation type parameter is `AnySqlMigrationOperation<TDetails>` — the
|
|
26
|
+
* union of DDL-shaped `SqlMigrationPlanOperation` and `DataTransformOperation`
|
|
27
|
+
* — so subclasses can return a mix of schema operations (e.g. `setNotNull`,
|
|
28
|
+
* `addColumn`) and data-transform operations (e.g. `dataTransform`). Mirrors
|
|
29
|
+
* `MongoMigration`'s parameterization on `AnyMongoMigrationOperation`.
|
|
30
|
+
*/
|
|
31
|
+
export abstract class SqlMigration<TDetails extends SqlPlanTargetDetails> extends Migration<
|
|
32
|
+
AnySqlMigrationOperation<TDetails>
|
|
33
|
+
> {}
|
package/src/core/verify.ts
CHANGED
|
@@ -97,6 +97,27 @@ export function readMarkerSql(): { readonly sql: string; readonly params: readon
|
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Returns the SQL statement that probes for the existence of the marker table.
|
|
102
|
+
* Uses the SQL-standard `information_schema.tables` view so the query succeeds
|
|
103
|
+
* (returning zero rows) when the table has not been created yet — avoiding a
|
|
104
|
+
* `relation does not exist` error. Some Postgres wire-protocol implementations
|
|
105
|
+
* (e.g. PGlite's TCP proxy) do not fully recover from an extended-protocol
|
|
106
|
+
* parse error, so we probe first instead of relying on an error signal.
|
|
107
|
+
* @internal - Used internally by readMarker().
|
|
108
|
+
*/
|
|
109
|
+
export function markerTableExistsSql(): {
|
|
110
|
+
readonly sql: string;
|
|
111
|
+
readonly params: readonly unknown[];
|
|
112
|
+
} {
|
|
113
|
+
return {
|
|
114
|
+
sql: `select 1
|
|
115
|
+
from information_schema.tables
|
|
116
|
+
where table_schema = $1 and table_name = $2`,
|
|
117
|
+
params: ['prisma_contract', 'marker'],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
100
121
|
/**
|
|
101
122
|
* Reads the contract marker from the database using the provided driver.
|
|
102
123
|
* Returns the parsed marker record or null if no marker is found.
|
|
@@ -108,41 +129,38 @@ export function readMarkerSql(): { readonly sql: string; readonly params: readon
|
|
|
108
129
|
export async function readMarker(
|
|
109
130
|
driver: ControlDriverInstance<'sql', string>,
|
|
110
131
|
): Promise<ContractMarkerRecord | null> {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
meta: unknown | null;
|
|
122
|
-
}>(markerStatement.sql, markerStatement.params);
|
|
123
|
-
|
|
124
|
-
if (queryResult.rows.length === 0) {
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
132
|
+
// Probe for the marker table first so that a fresh database (no
|
|
133
|
+
// `prisma_contract` schema) returns null cleanly instead of surfacing a
|
|
134
|
+
// `relation does not exist` error. This keeps the control connection in a
|
|
135
|
+
// predictable state for driver implementations that are sensitive to
|
|
136
|
+
// extended-protocol parse errors.
|
|
137
|
+
const existsStatement = markerTableExistsSql();
|
|
138
|
+
const existsResult = await driver.query(existsStatement.sql, existsStatement.params);
|
|
139
|
+
if (existsResult.rows.length === 0) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
127
142
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
143
|
+
const markerStatement = readMarkerSql();
|
|
144
|
+
const queryResult = await driver.query<{
|
|
145
|
+
core_hash: string;
|
|
146
|
+
profile_hash: string;
|
|
147
|
+
contract_json: unknown | null;
|
|
148
|
+
canonical_version: number | null;
|
|
149
|
+
updated_at: Date | string;
|
|
150
|
+
app_tag: string | null;
|
|
151
|
+
meta: unknown | null;
|
|
152
|
+
}>(markerStatement.sql, markerStatement.params);
|
|
153
|
+
|
|
154
|
+
if (queryResult.rows.length === 0) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
133
157
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// PostgreSQL error code 42P01 = undefined_table
|
|
138
|
-
if (
|
|
139
|
-
error instanceof Error &&
|
|
140
|
-
(error.message.includes('does not exist') || (error as { code?: string }).code === '42P01')
|
|
141
|
-
) {
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
throw error;
|
|
158
|
+
const markerRow = queryResult.rows[0];
|
|
159
|
+
if (!markerRow) {
|
|
160
|
+
throw new Error('Database query returned unexpected result structure');
|
|
145
161
|
}
|
|
162
|
+
|
|
163
|
+
return parseContractMarkerRow(markerRow);
|
|
146
164
|
}
|
|
147
165
|
|
|
148
166
|
/**
|
package/src/exports/control.ts
CHANGED
|
@@ -33,6 +33,7 @@ export {
|
|
|
33
33
|
} from '../core/migrations/plan-helpers';
|
|
34
34
|
export { INIT_ADDITIVE_POLICY } from '../core/migrations/policies';
|
|
35
35
|
export type {
|
|
36
|
+
AnySqlMigrationOperation,
|
|
36
37
|
CodecControlHooks,
|
|
37
38
|
ComponentDatabaseDependencies,
|
|
38
39
|
ComponentDatabaseDependency,
|
|
@@ -62,6 +63,7 @@ export type {
|
|
|
62
63
|
SqlPlannerFailureResult,
|
|
63
64
|
SqlPlannerResult,
|
|
64
65
|
SqlPlannerSuccessResult,
|
|
66
|
+
SqlPlanTargetDetails,
|
|
65
67
|
StorageTypePlanResult,
|
|
66
68
|
} from '../core/migrations/types';
|
|
67
69
|
export { collectInitDependencies, isDatabaseDependencyProvider } from '../core/migrations/types';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SqlMigration as Migration } from '../core/sql-migration';
|