@fougere/adapter-sql 0.2.0-alpha.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/LICENSE +21 -0
- package/README.md +18 -0
- package/dist/check.d.ts +41 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +67 -0
- package/dist/check.js.map +1 -0
- package/dist/crud.d.ts +153 -0
- package/dist/crud.d.ts.map +1 -0
- package/dist/crud.js +458 -0
- package/dist/crud.js.map +1 -0
- package/dist/ddl.d.ts +87 -0
- package/dist/ddl.d.ts.map +1 -0
- package/dist/ddl.js +194 -0
- package/dist/ddl.js.map +1 -0
- package/dist/dialect.d.ts +54 -0
- package/dist/dialect.d.ts.map +1 -0
- package/dist/dialect.js +109 -0
- package/dist/dialect.js.map +1 -0
- package/dist/diff.d.ts +86 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +168 -0
- package/dist/diff.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/setup.d.ts +37 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +40 -0
- package/dist/setup.js.map +1 -0
- package/dist/table.d.ts +169 -0
- package/dist/table.d.ts.map +1 -0
- package/dist/table.js +292 -0
- package/dist/table.js.map +1 -0
- package/dist/values.d.ts +32 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +70 -0
- package/dist/values.js.map +1 -0
- package/package.json +56 -0
package/dist/ddl.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DDL — the table description, rendered as SQL.
|
|
3
|
+
*
|
|
4
|
+
* Kysely's schema builder is what makes this dialect-agnostic: it owns the
|
|
5
|
+
* identifier quoting and the per-engine syntax, so this module only decides
|
|
6
|
+
* *what* to emit. Compilation needs no connection — a `DummyDriver` paired with
|
|
7
|
+
* a real query compiler renders the statement for any engine, which is why the
|
|
8
|
+
* whole surface is testable without a database.
|
|
9
|
+
*/
|
|
10
|
+
import { Kysely, DummyDriver, sql, SqliteAdapter, SqliteQueryCompiler, SqliteIntrospector, PostgresAdapter, PostgresQueryCompiler, PostgresIntrospector, MysqlAdapter, MysqlQueryCompiler, MysqlIntrospector, MssqlAdapter, MssqlQueryCompiler, MssqlIntrospector, } from 'kysely';
|
|
11
|
+
import { isKeyed, orderTables, toTableName, toTables, } from './table.js';
|
|
12
|
+
import { resolveDialect } from './dialect.js';
|
|
13
|
+
import { checkFor } from './check.js';
|
|
14
|
+
// ─── Compile-only engines ──────────────────────────
|
|
15
|
+
const parts = {
|
|
16
|
+
sqlite: [SqliteAdapter, SqliteQueryCompiler, SqliteIntrospector],
|
|
17
|
+
pg: [PostgresAdapter, PostgresQueryCompiler, PostgresIntrospector],
|
|
18
|
+
mysql: [MysqlAdapter, MysqlQueryCompiler, MysqlIntrospector],
|
|
19
|
+
mssql: [MssqlAdapter, MssqlQueryCompiler, MssqlIntrospector],
|
|
20
|
+
};
|
|
21
|
+
const engines = new Map();
|
|
22
|
+
/** A Kysely bound to a dialect's compiler but to no connection — renders SQL only. */
|
|
23
|
+
export function compiler(name) {
|
|
24
|
+
const cached = engines.get(name);
|
|
25
|
+
if (cached)
|
|
26
|
+
return cached;
|
|
27
|
+
const [Adapter, QueryCompiler, Introspector] = parts[name];
|
|
28
|
+
const engine = new Kysely({
|
|
29
|
+
dialect: {
|
|
30
|
+
createAdapter: () => new Adapter(),
|
|
31
|
+
createDriver: () => new DummyDriver(),
|
|
32
|
+
createIntrospector: (db) => new Introspector(db),
|
|
33
|
+
createQueryCompiler: () => new QueryCompiler(),
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
engines.set(name, engine);
|
|
37
|
+
return engine;
|
|
38
|
+
}
|
|
39
|
+
// ─── CREATE TABLE ──────────────────────────────────
|
|
40
|
+
/**
|
|
41
|
+
* Render `CREATE TABLE` for one described table.
|
|
42
|
+
*
|
|
43
|
+
* `IF NOT EXISTS` is emitted everywhere it exists — SQL Server has no such
|
|
44
|
+
* clause, so there the statement is bare and the caller must not replay it
|
|
45
|
+
* blindly (the diff pass, once it lands, answers that properly).
|
|
46
|
+
*
|
|
47
|
+
* `skipReferences` names the columns whose FK is rendered WITHOUT the inline
|
|
48
|
+
* `references()` — the column itself still gets created; `orderTables` sends a
|
|
49
|
+
* column here when its target is part of a cycle, so the constraint reaches the
|
|
50
|
+
* table separately, once every table involved exists (`addForeignKeyConstraintSQL`).
|
|
51
|
+
*/
|
|
52
|
+
export function createTableSQL(table, dialectName, options) {
|
|
53
|
+
const dialect = resolveDialect(dialectName);
|
|
54
|
+
const composite = table.compositePrimary.length > 0;
|
|
55
|
+
const skip = options?.skipReferences;
|
|
56
|
+
let builder = compiler(dialectName).schema.createTable(table.name);
|
|
57
|
+
if (dialectName !== 'mssql')
|
|
58
|
+
builder = builder.ifNotExists();
|
|
59
|
+
for (const column of table.columns) {
|
|
60
|
+
const type = dialect.columnType(column, isKeyed(table, column));
|
|
61
|
+
builder = builder.addColumn(column.name, sql.raw(type), (col) => {
|
|
62
|
+
let built = col;
|
|
63
|
+
// A simple key is inline; a composite one becomes a table constraint.
|
|
64
|
+
if (column.primary && !composite)
|
|
65
|
+
built = built.primaryKey();
|
|
66
|
+
if (!column.nullable)
|
|
67
|
+
built = built.notNull();
|
|
68
|
+
if (column.default !== undefined)
|
|
69
|
+
built = built.defaultTo(column.default);
|
|
70
|
+
// Uniqueness is the storage's to enforce: no shape can express it, since judging
|
|
71
|
+
// one value never sees the other rows.
|
|
72
|
+
if (column.unique)
|
|
73
|
+
built = built.unique();
|
|
74
|
+
if (column.references && !skip?.has(column.name)) {
|
|
75
|
+
built = built.references(`${column.references.table}.${column.references.column}`);
|
|
76
|
+
if (column.references.onDelete)
|
|
77
|
+
built = built.onDelete(column.references.onDelete);
|
|
78
|
+
}
|
|
79
|
+
return built;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (composite) {
|
|
83
|
+
builder = builder.addPrimaryKeyConstraint(`${table.name}_pk`, table.compositePrimary);
|
|
84
|
+
}
|
|
85
|
+
// The pair the entity declared. Named after its columns so a second group on the
|
|
86
|
+
// same table cannot collide, and so a migration can recognize it later.
|
|
87
|
+
for (const group of table.uniqueGroups) {
|
|
88
|
+
builder = builder.addUniqueConstraint(`${table.name}_${group.join('_')}_unique`, group);
|
|
89
|
+
}
|
|
90
|
+
// What the shape says, told to the storage. Table-level rather than inline: a
|
|
91
|
+
// named constraint is what a later migration can find and replace, and the same
|
|
92
|
+
// form will hold a cross-field check when one is declared.
|
|
93
|
+
for (const column of table.columns) {
|
|
94
|
+
const check = checkFor(column);
|
|
95
|
+
if (check)
|
|
96
|
+
builder = builder.addCheckConstraint(`${table.name}_${column.name}_check`, check);
|
|
97
|
+
}
|
|
98
|
+
return builder.compile().sql;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* `CREATE INDEX` for every column that asked for one.
|
|
102
|
+
*
|
|
103
|
+
* Separate statements, never part of `CREATE TABLE`: an index is not a constraint, it
|
|
104
|
+
* changes no answer — only what a read costs. `IF NOT EXISTS` everywhere it exists, so
|
|
105
|
+
* replaying the batch is safe (SQL Server has no such clause, same rule as the tables).
|
|
106
|
+
*/
|
|
107
|
+
export function indexSQL(table, column, dialectName) {
|
|
108
|
+
let builder = compiler(dialectName)
|
|
109
|
+
.schema.createIndex(`${table.name}_${column.name}_idx`)
|
|
110
|
+
.on(table.name)
|
|
111
|
+
.column(column.name);
|
|
112
|
+
if (dialectName !== 'mssql')
|
|
113
|
+
builder = builder.ifNotExists();
|
|
114
|
+
return builder.compile().sql;
|
|
115
|
+
}
|
|
116
|
+
/** Every index one table asks for — one statement each. */
|
|
117
|
+
export function createIndexSQL(table, dialectName) {
|
|
118
|
+
return table.columns
|
|
119
|
+
.filter((column) => column.index)
|
|
120
|
+
.map((column) => indexSQL(table, column, dialectName));
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* `ALTER TABLE ADD CONSTRAINT` for one FK `orderTables` deferred — closes a
|
|
124
|
+
* relation cycle once every table in it exists. Not available on SQLite (its
|
|
125
|
+
* `ALTER TABLE` is limited to RENAME/ADD COLUMN/RENAME COLUMN/DROP COLUMN) — a
|
|
126
|
+
* caller on that dialect never produces a deferred edge to render here.
|
|
127
|
+
*/
|
|
128
|
+
export function addForeignKeyConstraintSQL(table, column, dialectName) {
|
|
129
|
+
const ref = column.references;
|
|
130
|
+
const name = `${table.name}_${column.name}_fk`;
|
|
131
|
+
let builder = compiler(dialectName)
|
|
132
|
+
.schema.alterTable(table.name)
|
|
133
|
+
.addForeignKeyConstraint(name, [column.name], ref.table, [ref.column]);
|
|
134
|
+
if (ref.onDelete)
|
|
135
|
+
builder = builder.onDelete(ref.onDelete);
|
|
136
|
+
return builder.compile().sql;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* `CREATE TABLE` for every entity the app hosts — scanned frond entities plus
|
|
140
|
+
* auth runtime entities when present.
|
|
141
|
+
*
|
|
142
|
+
* SQLite resolves FK targets lazily and accepts any order, and it has no
|
|
143
|
+
* `ALTER TABLE ADD CONSTRAINT` to close a cycle with — every FK stays inline,
|
|
144
|
+
* unordered. Every other engine needs a referenced table to exist first:
|
|
145
|
+
* `orderTables` sorts the batch and reports the edges a cycle forces to defer,
|
|
146
|
+
* rendered as `ALTER TABLE ADD CONSTRAINT` after every `CREATE TABLE`.
|
|
147
|
+
*
|
|
148
|
+
* Caveat for a repeat call (`autoMigrate`): `CREATE TABLE IF NOT EXISTS` is
|
|
149
|
+
* idempotent, `ADD CONSTRAINT` is not — on pg/mysql/mssql, calling this twice
|
|
150
|
+
* for an app with a relation cycle re-issues the same constraint and errors.
|
|
151
|
+
* The introspection-based `migrate()` (`diff.ts`) doesn't have this problem: it
|
|
152
|
+
* only ever emits a table's constraints once, the run that creates it.
|
|
153
|
+
*/
|
|
154
|
+
export function generateSQL(app, options) {
|
|
155
|
+
const resolve = options?.tableName ?? toTableName;
|
|
156
|
+
const dialect = options?.dialect ?? 'sqlite';
|
|
157
|
+
const tables = toTables(app, resolve);
|
|
158
|
+
if (dialect === 'sqlite') {
|
|
159
|
+
return [
|
|
160
|
+
...tables.map((table) => createTableSQL(table, dialect)),
|
|
161
|
+
...tables.flatMap((table) => createIndexSQL(table, dialect)),
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
const { ordered, deferred } = orderTables(tables);
|
|
165
|
+
const deferredColumnsOf = new Map();
|
|
166
|
+
for (const { table, column } of deferred) {
|
|
167
|
+
const names = deferredColumnsOf.get(table.name) ?? new Set();
|
|
168
|
+
names.add(column.name);
|
|
169
|
+
deferredColumnsOf.set(table.name, names);
|
|
170
|
+
}
|
|
171
|
+
const creates = ordered.map((table) => createTableSQL(table, dialect, { skipReferences: deferredColumnsOf.get(table.name) }));
|
|
172
|
+
const constraints = deferred.map(({ table, column }) => addForeignKeyConstraintSQL(table, column, dialect));
|
|
173
|
+
// Indexes last: every table exists by then, and an index on a table that does not is
|
|
174
|
+
// the one ordering mistake this pass can make.
|
|
175
|
+
const indexes = ordered.flatMap((table) => createIndexSQL(table, dialect));
|
|
176
|
+
return [...creates, ...constraints, ...indexes];
|
|
177
|
+
}
|
|
178
|
+
function runOn(sink, statement) {
|
|
179
|
+
return 'execute' in sink ? sink.execute(statement) : sink.exec(statement);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Create every missing table. Additive only — an existing table is left alone.
|
|
183
|
+
*
|
|
184
|
+
* Stays SYNCHRONOUS when the sink is (a raw better-sqlite3 handle), so a caller
|
|
185
|
+
* that doesn't await still gets its tables before the next statement. Returns a
|
|
186
|
+
* promise only when the sink actually returns one.
|
|
187
|
+
*/
|
|
188
|
+
export function autoMigrate(app, sink, options) {
|
|
189
|
+
const pending = generateSQL(app, options)
|
|
190
|
+
.map((statement) => runOn(sink, statement))
|
|
191
|
+
.filter((result) => typeof result?.then === 'function');
|
|
192
|
+
return pending.length ? Promise.all(pending).then(() => undefined) : undefined;
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=ddl.js.map
|
package/dist/ddl.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ddl.js","sourceRoot":"","sources":["../src/ddl.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dialect — the only place that speaks SQL.
|
|
3
|
+
*
|
|
4
|
+
* A dialect answers two questions and nothing else: which column type carries
|
|
5
|
+
* this shape, and does this engine support `RETURNING`. Everything structural
|
|
6
|
+
* (which columns exist, which are keys) is decided upstream in `TableDef`.
|
|
7
|
+
*/
|
|
8
|
+
import type { ColumnDef } from './table.js';
|
|
9
|
+
export type DialectName = 'sqlite' | 'pg' | 'mysql' | 'mssql';
|
|
10
|
+
export interface Dialect {
|
|
11
|
+
name: DialectName;
|
|
12
|
+
/**
|
|
13
|
+
* SQL type for a column. `keyed` is true when the column belongs to a primary
|
|
14
|
+
* key — MySQL and SQL Server cannot index an unbounded text column, so they
|
|
15
|
+
* narrow to a bounded varchar there.
|
|
16
|
+
*/
|
|
17
|
+
columnType(column: ColumnDef, keyed: boolean): string;
|
|
18
|
+
/**
|
|
19
|
+
* Does `INSERT … RETURNING` work? MySQL has no such clause and SQL Server
|
|
20
|
+
* spells it `OUTPUT`; both take the insert-then-select path instead.
|
|
21
|
+
*/
|
|
22
|
+
supportsReturning: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* How many values one statement may bind — what splits a batch read into several.
|
|
25
|
+
*
|
|
26
|
+
* A key set comes from a PAGE, and a page has no ceiling (`list()` with no limit
|
|
27
|
+
* reads the table), so `where id in (…)` eventually meets the engine's limit.
|
|
28
|
+
* Measured on SQLite: 32 766 binds, and 32 767 answers `too many SQL variables`.
|
|
29
|
+
* SQL Server is the low one at 2100, which is why this is per dialect and not one
|
|
30
|
+
* constant — a batch that works on SQLite and dies on SQL Server is the same value
|
|
31
|
+
* behaving differently per engine, the thing this file exists to absorb.
|
|
32
|
+
*
|
|
33
|
+
* The number below is the limit MINUS a margin for the other values a statement
|
|
34
|
+
* carries (a filter, a cursor): a batch read is never the only thing in the query.
|
|
35
|
+
*/
|
|
36
|
+
maxBindings: number;
|
|
37
|
+
/**
|
|
38
|
+
* How this engine spells "write it, or replace what is there".
|
|
39
|
+
*
|
|
40
|
+
* `'on conflict'` is the standard clause (SQLite, Postgres); MySQL spells the same
|
|
41
|
+
* thing `ON DUPLICATE KEY UPDATE`; SQL Server has only `MERGE`, a different statement
|
|
42
|
+
* with different semantics — so it answers `false` and the port refuses by name
|
|
43
|
+
* rather than emulating a write with a read in front of it, which would be a lie
|
|
44
|
+
* about atomicity in an engine that has no transaction here either.
|
|
45
|
+
*/
|
|
46
|
+
upsert: 'on conflict' | 'on duplicate key' | false;
|
|
47
|
+
}
|
|
48
|
+
export declare const sqliteDialect: Dialect;
|
|
49
|
+
export declare const pgDialect: Dialect;
|
|
50
|
+
export declare const mysqlDialect: Dialect;
|
|
51
|
+
export declare const mssqlDialect: Dialect;
|
|
52
|
+
export declare const dialects: Record<DialectName, Dialect>;
|
|
53
|
+
export declare function resolveDialect(name: DialectName): Dialect;
|
|
54
|
+
//# sourceMappingURL=dialect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialect.d.ts","sourceRoot":"","sources":["../src/dialect.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,IAAI,GAAG,OAAO,GAAG,OAAO,CAAC;AAE9D,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACtD;;;OAGG;IACH,iBAAiB,EAAE,OAAO,CAAC;IAC3B;;;;;;;;;;;;OAYG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;;;OAQG;IACH,MAAM,EAAE,aAAa,GAAG,kBAAkB,GAAG,KAAK,CAAC;CACpD;AAUD,eAAO,MAAM,aAAa,EAAE,OAmB3B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,OAuBvB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,OAsB1B,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,OAsB1B,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,WAAW,EAAE,OAAO,CAKjD,CAAC;AAEF,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAIzD"}
|
package/dist/dialect.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/** Bounded length for a key column, when the shape doesn't state its own. */
|
|
2
|
+
const KEY_LENGTH = 255;
|
|
3
|
+
function keyLength(column) {
|
|
4
|
+
const declared = column.shape?.maxLength;
|
|
5
|
+
return declared !== undefined && declared > 0 && declared <= KEY_LENGTH ? declared : KEY_LENGTH;
|
|
6
|
+
}
|
|
7
|
+
export const sqliteDialect = {
|
|
8
|
+
upsert: 'on conflict',
|
|
9
|
+
// SQLITE_MAX_VARIABLE_NUMBER is 32766 on any build since 3.32 (measured on better-sqlite3).
|
|
10
|
+
maxBindings: 30000,
|
|
11
|
+
name: 'sqlite',
|
|
12
|
+
supportsReturning: true,
|
|
13
|
+
// SQLite has one integer, one float and one text type — a boolean is an int,
|
|
14
|
+
// JSON is text. Any column may be a key, so `keyed` changes nothing.
|
|
15
|
+
columnType(column) {
|
|
16
|
+
switch (column.shape?.type) {
|
|
17
|
+
case 'integer':
|
|
18
|
+
case 'boolean':
|
|
19
|
+
return 'integer';
|
|
20
|
+
case 'number':
|
|
21
|
+
return 'real';
|
|
22
|
+
default:
|
|
23
|
+
return 'text';
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export const pgDialect = {
|
|
28
|
+
upsert: 'on conflict',
|
|
29
|
+
// the wire protocol counts parameters in an int16 — 65535.
|
|
30
|
+
maxBindings: 60000,
|
|
31
|
+
name: 'pg',
|
|
32
|
+
supportsReturning: true,
|
|
33
|
+
// Postgres has real types for everything, and `text` is indexable — so a key
|
|
34
|
+
// needs no narrowing.
|
|
35
|
+
columnType(column) {
|
|
36
|
+
switch (column.shape?.type) {
|
|
37
|
+
case 'integer':
|
|
38
|
+
return 'integer';
|
|
39
|
+
case 'number':
|
|
40
|
+
return 'double precision';
|
|
41
|
+
case 'boolean':
|
|
42
|
+
return 'boolean';
|
|
43
|
+
case 'object':
|
|
44
|
+
case 'array':
|
|
45
|
+
return 'jsonb';
|
|
46
|
+
default:
|
|
47
|
+
return 'text';
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
export const mysqlDialect = {
|
|
52
|
+
upsert: 'on duplicate key',
|
|
53
|
+
// no parameter ceiling of its own; max_allowed_packet is what gives way, and it grows with the VALUES not the count.
|
|
54
|
+
maxBindings: 60000,
|
|
55
|
+
name: 'mysql',
|
|
56
|
+
supportsReturning: false,
|
|
57
|
+
columnType(column, keyed) {
|
|
58
|
+
switch (column.shape?.type) {
|
|
59
|
+
case 'integer':
|
|
60
|
+
return 'int';
|
|
61
|
+
case 'number':
|
|
62
|
+
return 'double';
|
|
63
|
+
case 'boolean':
|
|
64
|
+
return 'boolean';
|
|
65
|
+
case 'object':
|
|
66
|
+
case 'array':
|
|
67
|
+
return 'json';
|
|
68
|
+
default:
|
|
69
|
+
// TEXT cannot take part in a key without a prefix length.
|
|
70
|
+
return keyed ? `varchar(${keyLength(column)})` : 'text';
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
export const mssqlDialect = {
|
|
75
|
+
upsert: false,
|
|
76
|
+
// 2100 parameters per statement, the lowest of the four by a wide margin.
|
|
77
|
+
maxBindings: 2000,
|
|
78
|
+
name: 'mssql',
|
|
79
|
+
supportsReturning: false,
|
|
80
|
+
columnType(column, keyed) {
|
|
81
|
+
switch (column.shape?.type) {
|
|
82
|
+
case 'integer':
|
|
83
|
+
return 'int';
|
|
84
|
+
case 'number':
|
|
85
|
+
return 'float';
|
|
86
|
+
case 'boolean':
|
|
87
|
+
return 'bit';
|
|
88
|
+
case 'object':
|
|
89
|
+
case 'array':
|
|
90
|
+
return 'nvarchar(max)';
|
|
91
|
+
default:
|
|
92
|
+
// nvarchar(max) is not indexable — a key narrows to a bounded length.
|
|
93
|
+
return keyed ? `nvarchar(${keyLength(column)})` : 'nvarchar(max)';
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
export const dialects = {
|
|
98
|
+
sqlite: sqliteDialect,
|
|
99
|
+
pg: pgDialect,
|
|
100
|
+
mysql: mysqlDialect,
|
|
101
|
+
mssql: mssqlDialect,
|
|
102
|
+
};
|
|
103
|
+
export function resolveDialect(name) {
|
|
104
|
+
const dialect = dialects[name];
|
|
105
|
+
if (!dialect)
|
|
106
|
+
throw new Error(`Unknown SQL dialect '${name}'. Known: ${Object.keys(dialects).join(', ')}`);
|
|
107
|
+
return dialect;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=dialect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialect.js","sourceRoot":"","sources":["../src/dialect.ts"],"names":[],"mappings":"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"}
|
package/dist/diff.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff — what the database is missing, compared to what the entities describe.
|
|
3
|
+
*
|
|
4
|
+
* Two states, one comparison, one realisation. The desired state comes from the
|
|
5
|
+
* entities; the actual one from Kysely's introspection, which is already
|
|
6
|
+
* engine-agnostic. The comparison itself is pure — no IO, no SQL.
|
|
7
|
+
*
|
|
8
|
+
* ADDITIVE ONLY, and that incapacity is the guarantee: a missing table is
|
|
9
|
+
* created, a missing column is added, and **nothing else ever happens**. Drops,
|
|
10
|
+
* renames and type changes are human intentions — a rename is not even
|
|
11
|
+
* detectable from a diff (it reads as a drop plus an add). Those belong in a
|
|
12
|
+
* written migration, never in an automatic pass.
|
|
13
|
+
*/
|
|
14
|
+
import { type Kysely } from 'kysely';
|
|
15
|
+
import { type GenerateOptions } from './ddl.js';
|
|
16
|
+
import { type DialectName } from './dialect.js';
|
|
17
|
+
import { type AppLike, type ColumnDef, type TableDef } from './table.js';
|
|
18
|
+
/** What the database actually holds: column names per table. */
|
|
19
|
+
export type SchemaState = Map<string, Set<string>>;
|
|
20
|
+
/** Read the live schema. Only names are needed — an additive pass never inspects types. */
|
|
21
|
+
export declare function actualState(db: Kysely<any>): Promise<SchemaState>;
|
|
22
|
+
/** Project the app's entities into the tables they ask for. */
|
|
23
|
+
export declare function desiredTables(app: AppLike, options?: GenerateOptions): TableDef[];
|
|
24
|
+
export type Change = {
|
|
25
|
+
kind: 'createTable';
|
|
26
|
+
table: TableDef;
|
|
27
|
+
deferredColumns?: string[];
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'addColumn';
|
|
30
|
+
table: TableDef;
|
|
31
|
+
column: ColumnDef;
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'addConstraint';
|
|
34
|
+
table: TableDef;
|
|
35
|
+
column: ColumnDef;
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'createIndex';
|
|
38
|
+
table: TableDef;
|
|
39
|
+
column: ColumnDef;
|
|
40
|
+
};
|
|
41
|
+
/** Compare the two states. Pure — the only place that decides what is missing. */
|
|
42
|
+
export declare function delta(desired: TableDef[], actual: SchemaState): Change[];
|
|
43
|
+
/**
|
|
44
|
+
* Order the changes `delta` found, dialect-aware — `delta` itself stays pure and
|
|
45
|
+
* unordered, this is the one place that adds engine knowledge to the plan.
|
|
46
|
+
*
|
|
47
|
+
* SQLite resolves FK targets lazily and accepts any order, with no `ALTER TABLE
|
|
48
|
+
* ADD CONSTRAINT` to defer to — changes pass through unchanged. Every other
|
|
49
|
+
* engine needs a `createTable`'s FK targets to already exist: `orderTables`
|
|
50
|
+
* sorts the NEW tables among themselves and reports the edges a cycle forces to
|
|
51
|
+
* defer as `addConstraint` changes. An `addColumn` always lands last — its
|
|
52
|
+
* table already exists (that's why it's `addColumn` and not `createTable`), but
|
|
53
|
+
* its FK target might be one of THIS batch's new tables, so it waits until
|
|
54
|
+
* every `createTable`/`addConstraint` above it has run.
|
|
55
|
+
*/
|
|
56
|
+
export declare function orderChanges(changes: Change[], dialectName: DialectName): Change[];
|
|
57
|
+
/**
|
|
58
|
+
* Render one change.
|
|
59
|
+
*
|
|
60
|
+
* A column added to a populated table cannot be `NOT NULL` without a default —
|
|
61
|
+
* every engine refuses it. So an added column keeps its `NOT NULL` only when a
|
|
62
|
+
* default answers the existing rows; otherwise it lands nullable, and tightening
|
|
63
|
+
* it is a written migration.
|
|
64
|
+
*/
|
|
65
|
+
export declare function changeSQL(change: Change, dialectName: DialectName): string;
|
|
66
|
+
/** Everything the database is missing, as statements ready to run. */
|
|
67
|
+
export declare function planMigration(app: AppLike, db: Kysely<any>, options?: GenerateOptions): Promise<{
|
|
68
|
+
changes: Change[];
|
|
69
|
+
statements: string[];
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Bring the database up to what the entities describe — additively.
|
|
73
|
+
*
|
|
74
|
+
* Returns what it did, so a caller can log or refuse. Replaces the old
|
|
75
|
+
* create-if-not-exists pass: it also catches a field added to an existing entity,
|
|
76
|
+
* which used to be silently ignored.
|
|
77
|
+
*/
|
|
78
|
+
/**
|
|
79
|
+
* Bring the schema up to date. Takes the setup itself — `migrate(app, setup)` — so the
|
|
80
|
+
* common case never has to reach into `setup.db`, the one handle that meets no judge.
|
|
81
|
+
* A bare Kysely instance is still accepted, for a caller who holds only that.
|
|
82
|
+
*/
|
|
83
|
+
export declare function migrate(app: AppLike, target: Kysely<any> | {
|
|
84
|
+
db: Kysely<any>;
|
|
85
|
+
}, options?: GenerateOptions): Promise<Change[]>;
|
|
86
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAO,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAkE,KAAK,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhH,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAKL,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,QAAQ,EACd,MAAM,YAAY,CAAC;AAEpB,gEAAgE;AAChE,MAAM,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAEnD,2FAA2F;AAC3F,wBAAsB,WAAW,CAAC,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAOvE;AAED,+DAA+D;AAC/D,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,QAAQ,EAAE,CAEjF;AAED,MAAM,MAAM,MAAM,GACd;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACpE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC;AAEhE,kFAAkF;AAClF,wBAAgB,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CAsBxE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,WAAW,GAAG,MAAM,EAAE,CAuBlF;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,MAAM,CAqC1E;AAED,sEAAsE;AACtE,wBAAsB,aAAa,CACjC,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,EACf,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAItD;AAED;;;;;;GAMG;AACH;;;;GAIG;AACH,wBAAsB,OAAO,CAC3B,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAA;CAAE,EACzC,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,MAAM,EAAE,CAAC,CAOnB"}
|
package/dist/diff.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff — what the database is missing, compared to what the entities describe.
|
|
3
|
+
*
|
|
4
|
+
* Two states, one comparison, one realisation. The desired state comes from the
|
|
5
|
+
* entities; the actual one from Kysely's introspection, which is already
|
|
6
|
+
* engine-agnostic. The comparison itself is pure — no IO, no SQL.
|
|
7
|
+
*
|
|
8
|
+
* ADDITIVE ONLY, and that incapacity is the guarantee: a missing table is
|
|
9
|
+
* created, a missing column is added, and **nothing else ever happens**. Drops,
|
|
10
|
+
* renames and type changes are human intentions — a rename is not even
|
|
11
|
+
* detectable from a diff (it reads as a drop plus an add). Those belong in a
|
|
12
|
+
* written migration, never in an automatic pass.
|
|
13
|
+
*/
|
|
14
|
+
import { sql } from 'kysely';
|
|
15
|
+
import { addForeignKeyConstraintSQL, compiler, createTableSQL, indexSQL } from './ddl.js';
|
|
16
|
+
import { checkFor } from './check.js';
|
|
17
|
+
import { resolveDialect } from './dialect.js';
|
|
18
|
+
import { isKeyed, orderTables, toTables, toTableName, } from './table.js';
|
|
19
|
+
/** Read the live schema. Only names are needed — an additive pass never inspects types. */
|
|
20
|
+
export async function actualState(db) {
|
|
21
|
+
const state = new Map();
|
|
22
|
+
for (const table of await db.introspection.getTables()) {
|
|
23
|
+
if (table.isView)
|
|
24
|
+
continue;
|
|
25
|
+
state.set(table.name, new Set(table.columns.map((column) => column.name)));
|
|
26
|
+
}
|
|
27
|
+
return state;
|
|
28
|
+
}
|
|
29
|
+
/** Project the app's entities into the tables they ask for. */
|
|
30
|
+
export function desiredTables(app, options) {
|
|
31
|
+
return toTables(app, options?.tableName ?? toTableName);
|
|
32
|
+
}
|
|
33
|
+
/** Compare the two states. Pure — the only place that decides what is missing. */
|
|
34
|
+
export function delta(desired, actual) {
|
|
35
|
+
const changes = [];
|
|
36
|
+
for (const table of desired) {
|
|
37
|
+
const existing = actual.get(table.name);
|
|
38
|
+
if (!existing) {
|
|
39
|
+
changes.push({ kind: 'createTable', table });
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
for (const column of table.columns) {
|
|
43
|
+
if (!existing.has(column.name))
|
|
44
|
+
changes.push({ kind: 'addColumn', table, column });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Indexes, unconditionally: this pass reads column NAMES from the live schema, so it
|
|
48
|
+
// cannot see whether an index exists. `CREATE INDEX IF NOT EXISTS` is idempotent, so
|
|
49
|
+
// proposing it every time is cheaper and more honest than introspecting to guess —
|
|
50
|
+
// the alternative would be an index that a `unique()` added later never gets.
|
|
51
|
+
for (const table of desired) {
|
|
52
|
+
for (const column of table.columns) {
|
|
53
|
+
if (column.index)
|
|
54
|
+
changes.push({ kind: 'createIndex', table, column });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return changes;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Order the changes `delta` found, dialect-aware — `delta` itself stays pure and
|
|
61
|
+
* unordered, this is the one place that adds engine knowledge to the plan.
|
|
62
|
+
*
|
|
63
|
+
* SQLite resolves FK targets lazily and accepts any order, with no `ALTER TABLE
|
|
64
|
+
* ADD CONSTRAINT` to defer to — changes pass through unchanged. Every other
|
|
65
|
+
* engine needs a `createTable`'s FK targets to already exist: `orderTables`
|
|
66
|
+
* sorts the NEW tables among themselves and reports the edges a cycle forces to
|
|
67
|
+
* defer as `addConstraint` changes. An `addColumn` always lands last — its
|
|
68
|
+
* table already exists (that's why it's `addColumn` and not `createTable`), but
|
|
69
|
+
* its FK target might be one of THIS batch's new tables, so it waits until
|
|
70
|
+
* every `createTable`/`addConstraint` above it has run.
|
|
71
|
+
*/
|
|
72
|
+
export function orderChanges(changes, dialectName) {
|
|
73
|
+
if (dialectName === 'sqlite')
|
|
74
|
+
return changes;
|
|
75
|
+
const creates = changes.filter((c) => c.kind === 'createTable');
|
|
76
|
+
const addColumns = changes.filter((c) => c.kind === 'addColumn');
|
|
77
|
+
const indexes = changes.filter((c) => c.kind === 'createIndex');
|
|
78
|
+
const { ordered, deferred } = orderTables(creates.map((c) => c.table));
|
|
79
|
+
const deferredColumnsOf = new Map();
|
|
80
|
+
for (const { table, column } of deferred) {
|
|
81
|
+
const names = deferredColumnsOf.get(table.name) ?? new Set();
|
|
82
|
+
names.add(column.name);
|
|
83
|
+
deferredColumnsOf.set(table.name, names);
|
|
84
|
+
}
|
|
85
|
+
const createChanges = ordered.map((table) => {
|
|
86
|
+
const names = deferredColumnsOf.get(table.name);
|
|
87
|
+
return names ? { kind: 'createTable', table, deferredColumns: [...names] } : { kind: 'createTable', table };
|
|
88
|
+
});
|
|
89
|
+
const constraintChanges = deferred.map(({ table, column }) => ({ kind: 'addConstraint', table, column }));
|
|
90
|
+
// Indexes last: the column they stand on may be one this very batch added.
|
|
91
|
+
return [...createChanges, ...constraintChanges, ...addColumns, ...indexes];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Render one change.
|
|
95
|
+
*
|
|
96
|
+
* A column added to a populated table cannot be `NOT NULL` without a default —
|
|
97
|
+
* every engine refuses it. So an added column keeps its `NOT NULL` only when a
|
|
98
|
+
* default answers the existing rows; otherwise it lands nullable, and tightening
|
|
99
|
+
* it is a written migration.
|
|
100
|
+
*/
|
|
101
|
+
export function changeSQL(change, dialectName) {
|
|
102
|
+
const dialect = resolveDialect(dialectName);
|
|
103
|
+
if (change.kind === 'createTable') {
|
|
104
|
+
// Reuse the same renderer as a fresh install — one builder, no drift.
|
|
105
|
+
const skip = change.deferredColumns ? new Set(change.deferredColumns) : undefined;
|
|
106
|
+
return createTableSQL(change.table, dialectName, { skipReferences: skip });
|
|
107
|
+
}
|
|
108
|
+
if (change.kind === 'addConstraint') {
|
|
109
|
+
return addForeignKeyConstraintSQL(change.table, change.column, dialectName);
|
|
110
|
+
}
|
|
111
|
+
if (change.kind === 'createIndex') {
|
|
112
|
+
// One statement per change — `migrate` runs them one by one, and no driver here
|
|
113
|
+
// accepts a batch.
|
|
114
|
+
return indexSQL(change.table, change.column, dialectName);
|
|
115
|
+
}
|
|
116
|
+
const { table, column } = change;
|
|
117
|
+
const type = dialect.columnType(column, isKeyed(table, column));
|
|
118
|
+
return compiler(dialectName)
|
|
119
|
+
.schema.alterTable(table.name)
|
|
120
|
+
.addColumn(column.name, sql.raw(type), (col) => {
|
|
121
|
+
let built = col;
|
|
122
|
+
if (column.default !== undefined) {
|
|
123
|
+
built = built.defaultTo(column.default);
|
|
124
|
+
if (!column.nullable)
|
|
125
|
+
built = built.notNull();
|
|
126
|
+
}
|
|
127
|
+
if (column.references) {
|
|
128
|
+
built = built.references(`${column.references.table}.${column.references.column}`);
|
|
129
|
+
if (column.references.onDelete)
|
|
130
|
+
built = built.onDelete(column.references.onDelete);
|
|
131
|
+
}
|
|
132
|
+
// Inline rather than a named table constraint: SQLite cannot ALTER one in, and
|
|
133
|
+
// the column is new, so no existing row can be caught out by it. A column that
|
|
134
|
+
// arrives later is bounded like a column that was there from the start.
|
|
135
|
+
const check = checkFor(column);
|
|
136
|
+
if (check)
|
|
137
|
+
built = built.check(check);
|
|
138
|
+
return built;
|
|
139
|
+
})
|
|
140
|
+
.compile().sql;
|
|
141
|
+
}
|
|
142
|
+
/** Everything the database is missing, as statements ready to run. */
|
|
143
|
+
export async function planMigration(app, db, options) {
|
|
144
|
+
const dialect = options?.dialect ?? 'sqlite';
|
|
145
|
+
const changes = orderChanges(delta(desiredTables(app, options), await actualState(db)), dialect);
|
|
146
|
+
return { changes, statements: changes.map((change) => changeSQL(change, dialect)) };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Bring the database up to what the entities describe — additively.
|
|
150
|
+
*
|
|
151
|
+
* Returns what it did, so a caller can log or refuse. Replaces the old
|
|
152
|
+
* create-if-not-exists pass: it also catches a field added to an existing entity,
|
|
153
|
+
* which used to be silently ignored.
|
|
154
|
+
*/
|
|
155
|
+
/**
|
|
156
|
+
* Bring the schema up to date. Takes the setup itself — `migrate(app, setup)` — so the
|
|
157
|
+
* common case never has to reach into `setup.db`, the one handle that meets no judge.
|
|
158
|
+
* A bare Kysely instance is still accepted, for a caller who holds only that.
|
|
159
|
+
*/
|
|
160
|
+
export async function migrate(app, target, options) {
|
|
161
|
+
const db = target.db ?? target;
|
|
162
|
+
const { changes, statements } = await planMigration(app, db, options);
|
|
163
|
+
for (const statement of statements) {
|
|
164
|
+
await sql.raw(statement).execute(db);
|
|
165
|
+
}
|
|
166
|
+
return changes;
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=diff.js.map
|
package/dist/diff.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"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"}
|