@prisma-next/target-sqlite 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/dist/{codec-ids-CYwMu3-4.d.mts → codec-ids-BfPkjMmk.d.mts} +1 -1
- package/dist/{codec-ids-CYwMu3-4.d.mts.map → codec-ids-BfPkjMmk.d.mts.map} +1 -1
- package/dist/{codec-ids-CuUxYcd0.mjs → codec-ids-DSU7S2Li.mjs} +1 -1
- package/dist/{codec-ids-CuUxYcd0.mjs.map → codec-ids-DSU7S2Li.mjs.map} +1 -1
- package/dist/codec-ids.d.mts +1 -1
- package/dist/codec-ids.mjs +1 -1
- package/dist/{codec-types-DNauB5UT.d.mts → codec-types-izdPhp_9.d.mts} +6 -7
- package/dist/codec-types-izdPhp_9.d.mts.map +1 -0
- package/dist/codec-types.d.mts +3 -3
- package/dist/{codecs-BAlEiSeP.d.mts → codecs-BGeJavlQ.d.mts} +16 -18
- package/dist/codecs-BGeJavlQ.d.mts.map +1 -0
- package/dist/{codecs-DVnHtVWW.mjs → codecs-DsC4OGmU.mjs} +2 -2
- package/dist/{codecs-DVnHtVWW.mjs.map → codecs-DsC4OGmU.mjs.map} +1 -1
- package/dist/codecs.d.mts +2 -2
- package/dist/codecs.mjs +1 -1
- package/dist/contract-free.d.mts +55 -0
- package/dist/contract-free.d.mts.map +1 -0
- package/dist/contract-free.mjs +111 -0
- package/dist/contract-free.mjs.map +1 -0
- package/dist/control-tables-7KwMyJ6i.mjs +12 -0
- package/dist/control-tables-7KwMyJ6i.mjs.map +1 -0
- package/dist/control-tables.d.mts +11 -0
- package/dist/control-tables.d.mts.map +1 -0
- package/dist/control-tables.mjs +2 -0
- package/dist/control.d.mts +31 -15
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +66 -124
- package/dist/control.mjs.map +1 -1
- package/dist/ddl.d.mts +2 -0
- package/dist/ddl.mjs +2 -0
- package/dist/{default-normalizer-3Fccw7yw.mjs → default-normalizer-DuoHj9-O.mjs} +1 -1
- package/dist/{default-normalizer-3Fccw7yw.mjs.map → default-normalizer-DuoHj9-O.mjs.map} +1 -1
- package/dist/default-normalizer.mjs +1 -1
- package/dist/descriptor-meta-Dxx2A6PT.mjs +17 -0
- package/dist/descriptor-meta-Dxx2A6PT.mjs.map +1 -0
- package/dist/descriptor-meta-runtime-BkXK3OjD.mjs +12 -0
- package/dist/descriptor-meta-runtime-BkXK3OjD.mjs.map +1 -0
- package/dist/migration.d.mts +2 -2
- package/dist/migration.mjs +3 -3
- package/dist/{native-type-normalizer-BlN5XfD-.mjs → native-type-normalizer-CiSyVmMP.mjs} +1 -1
- package/dist/{native-type-normalizer-BlN5XfD-.mjs.map → native-type-normalizer-CiSyVmMP.mjs.map} +1 -1
- package/dist/native-type-normalizer.mjs +1 -1
- package/dist/nodes-D0k4z7NL.mjs +33 -0
- package/dist/nodes-D0k4z7NL.mjs.map +1 -0
- package/dist/nodes-VzaaeUTb.d.mts +29 -0
- package/dist/nodes-VzaaeUTb.d.mts.map +1 -0
- package/dist/{op-factory-call-BnPhI25-.mjs → op-factory-call-DymqdXQW.mjs} +2 -2
- package/dist/{op-factory-call-BnPhI25-.mjs.map → op-factory-call-DymqdXQW.mjs.map} +1 -1
- package/dist/op-factory-call.d.mts +2 -2
- package/dist/op-factory-call.d.mts.map +1 -1
- package/dist/op-factory-call.mjs +1 -1
- package/dist/pack.d.mts +9 -7
- package/dist/pack.d.mts.map +1 -1
- package/dist/pack.mjs +1 -1
- package/dist/{planner-CEKTRydl.mjs → planner-DSNDwQy9.mjs} +9 -9
- package/dist/planner-DSNDwQy9.mjs.map +1 -0
- package/dist/{planner-produced-sqlite-migration-CI9LdXPr.d.mts → planner-produced-sqlite-migration-C1yqJAiM.d.mts} +3 -3
- package/dist/{planner-produced-sqlite-migration-CI9LdXPr.d.mts.map → planner-produced-sqlite-migration-C1yqJAiM.d.mts.map} +1 -1
- package/dist/{planner-produced-sqlite-migration-DCsg3RDZ.mjs → planner-produced-sqlite-migration-DowV_vHw.mjs} +3 -3
- package/dist/{planner-produced-sqlite-migration-DCsg3RDZ.mjs.map → planner-produced-sqlite-migration-DowV_vHw.mjs.map} +1 -1
- package/dist/planner-produced-sqlite-migration.d.mts +1 -1
- package/dist/planner-produced-sqlite-migration.mjs +1 -1
- package/dist/{planner-target-details-Bm71XPKb.mjs → planner-target-details-H8z9TFDg.mjs} +1 -1
- package/dist/{planner-target-details-Bm71XPKb.mjs.map → planner-target-details-H8z9TFDg.mjs.map} +1 -1
- package/dist/{planner-target-details-vhvZDWK1.d.mts → planner-target-details-xR6UfIcz.d.mts} +1 -1
- package/dist/{planner-target-details-vhvZDWK1.d.mts.map → planner-target-details-xR6UfIcz.d.mts.map} +1 -1
- package/dist/planner-target-details.d.mts +1 -1
- package/dist/planner-target-details.mjs +1 -1
- package/dist/planner.d.mts +2 -2
- package/dist/planner.d.mts.map +1 -1
- package/dist/planner.mjs +1 -1
- package/dist/{render-ops-CSRDT4YL.mjs → render-ops-CFRbJ3Yb.mjs} +1 -1
- package/dist/{render-ops-CSRDT4YL.mjs.map → render-ops-CFRbJ3Yb.mjs.map} +1 -1
- package/dist/render-ops.d.mts +1 -1
- package/dist/render-ops.mjs +1 -1
- package/dist/runtime.d.mts +17 -1
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +4 -3
- package/dist/runtime.mjs.map +1 -1
- package/dist/{shared-qLsgTOZs.d.mts → shared-Dhc8mLK1.d.mts} +2 -2
- package/dist/{shared-qLsgTOZs.d.mts.map → shared-Dhc8mLK1.d.mts.map} +1 -1
- package/dist/{sql-utils-DhevMgef.mjs → sql-utils-CV8Bdgtc.mjs} +1 -1
- package/dist/{sql-utils-DhevMgef.mjs.map → sql-utils-CV8Bdgtc.mjs.map} +1 -1
- package/dist/sql-utils.mjs +1 -1
- package/dist/sqlite-contract-serializer-jcRu8aHh.mjs +101 -0
- package/dist/sqlite-contract-serializer-jcRu8aHh.mjs.map +1 -0
- package/dist/{sqlite-migration-BBJktVVw.mjs → sqlite-migration-CUqgmzQH.mjs} +1 -1
- package/dist/{sqlite-migration-BBJktVVw.mjs.map → sqlite-migration-CUqgmzQH.mjs.map} +1 -1
- package/dist/{sqlite-migration-DAb2NEX6.d.mts → sqlite-migration-D4XGYzgQ.d.mts} +2 -2
- package/dist/{sqlite-migration-DAb2NEX6.d.mts.map → sqlite-migration-D4XGYzgQ.d.mts.map} +1 -1
- package/dist/{tables-DGRRJasz.mjs → tables-CjB7vXCr.mjs} +5 -11
- package/dist/tables-CjB7vXCr.mjs.map +1 -0
- package/package.json +23 -21
- package/src/contract-free/columns.ts +44 -0
- package/src/contract-free/control-bootstrap.ts +54 -0
- package/src/contract-free/ddl.ts +26 -0
- package/src/core/authoring.ts +1 -1
- package/src/core/control-tables.ts +11 -0
- package/src/core/control-target.ts +4 -6
- package/src/core/ddl/nodes.ts +54 -0
- package/src/core/descriptor-meta-runtime.ts +28 -0
- package/src/core/descriptor-meta.ts +4 -6
- package/src/core/migrations/issue-planner.ts +1 -1
- package/src/core/migrations/operations/shared.ts +1 -8
- package/src/core/migrations/planner-strategies.ts +1 -1
- package/src/core/migrations/runner.ts +78 -83
- package/src/core/runtime-target.ts +2 -2
- package/src/core/sqlite-contract-serializer.ts +21 -9
- package/src/core/sqlite-unbound-database.ts +113 -26
- package/src/exports/contract-free.ts +6 -0
- package/src/exports/control-tables.ts +5 -0
- package/src/exports/ddl.ts +6 -0
- package/src/exports/runtime.ts +1 -0
- package/dist/codec-types-DNauB5UT.d.mts.map +0 -1
- package/dist/codecs-BAlEiSeP.d.mts.map +0 -1
- package/dist/descriptor-meta-CE2Kbn9b.mjs +0 -17
- package/dist/descriptor-meta-CE2Kbn9b.mjs.map +0 -1
- package/dist/planner-CEKTRydl.mjs.map +0 -1
- package/dist/statement-builders-Dne-LkAV.mjs +0 -158
- package/dist/statement-builders-Dne-LkAV.mjs.map +0 -1
- package/dist/statement-builders.d.mts +0 -68
- package/dist/statement-builders.d.mts.map +0 -1
- package/dist/statement-builders.mjs +0 -2
- package/dist/tables-DGRRJasz.mjs.map +0 -1
- package/src/core/migrations/statement-builders.ts +0 -212
- package/src/exports/statement-builders.ts +0 -12
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { ColumnDefault, Contract } from '@prisma-next/contract/types';
|
|
2
|
-
import type {
|
|
3
|
-
SqlControlFamilyInstance,
|
|
4
|
-
SqlControlTargetDescriptor,
|
|
5
|
-
} from '@prisma-next/family-sql/control';
|
|
2
|
+
import type { SqlControlTargetDescriptor } from '@prisma-next/family-sql/control';
|
|
6
3
|
import { contractToSchemaIR } from '@prisma-next/family-sql/control';
|
|
4
|
+
import type { SqlControlAdapter } from '@prisma-next/family-sql/control-adapter';
|
|
7
5
|
import type {
|
|
8
6
|
ControlTargetInstance,
|
|
9
7
|
MigrationPlanner,
|
|
@@ -38,7 +36,7 @@ const sqliteControlTargetDescriptor: SqlControlTargetDescriptor<'sqlite', Sqlite
|
|
|
38
36
|
contractSerializer: new SqliteContractSerializer(),
|
|
39
37
|
schemaVerifier: new SqliteSchemaVerifier(),
|
|
40
38
|
migrations: {
|
|
41
|
-
createPlanner(
|
|
39
|
+
createPlanner(_adapter: SqlControlAdapter<'sqlite'>): MigrationPlanner<'sql', 'sqlite'> {
|
|
42
40
|
return createSqliteMigrationPlanner();
|
|
43
41
|
},
|
|
44
42
|
createRunner(family) {
|
|
@@ -69,7 +67,7 @@ const sqliteControlTargetDescriptor: SqlControlTargetDescriptor<'sqlite', Sqlite
|
|
|
69
67
|
targetId: 'sqlite',
|
|
70
68
|
};
|
|
71
69
|
},
|
|
72
|
-
createPlanner(
|
|
70
|
+
createPlanner(_adapter: SqlControlAdapter<'sqlite'>) {
|
|
73
71
|
return createSqliteMigrationPlanner();
|
|
74
72
|
},
|
|
75
73
|
createRunner(family) {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type DdlColumn,
|
|
3
|
+
DdlNode,
|
|
4
|
+
type DdlTableConstraint,
|
|
5
|
+
} from '@prisma-next/sql-relational-core/ast';
|
|
6
|
+
|
|
7
|
+
export interface SqliteDdlVisitor<R> {
|
|
8
|
+
createTable(node: SqliteCreateTable): R;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export abstract class SqliteDdlNode extends DdlNode {
|
|
12
|
+
abstract accept<R>(visitor: SqliteDdlVisitor<R>): R;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function freezeDdlColumns(columns: readonly DdlColumn[]): ReadonlyArray<DdlColumn> {
|
|
16
|
+
return Object.freeze([...columns]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function freezeConstraints(
|
|
20
|
+
constraints: readonly DdlTableConstraint[] | undefined,
|
|
21
|
+
): ReadonlyArray<DdlTableConstraint> | undefined {
|
|
22
|
+
return constraints ? Object.freeze([...constraints]) : undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class SqliteCreateTable extends SqliteDdlNode {
|
|
26
|
+
readonly kind = 'create-table' as const;
|
|
27
|
+
readonly table: string;
|
|
28
|
+
readonly schema: string | undefined;
|
|
29
|
+
readonly ifNotExists: boolean | undefined;
|
|
30
|
+
readonly columns: ReadonlyArray<DdlColumn>;
|
|
31
|
+
readonly constraints: ReadonlyArray<DdlTableConstraint> | undefined;
|
|
32
|
+
|
|
33
|
+
constructor(options: {
|
|
34
|
+
readonly table: string;
|
|
35
|
+
readonly schema?: string;
|
|
36
|
+
readonly ifNotExists?: boolean;
|
|
37
|
+
readonly columns: readonly DdlColumn[];
|
|
38
|
+
readonly constraints?: readonly DdlTableConstraint[];
|
|
39
|
+
}) {
|
|
40
|
+
super();
|
|
41
|
+
this.table = options.table;
|
|
42
|
+
this.schema = options.schema;
|
|
43
|
+
this.ifNotExists = options.ifNotExists;
|
|
44
|
+
this.columns = freezeDdlColumns(options.columns);
|
|
45
|
+
this.constraints = freezeConstraints(options.constraints);
|
|
46
|
+
this.freeze();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
override accept<R>(visitor: SqliteDdlVisitor<R>): R {
|
|
50
|
+
return visitor.createTable(this);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type AnySqliteDdlNode = SqliteCreateTable;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Runtime-safe slice of the sqlite target descriptor metadata.
|
|
2
|
+
//
|
|
3
|
+
// This file exists separately from ./descriptor-meta on purpose: the runtime
|
|
4
|
+
// plane reads only `kind/familyId/targetId/id/version/capabilities` (plus the
|
|
5
|
+
// `__codecTypes` phantom). The `authoring` slot lives on the pack/control
|
|
6
|
+
// descriptor only, because authoring contributions are consumed at
|
|
7
|
+
// contract-construction time by `assembleAuthoringContributions` (control
|
|
8
|
+
// plane) and the PSL interpreter — never at runtime.
|
|
9
|
+
//
|
|
10
|
+
// Keeping the runtime closure free of the `./authoring` import is what lets
|
|
11
|
+
// the bundler tree-shake `@prisma-next/family-sql/control` (and its
|
|
12
|
+
// transitive `verify-sql-schema` chunk) out of the runtime entry. Do not
|
|
13
|
+
// add an `authoring` field here — if you need to, the pack/control meta in
|
|
14
|
+
// `./descriptor-meta` is the right place. See TML-2766 for context.
|
|
15
|
+
import type { CodecTypes } from '../exports/codec-types';
|
|
16
|
+
|
|
17
|
+
const sqliteTargetDescriptorMetaRuntimeBase = {
|
|
18
|
+
kind: 'target',
|
|
19
|
+
familyId: 'sql',
|
|
20
|
+
targetId: 'sqlite',
|
|
21
|
+
id: 'sqlite',
|
|
22
|
+
version: '0.0.1',
|
|
23
|
+
capabilities: {},
|
|
24
|
+
} as const;
|
|
25
|
+
|
|
26
|
+
export const sqliteTargetDescriptorMetaRuntime: typeof sqliteTargetDescriptorMetaRuntimeBase & {
|
|
27
|
+
readonly __codecTypes?: CodecTypes;
|
|
28
|
+
} = sqliteTargetDescriptorMetaRuntimeBase;
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
+
import { UNBOUND_NAMESPACE_ID } from '@prisma-next/framework-components/ir';
|
|
1
2
|
import type { CodecTypes } from '../exports/codec-types';
|
|
2
3
|
import { sqliteAuthoringFieldPresets } from './authoring';
|
|
4
|
+
import { sqliteTargetDescriptorMetaRuntime } from './descriptor-meta-runtime';
|
|
3
5
|
|
|
4
6
|
const sqliteTargetDescriptorMetaBase = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
targetId: 'sqlite',
|
|
8
|
-
id: 'sqlite',
|
|
9
|
-
version: '0.0.1',
|
|
10
|
-
capabilities: {},
|
|
7
|
+
...sqliteTargetDescriptorMetaRuntime,
|
|
8
|
+
defaultNamespaceId: UNBOUND_NAMESPACE_ID,
|
|
11
9
|
authoring: {
|
|
12
10
|
field: sqliteAuthoringFieldPresets,
|
|
13
11
|
},
|
|
@@ -28,6 +28,7 @@ import { defaultIndexName } from '@prisma-next/sql-schema-ir/naming';
|
|
|
28
28
|
import type { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';
|
|
29
29
|
import type { Result } from '@prisma-next/utils/result';
|
|
30
30
|
import { notOk, ok } from '@prisma-next/utils/result';
|
|
31
|
+
import { CONTROL_TABLE_NAMES } from '../control-tables';
|
|
31
32
|
import {
|
|
32
33
|
AddColumnCall,
|
|
33
34
|
CreateIndexCall,
|
|
@@ -55,7 +56,6 @@ import {
|
|
|
55
56
|
sqlitePlannerStrategies,
|
|
56
57
|
tableAt,
|
|
57
58
|
} from './planner-strategies';
|
|
58
|
-
import { CONTROL_TABLE_NAMES } from './statement-builders';
|
|
59
59
|
|
|
60
60
|
export type { CallMigrationStrategy, StrategyContext };
|
|
61
61
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SqlMigrationPlanOperation } from '@prisma-next/family-sql/control';
|
|
2
|
+
import { REFERENTIAL_ACTION_SQL } from '@prisma-next/sql-contract/referential-action-sql';
|
|
2
3
|
import type { ReferentialAction } from '@prisma-next/sql-contract/types';
|
|
3
4
|
import { quoteIdentifier } from '../../sql-utils';
|
|
4
5
|
import type { SqlitePlanTargetDetails } from '../planner-target-details';
|
|
@@ -75,14 +76,6 @@ export interface SqliteIndexSpec {
|
|
|
75
76
|
readonly columns: readonly string[];
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
const REFERENTIAL_ACTION_SQL: Record<ReferentialAction, string> = {
|
|
79
|
-
noAction: 'NO ACTION',
|
|
80
|
-
restrict: 'RESTRICT',
|
|
81
|
-
cascade: 'CASCADE',
|
|
82
|
-
setNull: 'SET NULL',
|
|
83
|
-
setDefault: 'SET DEFAULT',
|
|
84
|
-
};
|
|
85
|
-
|
|
86
79
|
/**
|
|
87
80
|
* Renders a single column's inline DDL fragment within a `CREATE TABLE`
|
|
88
81
|
* statement. Honours the `inlineAutoincrementPrimaryKey` flag — SQLite
|
|
@@ -61,7 +61,7 @@ export function tableAt(
|
|
|
61
61
|
namespaceId: string,
|
|
62
62
|
tableName: string,
|
|
63
63
|
): StorageTable | undefined {
|
|
64
|
-
return storage.namespaces[namespaceId]?.
|
|
64
|
+
return storage.namespaces[namespaceId]?.entries.table[tableName] as StorageTable | undefined;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ContractMarkerRecord } from '@prisma-next/contract/types';
|
|
1
|
+
import type { Contract, ContractMarkerRecord } from '@prisma-next/contract/types';
|
|
2
2
|
import type {
|
|
3
3
|
MigrationOperationPolicy,
|
|
4
4
|
SqlControlFamilyInstance,
|
|
@@ -13,27 +13,17 @@ import type {
|
|
|
13
13
|
} from '@prisma-next/family-sql/control';
|
|
14
14
|
import { runnerFailure, runnerSuccess } from '@prisma-next/family-sql/control';
|
|
15
15
|
import { verifySqlSchema } from '@prisma-next/family-sql/schema-verify';
|
|
16
|
-
import
|
|
17
|
-
import type {
|
|
18
|
-
ControlDriverInstance,
|
|
19
|
-
MigrationRunnerResult,
|
|
20
|
-
} from '@prisma-next/framework-components/control';
|
|
16
|
+
import type { MigrationRunnerResult } from '@prisma-next/framework-components/control';
|
|
21
17
|
import { APP_SPACE_ID } from '@prisma-next/framework-components/control';
|
|
18
|
+
import type { SqlControlDriverInstance, SqlStorage } from '@prisma-next/sql-contract/types';
|
|
19
|
+
import type { LoweredStatement } from '@prisma-next/sql-relational-core/ast';
|
|
22
20
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
23
21
|
import type { Result } from '@prisma-next/utils/result';
|
|
24
22
|
import { notOk, ok, okVoid } from '@prisma-next/utils/result';
|
|
23
|
+
import { MARKER_TABLE_NAME } from '../control-tables';
|
|
25
24
|
import { parseSqliteDefault } from '../default-normalizer';
|
|
26
25
|
import { normalizeSqliteNativeType } from '../native-type-normalizer';
|
|
27
26
|
import type { SqlitePlanTargetDetails } from './planner-target-details';
|
|
28
|
-
import {
|
|
29
|
-
buildLedgerInsertStatement,
|
|
30
|
-
buildWriteMarkerStatements,
|
|
31
|
-
ensureLedgerTableStatement,
|
|
32
|
-
ensureMarkerTableStatement,
|
|
33
|
-
MARKER_TABLE_NAME,
|
|
34
|
-
readMarkerStatement,
|
|
35
|
-
type SqlStatement,
|
|
36
|
-
} from './statement-builders';
|
|
37
27
|
|
|
38
28
|
export function createSqliteMigrationRunner(
|
|
39
29
|
family: SqlControlFamilyInstance,
|
|
@@ -70,9 +60,9 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
|
|
|
70
60
|
const policyCheck = this.enforcePolicyCompatibility(options.policy, options.plan.operations);
|
|
71
61
|
if (!policyCheck.ok) return policyCheck;
|
|
72
62
|
|
|
73
|
-
const ensureResult = await this.ensureControlTables(driver);
|
|
63
|
+
const ensureResult = await this.ensureControlTables(driver, options.destinationContract);
|
|
74
64
|
if (!ensureResult.ok) return ensureResult;
|
|
75
|
-
const existingMarker = await this.readMarker(driver, space);
|
|
65
|
+
const existingMarker = await this.family.readMarker({ driver, space });
|
|
76
66
|
|
|
77
67
|
const markerCheck = this.ensureMarkerCompatibility(existingMarker, options.plan);
|
|
78
68
|
if (!markerCheck.ok) return markerCheck;
|
|
@@ -126,8 +116,9 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
|
|
|
126
116
|
const isSelfEdgeNoOp = isSelfEdge && operationsExecuted === 0 && incomingIsSubsetOfExisting;
|
|
127
117
|
|
|
128
118
|
if (!isSelfEdgeNoOp) {
|
|
129
|
-
await this.upsertMarker(driver, options, existingMarker, space);
|
|
130
|
-
|
|
119
|
+
const markerResult = await this.upsertMarker(driver, options, existingMarker, space);
|
|
120
|
+
if (!markerResult.ok) return markerResult;
|
|
121
|
+
await this.recordLedgerEntries(driver, options, executedOperations);
|
|
131
122
|
}
|
|
132
123
|
|
|
133
124
|
return runnerSuccess({
|
|
@@ -137,7 +128,7 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
|
|
|
137
128
|
}
|
|
138
129
|
|
|
139
130
|
async execute(options: {
|
|
140
|
-
readonly driver:
|
|
131
|
+
readonly driver: SqlControlDriverInstance<string>;
|
|
141
132
|
readonly perSpaceOptions: ReadonlyArray<
|
|
142
133
|
SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>
|
|
143
134
|
>;
|
|
@@ -306,17 +297,16 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
|
|
|
306
297
|
|
|
307
298
|
private async ensureControlTables(
|
|
308
299
|
driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],
|
|
300
|
+
contract: Contract<SqlStorage>,
|
|
309
301
|
): Promise<Result<void, SqlMigrationRunnerFailure>> {
|
|
310
|
-
// Pre-1.0 zero-range guardrail: detect a pre-cleanup single-row
|
|
311
|
-
// marker table (no `space` column) and surface a structured failure
|
|
312
|
-
// rather than silently rebuilding the table into the per-space
|
|
313
|
-
// shape. See `specs/framework-mechanism.spec.md § 2`.
|
|
314
302
|
const legacyDetection = await this.detectLegacyMarkerShape(driver);
|
|
315
303
|
if (!legacyDetection.ok) {
|
|
316
304
|
return legacyDetection;
|
|
317
305
|
}
|
|
318
|
-
|
|
319
|
-
|
|
306
|
+
const lowererContext = { contract };
|
|
307
|
+
for (const query of this.family.bootstrapControlTableQueries()) {
|
|
308
|
+
await this.executeStatement(driver, this.family.lowerAst(query, lowererContext));
|
|
309
|
+
}
|
|
320
310
|
return okVoid();
|
|
321
311
|
}
|
|
322
312
|
|
|
@@ -347,32 +337,6 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
|
|
|
347
337
|
);
|
|
348
338
|
}
|
|
349
339
|
|
|
350
|
-
private async readMarker(
|
|
351
|
-
driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],
|
|
352
|
-
space: string,
|
|
353
|
-
): Promise<ContractMarkerRecord | null> {
|
|
354
|
-
const stmt = readMarkerStatement(space);
|
|
355
|
-
try {
|
|
356
|
-
const result = await driver.query<ContractMarkerRow>(stmt.sql, stmt.params);
|
|
357
|
-
const row = result.rows[0];
|
|
358
|
-
if (!row) return null;
|
|
359
|
-
// SQLite stores arrays as JSON-encoded TEXT (no native array type), so
|
|
360
|
-
// the driver returns `invariants` as a string. Decode before delegating
|
|
361
|
-
// to the shared row schema, which expects `string[]`.
|
|
362
|
-
const invariants =
|
|
363
|
-
typeof row.invariants === 'string'
|
|
364
|
-
? (JSON.parse(row.invariants) as unknown)
|
|
365
|
-
: row.invariants;
|
|
366
|
-
return parseContractMarkerRow({ ...row, invariants });
|
|
367
|
-
} catch (error) {
|
|
368
|
-
// Table might not exist yet
|
|
369
|
-
if (error instanceof Error && error.message.includes('no such table')) {
|
|
370
|
-
return null;
|
|
371
|
-
}
|
|
372
|
-
throw error;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
340
|
private async runExpectationSteps(
|
|
377
341
|
driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],
|
|
378
342
|
steps: readonly SqlMigrationPlanOperationStep[],
|
|
@@ -600,48 +564,79 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
|
|
|
600
564
|
options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,
|
|
601
565
|
existingMarker: ContractMarkerRecord | null,
|
|
602
566
|
space: string,
|
|
603
|
-
): Promise<void
|
|
604
|
-
//
|
|
605
|
-
// the
|
|
606
|
-
// BEGIN EXCLUSIVE —
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
const invariants = Array.from(merged).sort();
|
|
610
|
-
const writeStatements = buildWriteMarkerStatements({
|
|
611
|
-
space,
|
|
567
|
+
): Promise<Result<void, SqlMigrationRunnerFailure>> {
|
|
568
|
+
// Pass the plan's incoming invariants verbatim; `updateMarker` unions them
|
|
569
|
+
// with the stored set (TS-side, dialect-uniform) under the runner's
|
|
570
|
+
// BEGIN EXCLUSIVE — no client-side pre-merge here, so there is no
|
|
571
|
+
// double-merge with the SPI's internal accumulate-dedupe.
|
|
572
|
+
const destination = {
|
|
612
573
|
storageHash: options.plan.destination.storageHash,
|
|
613
574
|
profileHash:
|
|
614
575
|
options.plan.destination.profileHash ??
|
|
615
576
|
options.destinationContract.profileHash ??
|
|
616
577
|
options.plan.destination.storageHash,
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
578
|
+
invariants: options.plan.providedInvariants ?? [],
|
|
579
|
+
};
|
|
580
|
+
if (!existingMarker) {
|
|
581
|
+
await this.family.initMarker({ driver, space, destination });
|
|
582
|
+
return okVoid();
|
|
583
|
+
}
|
|
584
|
+
const updated = await this.family.updateMarker({
|
|
585
|
+
driver,
|
|
586
|
+
space,
|
|
587
|
+
expectedFrom: existingMarker.storageHash,
|
|
588
|
+
destination,
|
|
621
589
|
});
|
|
622
|
-
|
|
623
|
-
|
|
590
|
+
if (!updated) {
|
|
591
|
+
return runnerFailure(
|
|
592
|
+
'MARKER_CAS_FAILURE',
|
|
593
|
+
'Marker was modified by another process during migration execution.',
|
|
594
|
+
{
|
|
595
|
+
meta: {
|
|
596
|
+
space,
|
|
597
|
+
expectedStorageHash: existingMarker.storageHash,
|
|
598
|
+
destinationStorageHash: options.plan.destination.storageHash,
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
return okVoid();
|
|
624
604
|
}
|
|
625
605
|
|
|
626
|
-
private async
|
|
606
|
+
private async recordLedgerEntries(
|
|
627
607
|
driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],
|
|
628
608
|
options: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>,
|
|
629
|
-
existingMarker: ContractMarkerRecord | null,
|
|
630
609
|
executedOperations: readonly SqlMigrationPlanOperation<SqlitePlanTargetDetails>[],
|
|
631
610
|
): Promise<void> {
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
611
|
+
const plan = options.plan;
|
|
612
|
+
const space = plan.spaceId;
|
|
613
|
+
const edges = options.migrationEdges;
|
|
614
|
+
const totalEdgeOps = edges.reduce((sum, edge) => sum + edge.operationCount, 0);
|
|
615
|
+
if (totalEdgeOps !== plan.operations.length) {
|
|
616
|
+
throw new Error(
|
|
617
|
+
`Ledger write: plan.operations length (${plan.operations.length}) does not match sum of migrationEdges operationCount (${totalEdgeOps})`,
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
// The ledger records the operations as executed — idempotency-skipped ops
|
|
621
|
+
// are substituted with skip records (empty `execute`) by `applyPlan`, so the
|
|
622
|
+
// journal reflects what actually ran rather than the raw plan.
|
|
623
|
+
let offset = 0;
|
|
624
|
+
for (const edge of edges) {
|
|
625
|
+
const edgeOps = executedOperations.slice(offset, offset + edge.operationCount);
|
|
626
|
+
offset += edge.operationCount;
|
|
627
|
+
await this.family.writeLedgerEntry({
|
|
628
|
+
driver,
|
|
629
|
+
space,
|
|
630
|
+
entry: {
|
|
631
|
+
edgeId: `${edge.from}->${edge.to}`,
|
|
632
|
+
from: edge.from,
|
|
633
|
+
to: edge.to,
|
|
634
|
+
migrationName: edge.dirName,
|
|
635
|
+
migrationHash: edge.migrationHash,
|
|
636
|
+
operations: edgeOps,
|
|
637
|
+
},
|
|
638
|
+
});
|
|
639
|
+
}
|
|
645
640
|
}
|
|
646
641
|
|
|
647
642
|
private async beginExclusiveTransaction(
|
|
@@ -664,7 +659,7 @@ class SqliteMigrationRunner implements SqlMigrationRunner<SqlitePlanTargetDetail
|
|
|
664
659
|
|
|
665
660
|
private async executeStatement(
|
|
666
661
|
driver: SqlMigrationRunnerExecuteOptions<SqlitePlanTargetDetails>['driver'],
|
|
667
|
-
statement:
|
|
662
|
+
statement: LoweredStatement,
|
|
668
663
|
): Promise<void> {
|
|
669
664
|
if (statement.params.length > 0) {
|
|
670
665
|
await driver.query(statement.sql, statement.params);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RuntimeTargetInstance } from '@prisma-next/framework-components/execution';
|
|
2
2
|
import type { SqlRuntimeTargetDescriptor } from '@prisma-next/sql-runtime';
|
|
3
|
-
import {
|
|
3
|
+
import { sqliteTargetDescriptorMetaRuntime } from './descriptor-meta-runtime';
|
|
4
4
|
|
|
5
5
|
export interface SqliteRuntimeTargetInstance extends RuntimeTargetInstance<'sql', 'sqlite'> {}
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ const sqliteRuntimeTargetDescriptor: SqlRuntimeTargetDescriptor<
|
|
|
8
8
|
'sqlite',
|
|
9
9
|
SqliteRuntimeTargetInstance
|
|
10
10
|
> = {
|
|
11
|
-
...
|
|
11
|
+
...sqliteTargetDescriptorMetaRuntime,
|
|
12
12
|
codecs: () => [],
|
|
13
13
|
create(): SqliteRuntimeTargetInstance {
|
|
14
14
|
return {
|
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
import type { Contract } from '@prisma-next/contract/types';
|
|
2
2
|
import { SqlContractSerializerBase } from '@prisma-next/family-sql/ir';
|
|
3
|
-
import type
|
|
3
|
+
import { type Namespace, NamespaceBase } from '@prisma-next/framework-components/ir';
|
|
4
|
+
import type { SqlNamespaceTablesInput, SqlStorage } from '@prisma-next/sql-contract/types';
|
|
5
|
+
import { blindCast } from '@prisma-next/utils/casts';
|
|
6
|
+
import { buildSqliteNamespace } from './sqlite-unbound-database';
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* SQLite target `ContractSerializer` concretion. Mirrors the Postgres
|
|
7
|
-
* shape: inherits the full SQL-family deserialization pipeline
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* `serializeContract` falls through to the family-base default —
|
|
12
|
-
* SQLite's contract is JSON-clean today. Once target-only fields land
|
|
13
|
-
* (e.g. per-target derived storage fields) this is the home for
|
|
14
|
-
* stripping them from the persisted envelope.
|
|
10
|
+
* shape: inherits the full SQL-family deserialization pipeline and
|
|
11
|
+
* materialises namespace entries as SQLite database concretions that
|
|
12
|
+
* expose `qualifyTable()` for runtime SQL rendering.
|
|
15
13
|
*/
|
|
16
14
|
export class SqliteContractSerializer extends SqlContractSerializerBase<Contract<SqlStorage>> {
|
|
17
15
|
constructor() {
|
|
18
16
|
super(new Map());
|
|
19
17
|
}
|
|
18
|
+
|
|
19
|
+
protected override hydrateSqlNamespaceEntry(
|
|
20
|
+
nsId: string,
|
|
21
|
+
raw: Namespace | Record<string, unknown>,
|
|
22
|
+
): Namespace | SqlNamespaceTablesInput {
|
|
23
|
+
if (raw instanceof NamespaceBase) {
|
|
24
|
+
return raw;
|
|
25
|
+
}
|
|
26
|
+
const hydrated = blindCast<
|
|
27
|
+
SqlNamespaceTablesInput,
|
|
28
|
+
'super.hydrateSqlNamespaceEntry returns the tables form when raw is not a NamespaceBase'
|
|
29
|
+
>(super.hydrateSqlNamespaceEntry(nsId, raw));
|
|
30
|
+
return buildSqliteNamespace(hydrated);
|
|
31
|
+
}
|
|
20
32
|
}
|
|
@@ -1,9 +1,81 @@
|
|
|
1
1
|
import {
|
|
2
2
|
freezeNode,
|
|
3
|
+
type Namespace,
|
|
3
4
|
NamespaceBase,
|
|
4
5
|
UNBOUND_NAMESPACE_ID,
|
|
5
6
|
} from '@prisma-next/framework-components/ir';
|
|
6
|
-
import
|
|
7
|
+
import {
|
|
8
|
+
type SqlNamespaceTablesInput,
|
|
9
|
+
StorageTable,
|
|
10
|
+
type StorageTableInput,
|
|
11
|
+
} from '@prisma-next/sql-contract/types';
|
|
12
|
+
import { blindCast, castAs } from '@prisma-next/utils/casts';
|
|
13
|
+
|
|
14
|
+
export type SqliteDatabaseInput = {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly entries: {
|
|
17
|
+
readonly table: Readonly<Record<string, StorageTable | StorageTableInput>>;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const SQLITE_NAMESPACE_KIND = 'sqlite-namespace' as const;
|
|
22
|
+
|
|
23
|
+
function isMaterializedSqliteNamespace(
|
|
24
|
+
ns: Namespace | SqlNamespaceTablesInput,
|
|
25
|
+
): ns is SqliteDatabase | SqliteUnboundDatabase {
|
|
26
|
+
if (typeof ns !== 'object' || ns === null) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const proto = Object.getPrototypeOf(ns);
|
|
30
|
+
if (proto === Object.prototype || proto === null) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return (ns as { kind?: unknown }).kind === SQLITE_NAMESPACE_KIND;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* SQLite namespace concretion carrying table metadata under
|
|
38
|
+
* `entries.table` and unqualified `qualifyTable()` emission for runtime
|
|
39
|
+
* SQL rendering.
|
|
40
|
+
*/
|
|
41
|
+
export class SqliteDatabase extends NamespaceBase {
|
|
42
|
+
declare readonly kind: string;
|
|
43
|
+
|
|
44
|
+
readonly id: string;
|
|
45
|
+
readonly entries: Readonly<{
|
|
46
|
+
readonly table: Readonly<Record<string, StorageTable>>;
|
|
47
|
+
}>;
|
|
48
|
+
|
|
49
|
+
constructor(input: SqliteDatabaseInput) {
|
|
50
|
+
super();
|
|
51
|
+
this.id = input.id;
|
|
52
|
+
this.entries = Object.freeze({
|
|
53
|
+
table: Object.freeze(
|
|
54
|
+
Object.fromEntries(
|
|
55
|
+
Object.entries(input.entries.table).map(([k, v]) => [
|
|
56
|
+
k,
|
|
57
|
+
v instanceof StorageTable ? v : new StorageTable(v as StorageTableInput),
|
|
58
|
+
]),
|
|
59
|
+
),
|
|
60
|
+
),
|
|
61
|
+
});
|
|
62
|
+
Object.defineProperty(this, 'kind', {
|
|
63
|
+
value: SQLITE_NAMESPACE_KIND,
|
|
64
|
+
writable: false,
|
|
65
|
+
enumerable: false,
|
|
66
|
+
configurable: true,
|
|
67
|
+
});
|
|
68
|
+
freezeNode(this);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
qualifier(): string {
|
|
72
|
+
return '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
qualifyTable(tableName: string): string {
|
|
76
|
+
return `"${tableName}"`;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
7
79
|
|
|
8
80
|
/**
|
|
9
81
|
* SQLite target `Namespace` concretion. SQLite has no schema or
|
|
@@ -22,43 +94,58 @@ import type { StorageTable } from '@prisma-next/sql-contract/types';
|
|
|
22
94
|
* `__unspecified__` AST bucket reaches the SQLite interpreter, which
|
|
23
95
|
* lowers it to this singleton.
|
|
24
96
|
*/
|
|
25
|
-
export class SqliteUnboundDatabase extends
|
|
97
|
+
export class SqliteUnboundDatabase extends SqliteDatabase {
|
|
26
98
|
static readonly instance: SqliteUnboundDatabase = new SqliteUnboundDatabase();
|
|
27
99
|
|
|
28
|
-
readonly kind = 'database' as const;
|
|
29
|
-
readonly id = UNBOUND_NAMESPACE_ID;
|
|
30
|
-
readonly tables: Readonly<Record<string, StorageTable>>;
|
|
31
|
-
|
|
32
100
|
private constructor() {
|
|
33
|
-
super();
|
|
34
|
-
this.tables = Object.freeze({});
|
|
35
|
-
freezeNode(this);
|
|
101
|
+
super({ id: UNBOUND_NAMESPACE_ID, entries: { table: {} } });
|
|
36
102
|
}
|
|
103
|
+
}
|
|
37
104
|
|
|
38
|
-
|
|
39
|
-
|
|
105
|
+
export function buildSqliteNamespace(
|
|
106
|
+
input: SqlNamespaceTablesInput,
|
|
107
|
+
): SqliteDatabase | SqliteUnboundDatabase {
|
|
108
|
+
if (input.id !== UNBOUND_NAMESPACE_ID) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`buildSqliteNamespace: SQLite has no schema concept; the only valid namespace id is "${UNBOUND_NAMESPACE_ID}" (received "${input.id}").`,
|
|
111
|
+
);
|
|
40
112
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return `"${tableName}"`;
|
|
113
|
+
if (Object.keys(input.entries.table).length === 0) {
|
|
114
|
+
return castAs<SqliteUnboundDatabase>(SqliteUnboundDatabase.instance);
|
|
44
115
|
}
|
|
116
|
+
return new SqliteDatabase({ id: input.id, entries: input.entries });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function buildSqliteNamespaceMap(
|
|
120
|
+
namespaces: Readonly<Record<string, Namespace | SqlNamespaceTablesInput>>,
|
|
121
|
+
): Readonly<Record<string, SqliteDatabase | SqliteUnboundDatabase>> {
|
|
122
|
+
return Object.fromEntries(
|
|
123
|
+
Object.entries(namespaces).map(([nsKey, ns]) => [
|
|
124
|
+
nsKey,
|
|
125
|
+
isMaterializedSqliteNamespace(ns)
|
|
126
|
+
? ns
|
|
127
|
+
: buildSqliteNamespace(
|
|
128
|
+
blindCast<
|
|
129
|
+
SqlNamespaceTablesInput,
|
|
130
|
+
'non-materialized SQLite namespace map entry is a SqlNamespaceTablesInput'
|
|
131
|
+
>(ns),
|
|
132
|
+
),
|
|
133
|
+
]),
|
|
134
|
+
);
|
|
45
135
|
}
|
|
46
136
|
|
|
47
137
|
/**
|
|
48
138
|
* Target-supplied `Namespace` factory the SQLite target plumbs through
|
|
49
139
|
* `defineContract({ createNamespace })`. SQLite has only one
|
|
50
140
|
* effective namespace slot — the framework `UNBOUND_NAMESPACE_ID`
|
|
51
|
-
* sentinel — so the factory always returns the singleton
|
|
52
|
-
*
|
|
53
|
-
* `defineContract` already rejects
|
|
54
|
-
* this throw is a structural
|
|
55
|
-
* surface.
|
|
141
|
+
* sentinel — so the factory always returns the singleton or a fresh
|
|
142
|
+
* `SqliteDatabase` for the unbound slot with tables. The SQL family's
|
|
143
|
+
* defensive validation in `defineContract` already rejects
|
|
144
|
+
* user-declared SQLite namespaces, so this throw is a structural
|
|
145
|
+
* safety net rather than a user-facing surface.
|
|
56
146
|
*/
|
|
57
|
-
export function sqliteCreateNamespace(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
throw new Error(
|
|
62
|
-
`sqliteCreateNamespace: SQLite has no schema concept; the only valid namespace id is "${UNBOUND_NAMESPACE_ID}" (received "${id}").`,
|
|
63
|
-
);
|
|
147
|
+
export function sqliteCreateNamespace(
|
|
148
|
+
input: SqlNamespaceTablesInput,
|
|
149
|
+
): SqliteDatabase | SqliteUnboundDatabase {
|
|
150
|
+
return buildSqliteNamespace(input);
|
|
64
151
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { datetime, integer, jsonText, sqliteTable, text } from '../contract-free/columns';
|
|
2
|
+
export {
|
|
3
|
+
buildControlTableBootstrapQueries,
|
|
4
|
+
buildSignMarkerBootstrapQueries,
|
|
5
|
+
} from '../contract-free/control-bootstrap';
|
|
6
|
+
export { createTable } from '../contract-free/ddl';
|