@typesql/core 0.8.1 → 0.9.0
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 +37 -0
- package/dist/migration/context.d.ts +35 -0
- package/dist/migration/context.d.ts.map +1 -0
- package/dist/migration/context.js +15 -0
- package/dist/migration/context.js.map +1 -0
- package/dist/migration/files.d.ts +19 -0
- package/dist/migration/files.d.ts.map +1 -0
- package/dist/migration/files.js +73 -0
- package/dist/migration/files.js.map +1 -0
- package/dist/migration/index.d.ts +4 -0
- package/dist/migration/index.d.ts.map +1 -0
- package/dist/migration/index.js +3 -0
- package/dist/migration/index.js.map +1 -0
- package/dist/migration/runtime.d.ts +16 -0
- package/dist/migration/runtime.d.ts.map +1 -0
- package/dist/migration/runtime.js +273 -0
- package/dist/migration/runtime.js.map +1 -0
- package/dist/postgres/engine/cache.d.ts +1 -1
- package/dist/sqlite/engine/cache.d.ts +1 -1
- package/package.json +5 -1
- package/src/migration/context.ts +92 -0
- package/src/migration/files.ts +94 -0
- package/src/migration/index.ts +19 -0
- package/src/migration/runtime.ts +348 -0
- package/src/postgres/engine/cache.ts +1 -1
- package/src/sqlite/engine/cache.ts +1 -1
package/README.md
CHANGED
|
@@ -71,6 +71,43 @@ const query = findUser(7);
|
|
|
71
71
|
|
|
72
72
|
Named parameters and fixed fragments work too. The `pg` and `postgres` adapters also request server-side preparation when these queries execute. See the [prepared query API](https://typesql.karsli.net/reference/api#sql-prepare-text-slots).
|
|
73
73
|
|
|
74
|
+
## Tracked migrations
|
|
75
|
+
|
|
76
|
+
The separate `@typesql/core/migration` entry point loads TypeScript migration files and runs them against PostgreSQL or SQLite on Node 26. Each file exports `up` and `down`:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import type { PostgresMigration } from "@typesql/core/migration";
|
|
80
|
+
|
|
81
|
+
export async function up(db: PostgresMigration) {
|
|
82
|
+
const next = await db.add("CREATE TABLE users (id int PRIMARY KEY, name text NOT NULL)");
|
|
83
|
+
await next("INSERT INTO users (id, name) VALUES ($1, $2)", 1, "Ada").execute();
|
|
84
|
+
return next;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function down(db: PostgresMigration) {
|
|
88
|
+
await db.raw("DROP TABLE users");
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Use `SqliteMigration` for SQLite. Later files can declare their starting schema as
|
|
93
|
+
`PostgresMigration<MigrationSchema<typeof previousUp>>` by importing the previous
|
|
94
|
+
`up` as a type. Await `.add()` or `.raw<NewSchema>()` into a new constant to retain
|
|
95
|
+
the updated schema; return that context from `up`, including for a no-op migration.
|
|
96
|
+
`down` may return nothing.
|
|
97
|
+
|
|
98
|
+
`migrateUp`, `migrateDown`, and `migrateLatest` accept `{ dialect, migrationsDir,
|
|
99
|
+
execute, migrationTable? }`. `execute` is a TypeSQL executor bound to one dedicated
|
|
100
|
+
database connection for the entire call. Files run in filename order, each in its
|
|
101
|
+
own transaction, with SHA-256 history checks and concurrency protection. A failed
|
|
102
|
+
file rolls back while earlier committed files remain applied. Migration code must
|
|
103
|
+
await every database operation and leave transaction management to the runner.
|
|
104
|
+
The default tracking table is `schema_migrations`. Hashes cover each migration
|
|
105
|
+
file's runtime code; imported runtime helpers are not tracked.
|
|
106
|
+
|
|
107
|
+
For configuration, file generation, and command-line execution, install
|
|
108
|
+
`@typesql/cli` and use `typesql migrate make <name>`, `up`, `down`, or `latest`.
|
|
109
|
+
See the [migration guide](https://typesql.karsli.net/guide/migrations).
|
|
110
|
+
|
|
74
111
|
## Documentation
|
|
75
112
|
|
|
76
113
|
The full guide is at **[typesql.karsli.net](https://typesql.karsli.net)**. It covers the [schema](https://typesql.karsli.net/guide/schema), [parameters](https://typesql.karsli.net/guide/parameters), [writing data](https://typesql.karsli.net/guide/writing-data), [dynamic SQL](https://typesql.karsli.net/guide/dynamic-sql), [migrations](https://typesql.karsli.net/guide/migrations), the [type cache](https://typesql.karsli.net/guide/performance) and [known limits](https://typesql.karsli.net/guide/limits).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ApplyDdlOf, DialectTypes, ValidateDdlOf } from "../dialect.js";
|
|
2
|
+
import type { EngineTypes } from "../engine/contract.js";
|
|
3
|
+
import type { ParamsOf, RowOf } from "../engine/infer.js";
|
|
4
|
+
import { type Postgres } from "../postgres/dialect.js";
|
|
5
|
+
import type { PgEngine } from "../postgres/engine/engine.js";
|
|
6
|
+
import type { Executor, Query } from "../query.js";
|
|
7
|
+
import type { SqlApi } from "../sql/api.js";
|
|
8
|
+
import type { BoxCallArgs, BoxSlottedArgs, CallRet, GateCallCheck, GateSlottedCheck, SplicedOr } from "../sql/call.js";
|
|
9
|
+
import type { SlotMap, SlotsFor } from "../sql/slots.js";
|
|
10
|
+
import { type Sqlite } from "../sqlite/dialect.js";
|
|
11
|
+
import type { SqliteEngine } from "../sqlite/engine/engine.js";
|
|
12
|
+
type GateDdl<T extends DialectTypes, DB, Q extends string> = [DB, Q] extends [
|
|
13
|
+
infer D,
|
|
14
|
+
infer Text extends string
|
|
15
|
+
] ? ValidateDdlOf<T, D, Text> : never;
|
|
16
|
+
/** A checked SQL executor whose schema evolves after each awaited DDL statement. */
|
|
17
|
+
export interface MigrationContext<E extends EngineTypes, T extends DialectTypes, DB, TM = T["typeMap"]> extends Omit<SqlApi<E, DB, TM>, "raw"> {
|
|
18
|
+
/** Type-only schema marker; no database introspection is performed. */
|
|
19
|
+
readonly schema: DB;
|
|
20
|
+
<const Q extends string>(query: GateCallCheck<E, DB, TM, "preserve", Q> & Q, ...params: BoxCallArgs<E, DB, TM, "preserve", Q>): CallRet<E, DB, TM, "preserve", Q>;
|
|
21
|
+
<const Q extends string, const S extends SlotMap>(query: GateSlottedCheck<E, DB, TM, Q, S> & Q, slots: S & SlotsFor<Q, S>, ...params: BoxSlottedArgs<E, DB, TM, Q, S>): Query<RowOf<E, DB, TM, SplicedOr<Q, S>>, ParamsOf<E, DB, TM, SplicedOr<Q, S>>, SplicedOr<Q, S>>;
|
|
22
|
+
add<const Q extends string>(ddl: GateDdl<T, DB, Q> & Q): Promise<MigrationContext<E, T, ApplyDdlOf<T, DB, Q>, TM>>;
|
|
23
|
+
/** Execute SQL the checker cannot see, explicitly asserting its resulting schema. */
|
|
24
|
+
raw<NewSchema = DB>(sql: string): Promise<MigrationContext<E, T, NewSchema, TM>>;
|
|
25
|
+
}
|
|
26
|
+
export type PostgresMigration<Base = {}> = MigrationContext<PgEngine, Postgres, Base>;
|
|
27
|
+
export type SqliteMigration<Base = {}> = MigrationContext<SqliteEngine, Sqlite, Base>;
|
|
28
|
+
/** Extract the final schema returned by an async migration's up function. */
|
|
29
|
+
export type MigrationSchema<Up extends (...args: never[]) => unknown> = Awaited<ReturnType<Up>> extends {
|
|
30
|
+
readonly schema: infer Schema;
|
|
31
|
+
} ? Schema : never;
|
|
32
|
+
/** Internal factory: execution is bound to the runner's active transaction. */
|
|
33
|
+
export declare function createMigrationContext(dialect: "postgres" | "sqlite", execute: Executor): PostgresMigration | SqliteMigration;
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/migration/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,KAAK,QAAQ,EAAY,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EACX,WAAW,EACX,cAAc,EACd,OAAO,EACP,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,KAAK,MAAM,EAAU,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE/D,KAAK,OAAO,CAAC,CAAC,SAAS,YAAY,EAAE,EAAE,EAAE,CAAC,SAAS,MAAM,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS;IAC5E,MAAM,CAAC;IACP,MAAM,IAAI,SAAS,MAAM;CACzB,GACE,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GACzB,KAAK,CAAC;AAET,oFAAoF;AACpF,MAAM,WAAW,gBAAgB,CAChC,CAAC,SAAS,WAAW,EACrB,CAAC,SAAS,YAAY,EACtB,EAAE,EACF,EAAE,GAAG,CAAC,CAAC,SAAS,CAAC,CAChB,SAAQ,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC;IACvC,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;IACpB,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,EACtB,KAAK,EAAE,aAAa,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,EAClD,GAAG,MAAM,EAAE,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,GAC9C,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;IACrC,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,CAAC,CAAC,SAAS,OAAO,EAC/C,KAAK,EAAE,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,EAC5C,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,EACzB,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,GACxC,KAAK,CACP,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EACjC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EACpC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CACf,CAAC;IACF,GAAG,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,EACzB,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,GACxB,OAAO,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAC7D,qFAAqF;IACrF,GAAG,CAAC,SAAS,GAAG,EAAE,EACjB,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;CAClD;AAED,MAAM,MAAM,iBAAiB,CAAC,IAAI,GAAG,EAAE,IAAI,gBAAgB,CAC1D,QAAQ,EACR,QAAQ,EACR,IAAI,CACJ,CAAC;AACF,MAAM,MAAM,eAAe,CAAC,IAAI,GAAG,EAAE,IAAI,gBAAgB,CACxD,YAAY,EACZ,MAAM,EACN,IAAI,CACJ,CAAC;AAEF,6EAA6E;AAC7E,MAAM,MAAM,eAAe,CAAC,EAAE,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,IACnE,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,MAAM,CAAA;CAAE,GAC9D,MAAM,GACN,KAAK,CAAC;AAEV,+EAA+E;AAC/E,wBAAgB,sBAAsB,CACrC,OAAO,EAAE,UAAU,GAAG,QAAQ,EAC9B,OAAO,EAAE,QAAQ,GACf,iBAAiB,GAAG,eAAe,CAYrC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createDialectSql } from "../create-sql.js";
|
|
2
|
+
import { postgres } from "../postgres/dialect.js";
|
|
3
|
+
import { sqlite } from "../sqlite/dialect.js";
|
|
4
|
+
/** Internal factory: execution is bound to the runner's active transaction. */
|
|
5
|
+
export function createMigrationContext(dialect, execute) {
|
|
6
|
+
const sql = dialect === "postgres"
|
|
7
|
+
? createDialectSql(postgres, execute)
|
|
8
|
+
: createDialectSql(sqlite, execute);
|
|
9
|
+
const next = async (statement) => {
|
|
10
|
+
await execute(statement, []);
|
|
11
|
+
return createMigrationContext(dialect, execute);
|
|
12
|
+
};
|
|
13
|
+
return Object.assign(sql, { add: next, raw: next });
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/migration/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAIpD,OAAO,EAAiB,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAajE,OAAO,EAAe,MAAM,EAAE,MAAM,sBAAsB,CAAC;AA0D3D,+EAA+E;AAC/E,MAAM,UAAU,sBAAsB,CACrC,OAA8B,EAC9B,OAAiB;IAEjB,MAAM,GAAG,GACR,OAAO,KAAK,UAAU;QACrB,CAAC,CAAC,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC;QACrC,CAAC,CAAC,gBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,KAAK,EAAE,SAAiB,EAAE,EAAE;QACxC,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC7B,OAAO,sBAAsB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAEhC,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** A discovered TypeScript migration. Loading uses Node's native TypeScript support. */
|
|
2
|
+
export interface MigrationFile {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly path: string;
|
|
5
|
+
readonly hash: string;
|
|
6
|
+
load(): Promise<MigrationModule>;
|
|
7
|
+
}
|
|
8
|
+
export interface MigrationModule {
|
|
9
|
+
readonly up: (context: never) => unknown;
|
|
10
|
+
readonly down: (context: never) => unknown;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Hash the erased JavaScript's syntax tree and original runtime token spellings.
|
|
14
|
+
* Tree boundaries preserve automatic semicolon insertion (including return/newline)
|
|
15
|
+
* while trivia and TypeScript-only declarations do not contribute to the hash.
|
|
16
|
+
*/
|
|
17
|
+
export declare function migrationHash(source: string): string;
|
|
18
|
+
export declare function discoverMigrations(migrationsDir: string): Promise<readonly MigrationFile[]>;
|
|
19
|
+
//# sourceMappingURL=files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/migration/files.ts"],"names":[],"mappings":"AAQA,wFAAwF;AACxF,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,IAAI,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,KAAK,KAAK,OAAO,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,KAAK,KAAK,OAAO,CAAC;CAC3C;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAqBpD;AAED,wBAAsB,kBAAkB,CACvC,aAAa,EAAE,MAAM,GACnB,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CA0CnC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
3
|
+
import { stripTypeScriptTypes } from "node:module";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { formatSyntaxKind } from "typescript/ast";
|
|
7
|
+
import { API } from "typescript/sync";
|
|
8
|
+
/**
|
|
9
|
+
* Hash the erased JavaScript's syntax tree and original runtime token spellings.
|
|
10
|
+
* Tree boundaries preserve automatic semicolon insertion (including return/newline)
|
|
11
|
+
* while trivia and TypeScript-only declarations do not contribute to the hash.
|
|
12
|
+
*/
|
|
13
|
+
export function migrationHash(source) {
|
|
14
|
+
const javascript = stripTypeScriptTypes(source);
|
|
15
|
+
const api = new API();
|
|
16
|
+
try {
|
|
17
|
+
const lease = api.createSourceFile("migration.js", javascript);
|
|
18
|
+
const file = lease.sourceFile;
|
|
19
|
+
const hash = createHash("sha256");
|
|
20
|
+
const visit = (node) => {
|
|
21
|
+
if (formatSyntaxKind(node.kind).startsWith("JSDoc"))
|
|
22
|
+
return;
|
|
23
|
+
const children = node.getChildren(file);
|
|
24
|
+
hash.update(`${formatSyntaxKind(node.kind)}[`);
|
|
25
|
+
if (children.length === 0)
|
|
26
|
+
hash.update(JSON.stringify(node.getText(file)));
|
|
27
|
+
else
|
|
28
|
+
for (const child of children)
|
|
29
|
+
visit(child);
|
|
30
|
+
hash.update("]");
|
|
31
|
+
};
|
|
32
|
+
visit(file);
|
|
33
|
+
return hash.digest("hex");
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
api.close();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export async function discoverMigrations(migrationsDir) {
|
|
40
|
+
const directory = resolve(migrationsDir);
|
|
41
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
42
|
+
const names = entries
|
|
43
|
+
.filter((entry) => (entry.isFile() || entry.isSymbolicLink()) &&
|
|
44
|
+
entry.name.endsWith(".ts"))
|
|
45
|
+
.map((entry) => entry.name)
|
|
46
|
+
.sort();
|
|
47
|
+
return Promise.all(names.map(async (name) => {
|
|
48
|
+
const path = resolve(directory, name);
|
|
49
|
+
const source = await readFile(path, "utf8");
|
|
50
|
+
const hash = migrationHash(source);
|
|
51
|
+
return {
|
|
52
|
+
name,
|
|
53
|
+
path,
|
|
54
|
+
hash,
|
|
55
|
+
async load() {
|
|
56
|
+
if (migrationHash(await readFile(path, "utf8")) !== hash)
|
|
57
|
+
throw new Error(`Migration changed during execution: ${name}`);
|
|
58
|
+
const url = pathToFileURL(path);
|
|
59
|
+
url.searchParams.set("typesql", hash);
|
|
60
|
+
const module = await import(url.href);
|
|
61
|
+
if (module === null ||
|
|
62
|
+
typeof module !== "object" ||
|
|
63
|
+
!("up" in module) ||
|
|
64
|
+
typeof module.up !== "function" ||
|
|
65
|
+
!("down" in module) ||
|
|
66
|
+
typeof module.down !== "function")
|
|
67
|
+
throw new Error(`Migration must export up and down functions: ${name}`);
|
|
68
|
+
return module;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/migration/files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { MigrationContext, MigrationSchema, PostgresMigration, SqliteMigration, } from "./context.js";
|
|
2
|
+
export { discoverMigrations, type MigrationFile, type MigrationModule, migrationHash, } from "./files.js";
|
|
3
|
+
export { type MigrationOptions, type MigrationResult, migrateDown, migrateLatest, migrateUp, } from "./runtime.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migration/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACX,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,eAAe,GACf,MAAM,cAAc,CAAC;AACtB,OAAO,EACN,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,aAAa,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,WAAW,EACX,aAAa,EACb,SAAS,GACT,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migration/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACN,kBAAkB,EAGlB,aAAa,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAGN,WAAW,EACX,aAAa,EACb,SAAS,GACT,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Executor } from "../query.js";
|
|
2
|
+
export interface MigrationOptions {
|
|
3
|
+
readonly dialect: "postgres" | "sqlite";
|
|
4
|
+
readonly migrationsDir: string;
|
|
5
|
+
readonly migrationTable?: string;
|
|
6
|
+
/** A dedicated connection, kept open and unused by other work until completion. */
|
|
7
|
+
readonly execute: Executor;
|
|
8
|
+
}
|
|
9
|
+
export interface MigrationResult {
|
|
10
|
+
readonly direction: "up" | "down";
|
|
11
|
+
readonly migrations: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
export declare function migrateUp(options: MigrationOptions): Promise<MigrationResult>;
|
|
14
|
+
export declare function migrateDown(options: MigrationOptions): Promise<MigrationResult>;
|
|
15
|
+
export declare function migrateLatest(options: MigrationOptions): Promise<MigrationResult>;
|
|
16
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/migration/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAkB,MAAM,aAAa,CAAC;AAI5D,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,QAAQ,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,mFAAmF;IACnF,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAOD,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAE7E;AAED,wBAAgB,WAAW,CAC1B,OAAO,EAAE,gBAAgB,GACvB,OAAO,CAAC,eAAe,CAAC,CAE1B;AAED,wBAAgB,aAAa,CAC5B,OAAO,EAAE,gBAAgB,GACvB,OAAO,CAAC,eAAe,CAAC,CAE1B"}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createMigrationContext } from "./context.js";
|
|
3
|
+
import { discoverMigrations } from "./files.js";
|
|
4
|
+
export function migrateUp(options) {
|
|
5
|
+
return migrate(options, "up", false);
|
|
6
|
+
}
|
|
7
|
+
export function migrateDown(options) {
|
|
8
|
+
return migrate(options, "down", false);
|
|
9
|
+
}
|
|
10
|
+
export function migrateLatest(options) {
|
|
11
|
+
return migrate(options, "up", true);
|
|
12
|
+
}
|
|
13
|
+
function identifier(name) {
|
|
14
|
+
if (!name || name.includes("\0"))
|
|
15
|
+
throw new Error("Migration table and schema names must be nonempty and contain no NUL");
|
|
16
|
+
return `"${name.replaceAll('"', '""')}"`;
|
|
17
|
+
}
|
|
18
|
+
async function rows(execute, text, values = []) {
|
|
19
|
+
const result = await execute(text, values);
|
|
20
|
+
return Array.isArray(result) ? result : result.rows;
|
|
21
|
+
}
|
|
22
|
+
function readHistory(records) {
|
|
23
|
+
return records.map((record) => {
|
|
24
|
+
if (typeof record !== "object" ||
|
|
25
|
+
record === null ||
|
|
26
|
+
!("name" in record) ||
|
|
27
|
+
typeof record.name !== "string" ||
|
|
28
|
+
!("hash" in record) ||
|
|
29
|
+
typeof record.hash !== "string")
|
|
30
|
+
throw new Error("Invalid migration history: expected string name and hash columns");
|
|
31
|
+
return { name: record.name, hash: record.hash };
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function validateHistory(files, applied) {
|
|
35
|
+
const byName = new Map(files.map((file) => [file.name, file]));
|
|
36
|
+
const appliedNames = new Set(applied.map((file) => file.name));
|
|
37
|
+
let latest = "";
|
|
38
|
+
for (const entry of applied) {
|
|
39
|
+
const file = byName.get(entry.name);
|
|
40
|
+
if (!file)
|
|
41
|
+
throw new Error(`Applied migration is missing: ${entry.name}`);
|
|
42
|
+
if (file.hash !== entry.hash)
|
|
43
|
+
throw new Error(`Applied migration has changed: ${entry.name}`);
|
|
44
|
+
if (entry.name > latest)
|
|
45
|
+
latest = entry.name;
|
|
46
|
+
}
|
|
47
|
+
for (const file of files)
|
|
48
|
+
if (!appliedNames.has(file.name) && file.name < latest)
|
|
49
|
+
throw new Error(`Pending migration sorts before an applied migration: ${file.name}`);
|
|
50
|
+
}
|
|
51
|
+
async function migrate(options, direction, all) {
|
|
52
|
+
if (options.dialect !== "postgres" && options.dialect !== "sqlite")
|
|
53
|
+
throw new Error(`Unsupported migration dialect: ${String(options.dialect)}`);
|
|
54
|
+
const files = await discoverMigrations(options.migrationsDir);
|
|
55
|
+
const tableName = options.migrationTable ?? "schema_migrations";
|
|
56
|
+
let table = identifier(tableName);
|
|
57
|
+
const execute = options.execute;
|
|
58
|
+
const postgres = options.dialect === "postgres";
|
|
59
|
+
let lock;
|
|
60
|
+
if (postgres) {
|
|
61
|
+
const [location] = await rows(execute, "SELECT current_schema() AS schema, current_database() AS database, current_setting('max_identifier_length')::integer AS max_identifier_length");
|
|
62
|
+
if (typeof location !== "object" ||
|
|
63
|
+
location === null ||
|
|
64
|
+
!("schema" in location) ||
|
|
65
|
+
typeof location.schema !== "string" ||
|
|
66
|
+
!("database" in location) ||
|
|
67
|
+
typeof location.database !== "string" ||
|
|
68
|
+
!("max_identifier_length" in location) ||
|
|
69
|
+
typeof location.max_identifier_length !== "number")
|
|
70
|
+
throw new Error("Cannot determine the PostgreSQL migration schema");
|
|
71
|
+
if (Buffer.byteLength(tableName, "utf8") > location.max_identifier_length)
|
|
72
|
+
throw new Error(`Migration table name exceeds PostgreSQL's ${location.max_identifier_length}-byte identifier limit`);
|
|
73
|
+
table = `${identifier(location.schema)}.${table}`;
|
|
74
|
+
lock = createHash("sha256")
|
|
75
|
+
.update(JSON.stringify([location.database, location.schema, tableName]))
|
|
76
|
+
.digest()
|
|
77
|
+
.readBigInt64BE()
|
|
78
|
+
.toString();
|
|
79
|
+
await execute("SELECT pg_advisory_lock($1::bigint)", [lock]);
|
|
80
|
+
}
|
|
81
|
+
const completed = [];
|
|
82
|
+
try {
|
|
83
|
+
for (;;) {
|
|
84
|
+
await execute(postgres ? "BEGIN" : "BEGIN IMMEDIATE", []);
|
|
85
|
+
try {
|
|
86
|
+
await execute(`CREATE TABLE IF NOT EXISTS ${table} (name TEXT PRIMARY KEY NOT NULL, hash TEXT NOT NULL, applied_at ${postgres ? "TIMESTAMPTZ" : "TEXT"} NOT NULL DEFAULT CURRENT_TIMESTAMP)`, []);
|
|
87
|
+
// SQLite must read history only after obtaining its write lock.
|
|
88
|
+
const history = readHistory(await rows(execute, `SELECT name, hash FROM ${table}`));
|
|
89
|
+
validateHistory(files, history);
|
|
90
|
+
const applied = new Set(history.map((file) => file.name));
|
|
91
|
+
const selected = direction === "up"
|
|
92
|
+
? files.find((file) => !applied.has(file.name))
|
|
93
|
+
: files.findLast((file) => applied.has(file.name));
|
|
94
|
+
if (!selected) {
|
|
95
|
+
await execute("ROLLBACK", []);
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
const migration = await selected.load();
|
|
99
|
+
let active = true;
|
|
100
|
+
const checkActiveSql = (sql) => {
|
|
101
|
+
if (!active)
|
|
102
|
+
throw new Error("Migration context cannot execute after its migration has finished");
|
|
103
|
+
assertMigrationSql(sql, options.dialect);
|
|
104
|
+
};
|
|
105
|
+
const count = execute.count;
|
|
106
|
+
const guarded = Object.assign(async (sql, values) => {
|
|
107
|
+
checkActiveSql(sql);
|
|
108
|
+
return execute(sql, values);
|
|
109
|
+
}, count === undefined
|
|
110
|
+
? {}
|
|
111
|
+
: {
|
|
112
|
+
count: async (sql, values) => {
|
|
113
|
+
checkActiveSql(sql);
|
|
114
|
+
return count(sql, values);
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
try {
|
|
118
|
+
const context = createMigrationContext(options.dialect, guarded);
|
|
119
|
+
await migration[direction](context);
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
active = false;
|
|
123
|
+
}
|
|
124
|
+
if (direction === "up")
|
|
125
|
+
await execute(`INSERT INTO ${table} (name, hash) VALUES ($1, $2)`, [
|
|
126
|
+
selected.name,
|
|
127
|
+
selected.hash,
|
|
128
|
+
]);
|
|
129
|
+
else
|
|
130
|
+
await execute(`DELETE FROM ${table} WHERE name = $1`, [
|
|
131
|
+
selected.name,
|
|
132
|
+
]);
|
|
133
|
+
await execute("COMMIT", []);
|
|
134
|
+
completed.push(selected.name);
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
try {
|
|
138
|
+
await execute("ROLLBACK", []);
|
|
139
|
+
}
|
|
140
|
+
catch (rollbackError) {
|
|
141
|
+
throw new AggregateError([error, rollbackError], "Migration failed and rollback also failed");
|
|
142
|
+
}
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
if (!all)
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
return { direction, migrations: completed };
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
if (lock !== undefined)
|
|
152
|
+
await execute("SELECT pg_advisory_unlock($1::bigint)", [lock]);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/** Keep transaction ownership with the runner, including multi-statement raw SQL. */
|
|
156
|
+
function assertMigrationSql(sql, dialect) {
|
|
157
|
+
const words = [];
|
|
158
|
+
const checkRollback = () => {
|
|
159
|
+
if (words[0] === "ROLLBACK" &&
|
|
160
|
+
words[1] !== "TO" &&
|
|
161
|
+
!((words[1] === "WORK" || words[1] === "TRANSACTION") &&
|
|
162
|
+
words[2] === "TO"))
|
|
163
|
+
throw new Error("Migrations cannot roll back their enclosing transaction; use ROLLBACK TO a savepoint");
|
|
164
|
+
};
|
|
165
|
+
let trigger = false;
|
|
166
|
+
let start = true;
|
|
167
|
+
for (let i = 0; i < sql.length;) {
|
|
168
|
+
const char = sql.charAt(i);
|
|
169
|
+
if (/\s/.test(char)) {
|
|
170
|
+
i++;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (sql.startsWith("--", i)) {
|
|
174
|
+
const end = /[\r\n]/.exec(sql.slice(i + 2));
|
|
175
|
+
i = end === null ? sql.length : i + 2 + end.index + 1;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (sql.startsWith("/*", i)) {
|
|
179
|
+
let depth = 1;
|
|
180
|
+
i += 2;
|
|
181
|
+
while (i < sql.length && depth > 0) {
|
|
182
|
+
if (sql.startsWith("/*", i) && dialect === "postgres") {
|
|
183
|
+
depth++;
|
|
184
|
+
i += 2;
|
|
185
|
+
}
|
|
186
|
+
else if (sql.startsWith("*/", i)) {
|
|
187
|
+
depth--;
|
|
188
|
+
i += 2;
|
|
189
|
+
}
|
|
190
|
+
else
|
|
191
|
+
i++;
|
|
192
|
+
}
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (char === "'" ||
|
|
196
|
+
char === '"' ||
|
|
197
|
+
(dialect === "sqlite" && (char === "`" || char === "["))) {
|
|
198
|
+
const end = char === "[" ? "]" : char;
|
|
199
|
+
const escapes = dialect === "postgres" &&
|
|
200
|
+
char === "'" &&
|
|
201
|
+
/[eE]/.test(sql[i - 1] ?? "") &&
|
|
202
|
+
!/[\w$]/.test(sql[i - 2] ?? "");
|
|
203
|
+
i++;
|
|
204
|
+
while (i < sql.length) {
|
|
205
|
+
if (escapes && sql[i] === "\\")
|
|
206
|
+
i += 2;
|
|
207
|
+
else if (sql[i] === end) {
|
|
208
|
+
if (sql[i + 1] === end && char !== "[")
|
|
209
|
+
i += 2;
|
|
210
|
+
else {
|
|
211
|
+
i++;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else
|
|
216
|
+
i++;
|
|
217
|
+
}
|
|
218
|
+
if (words.length < 4)
|
|
219
|
+
words.push("");
|
|
220
|
+
start = false;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (char === "$" && dialect === "postgres") {
|
|
224
|
+
const tag = /^\$(?:[a-zA-Z_][a-zA-Z_0-9]*)?\$/.exec(sql.slice(i))?.[0];
|
|
225
|
+
if (tag) {
|
|
226
|
+
const end = sql.indexOf(tag, i + tag.length);
|
|
227
|
+
i = end < 0 ? sql.length : end + tag.length;
|
|
228
|
+
if (words.length < 4)
|
|
229
|
+
words.push("");
|
|
230
|
+
start = false;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (char === ";") {
|
|
235
|
+
checkRollback();
|
|
236
|
+
words.length = 0;
|
|
237
|
+
start = true;
|
|
238
|
+
i++;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const word = /^[a-zA-Z_][a-zA-Z_0-9$]*/.exec(sql.slice(i))?.[0];
|
|
242
|
+
if (word) {
|
|
243
|
+
const upper = word.toUpperCase();
|
|
244
|
+
if (trigger && start && upper === "END")
|
|
245
|
+
trigger = false;
|
|
246
|
+
else if (!trigger &&
|
|
247
|
+
start &&
|
|
248
|
+
/^(BEGIN|START|COMMIT|END|ABORT)$/.test(upper))
|
|
249
|
+
throw new Error("Migrations cannot control transactions; each migration runs in its own transaction");
|
|
250
|
+
if (words.length < 4)
|
|
251
|
+
words.push(upper);
|
|
252
|
+
if (words.length === 2 &&
|
|
253
|
+
words[0] === "PREPARE" &&
|
|
254
|
+
upper === "TRANSACTION")
|
|
255
|
+
throw new Error("Migrations cannot prepare transactions");
|
|
256
|
+
if (dialect === "sqlite" &&
|
|
257
|
+
upper === "TRIGGER" &&
|
|
258
|
+
(words.join(" ") === "CREATE TRIGGER" ||
|
|
259
|
+
words.join(" ") === "CREATE TEMP TRIGGER" ||
|
|
260
|
+
words.join(" ") === "CREATE TEMPORARY TRIGGER"))
|
|
261
|
+
trigger = true;
|
|
262
|
+
start = false;
|
|
263
|
+
i += word.length;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (words.length < 4)
|
|
267
|
+
words.push("");
|
|
268
|
+
start = false;
|
|
269
|
+
i++;
|
|
270
|
+
}
|
|
271
|
+
checkRollback();
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/migration/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,7 +17,7 @@ export interface DdlCache {
|
|
|
17
17
|
}
|
|
18
18
|
export interface DdlCacheKeys {
|
|
19
19
|
}
|
|
20
|
-
export type CacheVersion = "
|
|
20
|
+
export type CacheVersion = "19a89f093fa1";
|
|
21
21
|
export type Stale<DB, D extends DbSnapshot> = (D["v"] extends CacheVersion ? never : "*") | (Same<EnumsOf<DB>, D["t"]> extends true ? never : "*") | {
|
|
22
22
|
[W in keyof D["w"] & string]: WordFresh<DB, W, D["w"][W]> extends true ? never : W;
|
|
23
23
|
}[keyof D["w"] & string] | {
|
|
@@ -15,7 +15,7 @@ export interface DdlCache {
|
|
|
15
15
|
}
|
|
16
16
|
export interface DdlCacheKeys {
|
|
17
17
|
}
|
|
18
|
-
export type CacheVersion = "
|
|
18
|
+
export type CacheVersion = "19a89f093fa1";
|
|
19
19
|
export type Stale<DB, D extends DbSnapshot> = (D["v"] extends CacheVersion ? never : "*") | {
|
|
20
20
|
[W in keyof D["w"] & string]: Same<Relations<DB, W>, D["w"][W][0]> extends true ? Same<Named<Functions<DB>, W>, D["w"][W][1]> extends true ? Same<Named<FunctionCatalog, W>, D["w"][W][2]> extends true ? never : W : W : W;
|
|
21
21
|
}[keyof D["w"] & string] | Shared.StaleExtras<DB, D, HintIndex<DB>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typesql/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Type-level SQL inference for TypeScript: PostgreSQL (tested against PostgreSQL 18's regression suite) and SQLite. Zero runtime dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -36,6 +36,10 @@
|
|
|
36
36
|
"./sqlite/engine": {
|
|
37
37
|
"types": "./dist/sqlite/engine/index.d.ts",
|
|
38
38
|
"default": "./dist/sqlite/engine/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./migration": {
|
|
41
|
+
"types": "./dist/migration/index.d.ts",
|
|
42
|
+
"default": "./dist/migration/index.js"
|
|
39
43
|
}
|
|
40
44
|
},
|
|
41
45
|
"files": [
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { createDialectSql } from "../create-sql.js";
|
|
2
|
+
import type { ApplyDdlOf, DialectTypes, ValidateDdlOf } from "../dialect.js";
|
|
3
|
+
import type { EngineTypes } from "../engine/contract.js";
|
|
4
|
+
import type { ParamsOf, RowOf } from "../engine/infer.js";
|
|
5
|
+
import { type Postgres, postgres } from "../postgres/dialect.js";
|
|
6
|
+
import type { PgEngine } from "../postgres/engine/engine.js";
|
|
7
|
+
import type { Executor, Query } from "../query.js";
|
|
8
|
+
import type { SqlApi } from "../sql/api.js";
|
|
9
|
+
import type {
|
|
10
|
+
BoxCallArgs,
|
|
11
|
+
BoxSlottedArgs,
|
|
12
|
+
CallRet,
|
|
13
|
+
GateCallCheck,
|
|
14
|
+
GateSlottedCheck,
|
|
15
|
+
SplicedOr,
|
|
16
|
+
} from "../sql/call.js";
|
|
17
|
+
import type { SlotMap, SlotsFor } from "../sql/slots.js";
|
|
18
|
+
import { type Sqlite, sqlite } from "../sqlite/dialect.js";
|
|
19
|
+
import type { SqliteEngine } from "../sqlite/engine/engine.js";
|
|
20
|
+
|
|
21
|
+
type GateDdl<T extends DialectTypes, DB, Q extends string> = [DB, Q] extends [
|
|
22
|
+
infer D,
|
|
23
|
+
infer Text extends string,
|
|
24
|
+
]
|
|
25
|
+
? ValidateDdlOf<T, D, Text>
|
|
26
|
+
: never;
|
|
27
|
+
|
|
28
|
+
/** A checked SQL executor whose schema evolves after each awaited DDL statement. */
|
|
29
|
+
export interface MigrationContext<
|
|
30
|
+
E extends EngineTypes,
|
|
31
|
+
T extends DialectTypes,
|
|
32
|
+
DB,
|
|
33
|
+
TM = T["typeMap"],
|
|
34
|
+
> extends Omit<SqlApi<E, DB, TM>, "raw"> {
|
|
35
|
+
/** Type-only schema marker; no database introspection is performed. */
|
|
36
|
+
readonly schema: DB;
|
|
37
|
+
<const Q extends string>(
|
|
38
|
+
query: GateCallCheck<E, DB, TM, "preserve", Q> & Q,
|
|
39
|
+
...params: BoxCallArgs<E, DB, TM, "preserve", Q>
|
|
40
|
+
): CallRet<E, DB, TM, "preserve", Q>;
|
|
41
|
+
<const Q extends string, const S extends SlotMap>(
|
|
42
|
+
query: GateSlottedCheck<E, DB, TM, Q, S> & Q,
|
|
43
|
+
slots: S & SlotsFor<Q, S>,
|
|
44
|
+
...params: BoxSlottedArgs<E, DB, TM, Q, S>
|
|
45
|
+
): Query<
|
|
46
|
+
RowOf<E, DB, TM, SplicedOr<Q, S>>,
|
|
47
|
+
ParamsOf<E, DB, TM, SplicedOr<Q, S>>,
|
|
48
|
+
SplicedOr<Q, S>
|
|
49
|
+
>;
|
|
50
|
+
add<const Q extends string>(
|
|
51
|
+
ddl: GateDdl<T, DB, Q> & Q,
|
|
52
|
+
): Promise<MigrationContext<E, T, ApplyDdlOf<T, DB, Q>, TM>>;
|
|
53
|
+
/** Execute SQL the checker cannot see, explicitly asserting its resulting schema. */
|
|
54
|
+
raw<NewSchema = DB>(
|
|
55
|
+
sql: string,
|
|
56
|
+
): Promise<MigrationContext<E, T, NewSchema, TM>>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type PostgresMigration<Base = {}> = MigrationContext<
|
|
60
|
+
PgEngine,
|
|
61
|
+
Postgres,
|
|
62
|
+
Base
|
|
63
|
+
>;
|
|
64
|
+
export type SqliteMigration<Base = {}> = MigrationContext<
|
|
65
|
+
SqliteEngine,
|
|
66
|
+
Sqlite,
|
|
67
|
+
Base
|
|
68
|
+
>;
|
|
69
|
+
|
|
70
|
+
/** Extract the final schema returned by an async migration's up function. */
|
|
71
|
+
export type MigrationSchema<Up extends (...args: never[]) => unknown> =
|
|
72
|
+
Awaited<ReturnType<Up>> extends { readonly schema: infer Schema }
|
|
73
|
+
? Schema
|
|
74
|
+
: never;
|
|
75
|
+
|
|
76
|
+
/** Internal factory: execution is bound to the runner's active transaction. */
|
|
77
|
+
export function createMigrationContext(
|
|
78
|
+
dialect: "postgres" | "sqlite",
|
|
79
|
+
execute: Executor,
|
|
80
|
+
): PostgresMigration | SqliteMigration {
|
|
81
|
+
const sql =
|
|
82
|
+
dialect === "postgres"
|
|
83
|
+
? createDialectSql(postgres, execute)
|
|
84
|
+
: createDialectSql(sqlite, execute);
|
|
85
|
+
const next = async (statement: string) => {
|
|
86
|
+
await execute(statement, []);
|
|
87
|
+
return createMigrationContext(dialect, execute);
|
|
88
|
+
};
|
|
89
|
+
return Object.assign(sql, { add: next, raw: next }) as unknown as
|
|
90
|
+
| PostgresMigration
|
|
91
|
+
| SqliteMigration;
|
|
92
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
3
|
+
import { stripTypeScriptTypes } from "node:module";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { formatSyntaxKind, type Node } from "typescript/ast";
|
|
7
|
+
import { API } from "typescript/sync";
|
|
8
|
+
|
|
9
|
+
/** A discovered TypeScript migration. Loading uses Node's native TypeScript support. */
|
|
10
|
+
export interface MigrationFile {
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly path: string;
|
|
13
|
+
readonly hash: string;
|
|
14
|
+
load(): Promise<MigrationModule>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface MigrationModule {
|
|
18
|
+
readonly up: (context: never) => unknown;
|
|
19
|
+
readonly down: (context: never) => unknown;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Hash the erased JavaScript's syntax tree and original runtime token spellings.
|
|
24
|
+
* Tree boundaries preserve automatic semicolon insertion (including return/newline)
|
|
25
|
+
* while trivia and TypeScript-only declarations do not contribute to the hash.
|
|
26
|
+
*/
|
|
27
|
+
export function migrationHash(source: string): string {
|
|
28
|
+
const javascript = stripTypeScriptTypes(source);
|
|
29
|
+
const api = new API();
|
|
30
|
+
try {
|
|
31
|
+
const lease = api.createSourceFile("migration.js", javascript);
|
|
32
|
+
const file = lease.sourceFile;
|
|
33
|
+
const hash = createHash("sha256");
|
|
34
|
+
const visit = (node: Node): void => {
|
|
35
|
+
if (formatSyntaxKind(node.kind).startsWith("JSDoc")) return;
|
|
36
|
+
const children = node.getChildren(file);
|
|
37
|
+
hash.update(`${formatSyntaxKind(node.kind)}[`);
|
|
38
|
+
if (children.length === 0)
|
|
39
|
+
hash.update(JSON.stringify(node.getText(file)));
|
|
40
|
+
else for (const child of children) visit(child);
|
|
41
|
+
hash.update("]");
|
|
42
|
+
};
|
|
43
|
+
visit(file);
|
|
44
|
+
return hash.digest("hex");
|
|
45
|
+
} finally {
|
|
46
|
+
api.close();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function discoverMigrations(
|
|
51
|
+
migrationsDir: string,
|
|
52
|
+
): Promise<readonly MigrationFile[]> {
|
|
53
|
+
const directory = resolve(migrationsDir);
|
|
54
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
55
|
+
const names = entries
|
|
56
|
+
.filter(
|
|
57
|
+
(entry) =>
|
|
58
|
+
(entry.isFile() || entry.isSymbolicLink()) &&
|
|
59
|
+
entry.name.endsWith(".ts"),
|
|
60
|
+
)
|
|
61
|
+
.map((entry) => entry.name)
|
|
62
|
+
.sort();
|
|
63
|
+
return Promise.all(
|
|
64
|
+
names.map(async (name): Promise<MigrationFile> => {
|
|
65
|
+
const path = resolve(directory, name);
|
|
66
|
+
const source = await readFile(path, "utf8");
|
|
67
|
+
const hash = migrationHash(source);
|
|
68
|
+
return {
|
|
69
|
+
name,
|
|
70
|
+
path,
|
|
71
|
+
hash,
|
|
72
|
+
async load() {
|
|
73
|
+
if (migrationHash(await readFile(path, "utf8")) !== hash)
|
|
74
|
+
throw new Error(`Migration changed during execution: ${name}`);
|
|
75
|
+
const url = pathToFileURL(path);
|
|
76
|
+
url.searchParams.set("typesql", hash);
|
|
77
|
+
const module: unknown = await import(url.href);
|
|
78
|
+
if (
|
|
79
|
+
module === null ||
|
|
80
|
+
typeof module !== "object" ||
|
|
81
|
+
!("up" in module) ||
|
|
82
|
+
typeof module.up !== "function" ||
|
|
83
|
+
!("down" in module) ||
|
|
84
|
+
typeof module.down !== "function"
|
|
85
|
+
)
|
|
86
|
+
throw new Error(
|
|
87
|
+
`Migration must export up and down functions: ${name}`,
|
|
88
|
+
);
|
|
89
|
+
return module as MigrationModule;
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
MigrationContext,
|
|
3
|
+
MigrationSchema,
|
|
4
|
+
PostgresMigration,
|
|
5
|
+
SqliteMigration,
|
|
6
|
+
} from "./context.js";
|
|
7
|
+
export {
|
|
8
|
+
discoverMigrations,
|
|
9
|
+
type MigrationFile,
|
|
10
|
+
type MigrationModule,
|
|
11
|
+
migrationHash,
|
|
12
|
+
} from "./files.js";
|
|
13
|
+
export {
|
|
14
|
+
type MigrationOptions,
|
|
15
|
+
type MigrationResult,
|
|
16
|
+
migrateDown,
|
|
17
|
+
migrateLatest,
|
|
18
|
+
migrateUp,
|
|
19
|
+
} from "./runtime.js";
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import type { Executor, ExecutorResult } from "../query.js";
|
|
3
|
+
import { createMigrationContext } from "./context.js";
|
|
4
|
+
import { discoverMigrations, type MigrationFile } from "./files.js";
|
|
5
|
+
|
|
6
|
+
export interface MigrationOptions {
|
|
7
|
+
readonly dialect: "postgres" | "sqlite";
|
|
8
|
+
readonly migrationsDir: string;
|
|
9
|
+
readonly migrationTable?: string;
|
|
10
|
+
/** A dedicated connection, kept open and unused by other work until completion. */
|
|
11
|
+
readonly execute: Executor;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface MigrationResult {
|
|
15
|
+
readonly direction: "up" | "down";
|
|
16
|
+
readonly migrations: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface AppliedMigration {
|
|
20
|
+
readonly name: string;
|
|
21
|
+
readonly hash: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function migrateUp(options: MigrationOptions): Promise<MigrationResult> {
|
|
25
|
+
return migrate(options, "up", false);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function migrateDown(
|
|
29
|
+
options: MigrationOptions,
|
|
30
|
+
): Promise<MigrationResult> {
|
|
31
|
+
return migrate(options, "down", false);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function migrateLatest(
|
|
35
|
+
options: MigrationOptions,
|
|
36
|
+
): Promise<MigrationResult> {
|
|
37
|
+
return migrate(options, "up", true);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function identifier(name: string): string {
|
|
41
|
+
if (!name || name.includes("\0"))
|
|
42
|
+
throw new Error(
|
|
43
|
+
"Migration table and schema names must be nonempty and contain no NUL",
|
|
44
|
+
);
|
|
45
|
+
return `"${name.replaceAll('"', '""')}"`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function rows(
|
|
49
|
+
execute: Executor,
|
|
50
|
+
text: string,
|
|
51
|
+
values: readonly unknown[] = [],
|
|
52
|
+
): Promise<readonly unknown[]> {
|
|
53
|
+
const result = await execute(text, values);
|
|
54
|
+
return Array.isArray(result) ? result : (result as ExecutorResult).rows;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readHistory(records: readonly unknown[]): AppliedMigration[] {
|
|
58
|
+
return records.map((record) => {
|
|
59
|
+
if (
|
|
60
|
+
typeof record !== "object" ||
|
|
61
|
+
record === null ||
|
|
62
|
+
!("name" in record) ||
|
|
63
|
+
typeof record.name !== "string" ||
|
|
64
|
+
!("hash" in record) ||
|
|
65
|
+
typeof record.hash !== "string"
|
|
66
|
+
)
|
|
67
|
+
throw new Error(
|
|
68
|
+
"Invalid migration history: expected string name and hash columns",
|
|
69
|
+
);
|
|
70
|
+
return { name: record.name, hash: record.hash };
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function validateHistory(
|
|
75
|
+
files: readonly MigrationFile[],
|
|
76
|
+
applied: readonly AppliedMigration[],
|
|
77
|
+
): void {
|
|
78
|
+
const byName = new Map(files.map((file) => [file.name, file]));
|
|
79
|
+
const appliedNames = new Set(applied.map((file) => file.name));
|
|
80
|
+
let latest = "";
|
|
81
|
+
for (const entry of applied) {
|
|
82
|
+
const file = byName.get(entry.name);
|
|
83
|
+
if (!file) throw new Error(`Applied migration is missing: ${entry.name}`);
|
|
84
|
+
if (file.hash !== entry.hash)
|
|
85
|
+
throw new Error(`Applied migration has changed: ${entry.name}`);
|
|
86
|
+
if (entry.name > latest) latest = entry.name;
|
|
87
|
+
}
|
|
88
|
+
for (const file of files)
|
|
89
|
+
if (!appliedNames.has(file.name) && file.name < latest)
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Pending migration sorts before an applied migration: ${file.name}`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function migrate(
|
|
96
|
+
options: MigrationOptions,
|
|
97
|
+
direction: "up" | "down",
|
|
98
|
+
all: boolean,
|
|
99
|
+
): Promise<MigrationResult> {
|
|
100
|
+
if (options.dialect !== "postgres" && options.dialect !== "sqlite")
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Unsupported migration dialect: ${String(options.dialect)}`,
|
|
103
|
+
);
|
|
104
|
+
const files = await discoverMigrations(options.migrationsDir);
|
|
105
|
+
const tableName = options.migrationTable ?? "schema_migrations";
|
|
106
|
+
let table = identifier(tableName);
|
|
107
|
+
const execute = options.execute;
|
|
108
|
+
const postgres = options.dialect === "postgres";
|
|
109
|
+
let lock: string | undefined;
|
|
110
|
+
if (postgres) {
|
|
111
|
+
const [location] = await rows(
|
|
112
|
+
execute,
|
|
113
|
+
"SELECT current_schema() AS schema, current_database() AS database, current_setting('max_identifier_length')::integer AS max_identifier_length",
|
|
114
|
+
);
|
|
115
|
+
if (
|
|
116
|
+
typeof location !== "object" ||
|
|
117
|
+
location === null ||
|
|
118
|
+
!("schema" in location) ||
|
|
119
|
+
typeof location.schema !== "string" ||
|
|
120
|
+
!("database" in location) ||
|
|
121
|
+
typeof location.database !== "string" ||
|
|
122
|
+
!("max_identifier_length" in location) ||
|
|
123
|
+
typeof location.max_identifier_length !== "number"
|
|
124
|
+
)
|
|
125
|
+
throw new Error("Cannot determine the PostgreSQL migration schema");
|
|
126
|
+
if (Buffer.byteLength(tableName, "utf8") > location.max_identifier_length)
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Migration table name exceeds PostgreSQL's ${location.max_identifier_length}-byte identifier limit`,
|
|
129
|
+
);
|
|
130
|
+
table = `${identifier(location.schema)}.${table}`;
|
|
131
|
+
lock = createHash("sha256")
|
|
132
|
+
.update(JSON.stringify([location.database, location.schema, tableName]))
|
|
133
|
+
.digest()
|
|
134
|
+
.readBigInt64BE()
|
|
135
|
+
.toString();
|
|
136
|
+
await execute("SELECT pg_advisory_lock($1::bigint)", [lock]);
|
|
137
|
+
}
|
|
138
|
+
const completed: string[] = [];
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
for (;;) {
|
|
142
|
+
await execute(postgres ? "BEGIN" : "BEGIN IMMEDIATE", []);
|
|
143
|
+
try {
|
|
144
|
+
await execute(
|
|
145
|
+
`CREATE TABLE IF NOT EXISTS ${table} (name TEXT PRIMARY KEY NOT NULL, hash TEXT NOT NULL, applied_at ${postgres ? "TIMESTAMPTZ" : "TEXT"} NOT NULL DEFAULT CURRENT_TIMESTAMP)`,
|
|
146
|
+
[],
|
|
147
|
+
);
|
|
148
|
+
// SQLite must read history only after obtaining its write lock.
|
|
149
|
+
const history = readHistory(
|
|
150
|
+
await rows(execute, `SELECT name, hash FROM ${table}`),
|
|
151
|
+
);
|
|
152
|
+
validateHistory(files, history);
|
|
153
|
+
const applied = new Set(history.map((file) => file.name));
|
|
154
|
+
const selected =
|
|
155
|
+
direction === "up"
|
|
156
|
+
? files.find((file) => !applied.has(file.name))
|
|
157
|
+
: files.findLast((file) => applied.has(file.name));
|
|
158
|
+
if (!selected) {
|
|
159
|
+
await execute("ROLLBACK", []);
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
const migration = await selected.load();
|
|
163
|
+
let active = true;
|
|
164
|
+
const checkActiveSql = (sql: string): void => {
|
|
165
|
+
if (!active)
|
|
166
|
+
throw new Error(
|
|
167
|
+
"Migration context cannot execute after its migration has finished",
|
|
168
|
+
);
|
|
169
|
+
assertMigrationSql(sql, options.dialect);
|
|
170
|
+
};
|
|
171
|
+
const count = execute.count;
|
|
172
|
+
const guarded: Executor = Object.assign(
|
|
173
|
+
async (sql: string, values: readonly unknown[]) => {
|
|
174
|
+
checkActiveSql(sql);
|
|
175
|
+
return execute(sql, values);
|
|
176
|
+
},
|
|
177
|
+
count === undefined
|
|
178
|
+
? {}
|
|
179
|
+
: {
|
|
180
|
+
count: async (sql: string, values: readonly unknown[]) => {
|
|
181
|
+
checkActiveSql(sql);
|
|
182
|
+
return count(sql, values);
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
try {
|
|
187
|
+
const context = createMigrationContext(options.dialect, guarded);
|
|
188
|
+
await migration[direction](context as never);
|
|
189
|
+
} finally {
|
|
190
|
+
active = false;
|
|
191
|
+
}
|
|
192
|
+
if (direction === "up")
|
|
193
|
+
await execute(`INSERT INTO ${table} (name, hash) VALUES ($1, $2)`, [
|
|
194
|
+
selected.name,
|
|
195
|
+
selected.hash,
|
|
196
|
+
]);
|
|
197
|
+
else
|
|
198
|
+
await execute(`DELETE FROM ${table} WHERE name = $1`, [
|
|
199
|
+
selected.name,
|
|
200
|
+
]);
|
|
201
|
+
await execute("COMMIT", []);
|
|
202
|
+
completed.push(selected.name);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
try {
|
|
205
|
+
await execute("ROLLBACK", []);
|
|
206
|
+
} catch (rollbackError) {
|
|
207
|
+
throw new AggregateError(
|
|
208
|
+
[error, rollbackError],
|
|
209
|
+
"Migration failed and rollback also failed",
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
if (!all) break;
|
|
215
|
+
}
|
|
216
|
+
return { direction, migrations: completed };
|
|
217
|
+
} finally {
|
|
218
|
+
if (lock !== undefined)
|
|
219
|
+
await execute("SELECT pg_advisory_unlock($1::bigint)", [lock]);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Keep transaction ownership with the runner, including multi-statement raw SQL. */
|
|
224
|
+
function assertMigrationSql(
|
|
225
|
+
sql: string,
|
|
226
|
+
dialect: MigrationOptions["dialect"],
|
|
227
|
+
): void {
|
|
228
|
+
const words: string[] = [];
|
|
229
|
+
const checkRollback = (): void => {
|
|
230
|
+
if (
|
|
231
|
+
words[0] === "ROLLBACK" &&
|
|
232
|
+
words[1] !== "TO" &&
|
|
233
|
+
!(
|
|
234
|
+
(words[1] === "WORK" || words[1] === "TRANSACTION") &&
|
|
235
|
+
words[2] === "TO"
|
|
236
|
+
)
|
|
237
|
+
)
|
|
238
|
+
throw new Error(
|
|
239
|
+
"Migrations cannot roll back their enclosing transaction; use ROLLBACK TO a savepoint",
|
|
240
|
+
);
|
|
241
|
+
};
|
|
242
|
+
let trigger = false;
|
|
243
|
+
let start = true;
|
|
244
|
+
for (let i = 0; i < sql.length; ) {
|
|
245
|
+
const char = sql.charAt(i);
|
|
246
|
+
if (/\s/.test(char)) {
|
|
247
|
+
i++;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (sql.startsWith("--", i)) {
|
|
251
|
+
const end = /[\r\n]/.exec(sql.slice(i + 2));
|
|
252
|
+
i = end === null ? sql.length : i + 2 + end.index + 1;
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (sql.startsWith("/*", i)) {
|
|
256
|
+
let depth = 1;
|
|
257
|
+
i += 2;
|
|
258
|
+
while (i < sql.length && depth > 0) {
|
|
259
|
+
if (sql.startsWith("/*", i) && dialect === "postgres") {
|
|
260
|
+
depth++;
|
|
261
|
+
i += 2;
|
|
262
|
+
} else if (sql.startsWith("*/", i)) {
|
|
263
|
+
depth--;
|
|
264
|
+
i += 2;
|
|
265
|
+
} else i++;
|
|
266
|
+
}
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (
|
|
270
|
+
char === "'" ||
|
|
271
|
+
char === '"' ||
|
|
272
|
+
(dialect === "sqlite" && (char === "`" || char === "["))
|
|
273
|
+
) {
|
|
274
|
+
const end = char === "[" ? "]" : char;
|
|
275
|
+
const escapes =
|
|
276
|
+
dialect === "postgres" &&
|
|
277
|
+
char === "'" &&
|
|
278
|
+
/[eE]/.test(sql[i - 1] ?? "") &&
|
|
279
|
+
!/[\w$]/.test(sql[i - 2] ?? "");
|
|
280
|
+
i++;
|
|
281
|
+
while (i < sql.length) {
|
|
282
|
+
if (escapes && sql[i] === "\\") i += 2;
|
|
283
|
+
else if (sql[i] === end) {
|
|
284
|
+
if (sql[i + 1] === end && char !== "[") i += 2;
|
|
285
|
+
else {
|
|
286
|
+
i++;
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
} else i++;
|
|
290
|
+
}
|
|
291
|
+
if (words.length < 4) words.push("");
|
|
292
|
+
start = false;
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if (char === "$" && dialect === "postgres") {
|
|
296
|
+
const tag = /^\$(?:[a-zA-Z_][a-zA-Z_0-9]*)?\$/.exec(sql.slice(i))?.[0];
|
|
297
|
+
if (tag) {
|
|
298
|
+
const end = sql.indexOf(tag, i + tag.length);
|
|
299
|
+
i = end < 0 ? sql.length : end + tag.length;
|
|
300
|
+
if (words.length < 4) words.push("");
|
|
301
|
+
start = false;
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (char === ";") {
|
|
306
|
+
checkRollback();
|
|
307
|
+
words.length = 0;
|
|
308
|
+
start = true;
|
|
309
|
+
i++;
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
const word = /^[a-zA-Z_][a-zA-Z_0-9$]*/.exec(sql.slice(i))?.[0];
|
|
313
|
+
if (word) {
|
|
314
|
+
const upper = word.toUpperCase();
|
|
315
|
+
if (trigger && start && upper === "END") trigger = false;
|
|
316
|
+
else if (
|
|
317
|
+
!trigger &&
|
|
318
|
+
start &&
|
|
319
|
+
/^(BEGIN|START|COMMIT|END|ABORT)$/.test(upper)
|
|
320
|
+
)
|
|
321
|
+
throw new Error(
|
|
322
|
+
"Migrations cannot control transactions; each migration runs in its own transaction",
|
|
323
|
+
);
|
|
324
|
+
if (words.length < 4) words.push(upper);
|
|
325
|
+
if (
|
|
326
|
+
words.length === 2 &&
|
|
327
|
+
words[0] === "PREPARE" &&
|
|
328
|
+
upper === "TRANSACTION"
|
|
329
|
+
)
|
|
330
|
+
throw new Error("Migrations cannot prepare transactions");
|
|
331
|
+
if (
|
|
332
|
+
dialect === "sqlite" &&
|
|
333
|
+
upper === "TRIGGER" &&
|
|
334
|
+
(words.join(" ") === "CREATE TRIGGER" ||
|
|
335
|
+
words.join(" ") === "CREATE TEMP TRIGGER" ||
|
|
336
|
+
words.join(" ") === "CREATE TEMPORARY TRIGGER")
|
|
337
|
+
)
|
|
338
|
+
trigger = true;
|
|
339
|
+
start = false;
|
|
340
|
+
i += word.length;
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (words.length < 4) words.push("");
|
|
344
|
+
start = false;
|
|
345
|
+
i++;
|
|
346
|
+
}
|
|
347
|
+
checkRollback();
|
|
348
|
+
}
|
|
@@ -26,7 +26,7 @@ export interface CacheSnapshots {}
|
|
|
26
26
|
export interface DdlCache {}
|
|
27
27
|
export interface DdlCacheKeys {}
|
|
28
28
|
|
|
29
|
-
export type CacheVersion = "
|
|
29
|
+
export type CacheVersion = "19a89f093fa1";
|
|
30
30
|
|
|
31
31
|
export type Stale<DB, D extends DbSnapshot> =
|
|
32
32
|
| (D["v"] extends CacheVersion ? never : "*")
|
|
@@ -20,7 +20,7 @@ export interface CacheSnapshots {}
|
|
|
20
20
|
export interface DdlCache {}
|
|
21
21
|
export interface DdlCacheKeys {}
|
|
22
22
|
|
|
23
|
-
export type CacheVersion = "
|
|
23
|
+
export type CacheVersion = "19a89f093fa1";
|
|
24
24
|
|
|
25
25
|
export type Stale<DB, D extends DbSnapshot> =
|
|
26
26
|
| (D["v"] extends CacheVersion ? never : "*")
|