@foul11/awesome-db 1.1.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/CHANGELOG.md +11 -0
- package/dist/BitFields.d.ts +29 -0
- package/dist/BitFields.d.ts.map +1 -0
- package/dist/Error.d.ts +21 -0
- package/dist/Error.d.ts.map +1 -0
- package/dist/ORM.d.ts +14 -0
- package/dist/ORM.d.ts.map +1 -0
- package/dist/SQLParser.d.ts +1394 -0
- package/dist/SQLParser.d.ts.map +1 -0
- package/dist/WebpackFileProvider.d.ts +12 -0
- package/dist/WebpackFileProvider.d.ts.map +1 -0
- package/dist/alter/column_add.d.ts +7 -0
- package/dist/alter/column_add.d.ts.map +1 -0
- package/dist/alter/column_drop.d.ts +6 -0
- package/dist/alter/column_drop.d.ts.map +1 -0
- package/dist/alter/column_rename.d.ts +6 -0
- package/dist/alter/column_rename.d.ts.map +1 -0
- package/dist/alter/column_update.d.ts +7 -0
- package/dist/alter/column_update.d.ts.map +1 -0
- package/dist/alter/columns_order.d.ts +6 -0
- package/dist/alter/columns_order.d.ts.map +1 -0
- package/dist/alter/index.d.ts +7 -0
- package/dist/alter/index.d.ts.map +1 -0
- package/dist/alter/pragma.d.ts +4 -0
- package/dist/alter/pragma.d.ts.map +1 -0
- package/dist/alter/utils.d.ts +6 -0
- package/dist/alter/utils.d.ts.map +1 -0
- package/dist/defaults.d.ts +2 -0
- package/dist/defaults.d.ts.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +1540 -0
- package/dist/index.mjs.map +1 -0
- package/dist/indexer.d.ts +12 -0
- package/dist/indexer.d.ts.map +1 -0
- package/dist/log/access_log.d.ts +7 -0
- package/dist/log/access_log.d.ts.map +1 -0
- package/dist/log/db.d.ts +6 -0
- package/dist/log/db.d.ts.map +1 -0
- package/dist/log/index.d.ts +3 -0
- package/dist/log/index.d.ts.map +1 -0
- package/dist/tables/AccessLog/index.d.ts +79 -0
- package/dist/tables/AccessLog/index.d.ts.map +1 -0
- package/dist/tables/AccessLog/schema.d.ts +17 -0
- package/dist/tables/AccessLog/schema.d.ts.map +1 -0
- package/dist/tables/Permission/index.d.ts +43 -0
- package/dist/tables/Permission/index.d.ts.map +1 -0
- package/dist/tables/Permission/schema.d.ts +12 -0
- package/dist/tables/Permission/schema.d.ts.map +1 -0
- package/dist/tables/SetString/index.d.ts +10 -0
- package/dist/tables/SetString/index.d.ts.map +1 -0
- package/dist/tables/SetString/schema.d.ts +7 -0
- package/dist/tables/SetString/schema.d.ts.map +1 -0
- package/dist/tables/Settings/index.d.ts +42 -0
- package/dist/tables/Settings/index.d.ts.map +1 -0
- package/dist/tables/Settings/schema.d.ts +8 -0
- package/dist/tables/Settings/schema.d.ts.map +1 -0
- package/dist/tables/Transaction/index.d.ts +90 -0
- package/dist/tables/Transaction/index.d.ts.map +1 -0
- package/dist/tables/Transaction/schema.d.ts +16 -0
- package/dist/tables/Transaction/schema.d.ts.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils.d.ts +42 -0
- package/dist/utils.d.ts.map +1 -0
- package/eslint.config.js +7 -0
- package/package.json +54 -0
- package/src/BitFields.ts +160 -0
- package/src/Error.ts +13 -0
- package/src/ORM.ts +49 -0
- package/src/SQLParser.js +1204 -0
- package/src/WebpackFileProvider.ts +63 -0
- package/src/alter/column_add.ts +79 -0
- package/src/alter/column_drop.ts +54 -0
- package/src/alter/column_rename.ts +55 -0
- package/src/alter/column_update.ts +92 -0
- package/src/alter/columns_order.ts +60 -0
- package/src/alter/index.ts +6 -0
- package/src/alter/pragma.ts +10 -0
- package/src/alter/utils.ts +70 -0
- package/src/defaults.ts +3 -0
- package/src/index.ts +227 -0
- package/src/indexer.ts +75 -0
- package/src/log/access_log.ts +29 -0
- package/src/log/db.ts +28 -0
- package/src/log/index.ts +2 -0
- package/src/tables/AccessLog/index.ts +252 -0
- package/src/tables/AccessLog/schema.ts +20 -0
- package/src/tables/Permission/index.ts +220 -0
- package/src/tables/Permission/schema.ts +13 -0
- package/src/tables/SetString/index.ts +45 -0
- package/src/tables/SetString/schema.ts +7 -0
- package/src/tables/Settings/index.ts +135 -0
- package/src/tables/Settings/schema.ts +8 -0
- package/src/tables/Transaction/index.ts +343 -0
- package/src/tables/Transaction/schema.ts +20 -0
- package/src/types/index.ts +33 -0
- package/src/utils.ts +48 -0
- package/test/sqliteExtExpert.test.ts +39 -0
- package/tsconfig.build.json +17 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Migration, MigrationProvider } from 'kysely/migration';
|
|
2
|
+
import type { requireDContext } from '@foul11/awesome';
|
|
3
|
+
|
|
4
|
+
type RequireContext = Awaited<ReturnType<typeof requireDContext>>;
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
7
|
+
function isFunction(obj: unknown): obj is Function {
|
|
8
|
+
return typeof obj === 'function';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isObject(obj: unknown): obj is Record<string, unknown> {
|
|
12
|
+
return typeof obj === 'object' && obj !== null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function isMigration(obj: unknown): obj is Migration {
|
|
16
|
+
return isObject(obj) && isFunction(obj.up);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class WebpackFileProvider implements MigrationProvider {
|
|
20
|
+
static db_migrations = new Map<string, Record<string, any>>();
|
|
21
|
+
|
|
22
|
+
#db_name: string;
|
|
23
|
+
|
|
24
|
+
constructor(db_name: string) {
|
|
25
|
+
this.#db_name = db_name;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getMigrations(): Promise<Record<string, Migration>> {
|
|
29
|
+
const migrations_list: Record<string, Migration> = {};
|
|
30
|
+
const migrations = WebpackFileProvider.db_migrations.get(this.#db_name);
|
|
31
|
+
|
|
32
|
+
if (!migrations)
|
|
33
|
+
return Promise.resolve(migrations_list);
|
|
34
|
+
|
|
35
|
+
for (const [ file, exports ] of Object.entries(migrations)) {
|
|
36
|
+
if (isMigration(exports?.default)) {
|
|
37
|
+
migrations_list[file] = exports.default;
|
|
38
|
+
} else if (isMigration(exports)) {
|
|
39
|
+
migrations_list[file] = exports;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return Promise.resolve(migrations_list);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static setupDynamicImport(ctx: RequireContext, db_name?: string) {
|
|
47
|
+
for (const [ fpath, exports ] of ctx.keys().map(m => [ m, ctx(m) ])) {
|
|
48
|
+
const [ , db, file ] = /^.+[\\/]([^\\/]+)[\\/](.+)\.[jt]s$/i.exec(fpath) ?? [];
|
|
49
|
+
|
|
50
|
+
if (!db_name)
|
|
51
|
+
db_name = db;
|
|
52
|
+
|
|
53
|
+
if (!db_name || !file)
|
|
54
|
+
continue;
|
|
55
|
+
|
|
56
|
+
if (WebpackFileProvider.db_migrations.has(db_name)) {
|
|
57
|
+
WebpackFileProvider.db_migrations.get(db_name)! [file] = exports;
|
|
58
|
+
} else {
|
|
59
|
+
WebpackFileProvider.db_migrations.set(db_name, { [file]: exports });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { itAddColumn, tmpTableName } from './utils';
|
|
2
|
+
import { get_table_schema } from '../utils';
|
|
3
|
+
import { pragma_foreign_keys } from './pragma';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import type { Kysely, AnyColumn, ColumnDefinitionBuilder, ExpressionBuilder, AliasedExpression, Expression } from 'kysely';
|
|
7
|
+
import type { ExtractTableAlias } from './utils';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Alter column add after column, or first if null
|
|
11
|
+
*/
|
|
12
|
+
export async function alter_column_add<DB, TB extends Extract<keyof DB, string>, CN extends AnyColumn<DB, TB>>(
|
|
13
|
+
db: Kysely<DB>,
|
|
14
|
+
table: TB,
|
|
15
|
+
column_name: CN,
|
|
16
|
+
column_type: Expression<unknown>,
|
|
17
|
+
after: CN | null,
|
|
18
|
+
col: (col: ColumnDefinitionBuilder) => ColumnDefinitionBuilder,
|
|
19
|
+
eb?: ((eb: ExpressionBuilder<DB, ExtractTableAlias<DB, TB>>) => AliasedExpression<any, CN>),
|
|
20
|
+
) {
|
|
21
|
+
await pragma_foreign_keys(db, false);
|
|
22
|
+
await db
|
|
23
|
+
.transaction()
|
|
24
|
+
.execute(async (trx) => {
|
|
25
|
+
const alter_table = tmpTableName(table);
|
|
26
|
+
const schema = await get_table_schema(trx, table);
|
|
27
|
+
|
|
28
|
+
if (!schema)
|
|
29
|
+
throw new Error(`Table ${table} does not exist`);
|
|
30
|
+
|
|
31
|
+
if (schema.columns.some(c => c.name == column_name))
|
|
32
|
+
return; // passthrough if column already exists
|
|
33
|
+
|
|
34
|
+
const after_column_idx = schema.columns.findIndex(c => c.name == after);
|
|
35
|
+
|
|
36
|
+
let columns_after = [] as any[];
|
|
37
|
+
let columns_before = [] as any[];
|
|
38
|
+
|
|
39
|
+
if (after_column_idx !== -1) {
|
|
40
|
+
columns_after = schema.columns.slice(0, after_column_idx + 1);
|
|
41
|
+
columns_before = schema.columns.slice(after_column_idx + 1);
|
|
42
|
+
} else {
|
|
43
|
+
columns_after = [];
|
|
44
|
+
columns_before = schema.columns;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let builder = trx.schema.createTable(alter_table);
|
|
48
|
+
builder = itAddColumn(builder, columns_after);
|
|
49
|
+
builder = builder.addColumn(column_name, column_type, col);
|
|
50
|
+
builder = itAddColumn(builder, columns_before);
|
|
51
|
+
await builder.execute();
|
|
52
|
+
|
|
53
|
+
await trx
|
|
54
|
+
.insertInto(alter_table)
|
|
55
|
+
.columns([
|
|
56
|
+
...columns_after.map(c => c.name),
|
|
57
|
+
...(eb ? [ column_name ] : []),
|
|
58
|
+
...columns_before.map(c => c.name),
|
|
59
|
+
])
|
|
60
|
+
.expression(() => (trx as any)
|
|
61
|
+
.selectFrom(table)
|
|
62
|
+
.select([
|
|
63
|
+
...columns_after.map(c => `${table}.${c.name}`),
|
|
64
|
+
...(eb ? [ eb ] : []),
|
|
65
|
+
...columns_before.map(c => `${table}.${c.name}`),
|
|
66
|
+
]))
|
|
67
|
+
.executeTakeFirstOrThrow();
|
|
68
|
+
|
|
69
|
+
await trx.schema
|
|
70
|
+
.dropTable(table)
|
|
71
|
+
.execute();
|
|
72
|
+
|
|
73
|
+
await trx.schema
|
|
74
|
+
.alterTable(alter_table)
|
|
75
|
+
.renameTo(table)
|
|
76
|
+
.execute();
|
|
77
|
+
});
|
|
78
|
+
await pragma_foreign_keys(db, true);
|
|
79
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { itAddColumn, tmpTableName } from './utils';
|
|
2
|
+
import { get_table_schema } from '../utils';
|
|
3
|
+
import { pragma_foreign_keys } from './pragma';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import type { Kysely } from 'kysely';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Alter column drop
|
|
11
|
+
*/
|
|
12
|
+
export async function alter_column_drop<DB, TB extends Extract<keyof DB, string>>(
|
|
13
|
+
db: Kysely<DB>,
|
|
14
|
+
table: TB,
|
|
15
|
+
column_name: string,
|
|
16
|
+
) {
|
|
17
|
+
await pragma_foreign_keys(db, false);
|
|
18
|
+
await db
|
|
19
|
+
.transaction()
|
|
20
|
+
.execute(async (trx) => {
|
|
21
|
+
const alter_table = tmpTableName(table);
|
|
22
|
+
const schema = await get_table_schema(trx, table);
|
|
23
|
+
|
|
24
|
+
if (!schema)
|
|
25
|
+
throw new Error(`Table ${table} does not exist`);
|
|
26
|
+
|
|
27
|
+
if (!schema.columns.find(c => c.name == column_name)?.type)
|
|
28
|
+
return; // passthrough if column not found
|
|
29
|
+
|
|
30
|
+
const columns = schema.columns.filter(c => c.name != column_name);
|
|
31
|
+
|
|
32
|
+
let builder = trx.schema.createTable(alter_table);
|
|
33
|
+
builder = itAddColumn(builder, columns);
|
|
34
|
+
await builder.execute();
|
|
35
|
+
|
|
36
|
+
await trx
|
|
37
|
+
.insertInto(alter_table)
|
|
38
|
+
.columns(columns.map(c => c.name) as any)
|
|
39
|
+
.expression(eb => (eb as any)
|
|
40
|
+
.selectFrom(table)
|
|
41
|
+
.select(columns.map(c => `${table}.${c.name}`)))
|
|
42
|
+
.executeTakeFirstOrThrow();
|
|
43
|
+
|
|
44
|
+
await trx.schema
|
|
45
|
+
.dropTable(table)
|
|
46
|
+
.execute();
|
|
47
|
+
|
|
48
|
+
await trx.schema
|
|
49
|
+
.alterTable(alter_table)
|
|
50
|
+
.renameTo(table)
|
|
51
|
+
.execute();
|
|
52
|
+
});
|
|
53
|
+
await pragma_foreign_keys(db, true);
|
|
54
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { itAddColumn, tmpTableName } from './utils';
|
|
2
|
+
import { get_table_schema } from '../utils';
|
|
3
|
+
import { pragma_foreign_keys } from './pragma';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import type { Kysely, AnyColumn } from 'kysely';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Alter column rename
|
|
11
|
+
*/
|
|
12
|
+
export async function alter_column_rename<DB, TB extends Extract<keyof DB, string>>(
|
|
13
|
+
db: Kysely<DB>,
|
|
14
|
+
table: TB,
|
|
15
|
+
column_name: string,
|
|
16
|
+
new_column_name: AnyColumn<DB, TB>,
|
|
17
|
+
) {
|
|
18
|
+
await pragma_foreign_keys(db, false);
|
|
19
|
+
await db
|
|
20
|
+
.transaction()
|
|
21
|
+
.execute(async (trx) => {
|
|
22
|
+
const alter_table = tmpTableName(table);
|
|
23
|
+
const schema = await get_table_schema(trx, table);
|
|
24
|
+
|
|
25
|
+
if (!schema)
|
|
26
|
+
throw new Error(`Table ${table} does not exist`);
|
|
27
|
+
|
|
28
|
+
const columns = schema.columns;
|
|
29
|
+
|
|
30
|
+
if (columns.some(c => c.name === new_column_name))
|
|
31
|
+
return; // passthrough if column already exists
|
|
32
|
+
|
|
33
|
+
let builder = trx.schema.createTable(alter_table);
|
|
34
|
+
builder = itAddColumn(builder, columns.map(c => ({ ...c, name: c.name == column_name ? new_column_name : c.name })));
|
|
35
|
+
await builder.execute();
|
|
36
|
+
|
|
37
|
+
await trx
|
|
38
|
+
.insertInto(alter_table)
|
|
39
|
+
.columns(columns.map(c => c.name == column_name ? new_column_name : c.name) as any)
|
|
40
|
+
.expression(eb => (eb as any)
|
|
41
|
+
.selectFrom(table)
|
|
42
|
+
.select(columns.map(c => `${table}.${c.name}`)))
|
|
43
|
+
.executeTakeFirstOrThrow();
|
|
44
|
+
|
|
45
|
+
await trx.schema
|
|
46
|
+
.dropTable(table)
|
|
47
|
+
.execute();
|
|
48
|
+
|
|
49
|
+
await trx.schema
|
|
50
|
+
.alterTable(alter_table)
|
|
51
|
+
.renameTo(table)
|
|
52
|
+
.execute();
|
|
53
|
+
});
|
|
54
|
+
await pragma_foreign_keys(db, true);
|
|
55
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { itAddColumn, tmpTableName } from './utils';
|
|
2
|
+
import { SQLParser } from '../SQLParser';
|
|
3
|
+
import { get_table_schema } from '../utils';
|
|
4
|
+
import { pragma_foreign_keys } from './pragma';
|
|
5
|
+
|
|
6
|
+
import type { Kysely, AnyColumn, ColumnDefinitionBuilder, ExpressionBuilder, AliasedExpression, Expression } from 'kysely';
|
|
7
|
+
import type { ExtractTableAlias } from './utils';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Alter column update settings (e.g. not null)
|
|
11
|
+
*/
|
|
12
|
+
export async function alter_column_update<
|
|
13
|
+
DB,
|
|
14
|
+
TB extends Extract<keyof DB, string>,
|
|
15
|
+
CN extends AnyColumn<DB, TB>,
|
|
16
|
+
>(
|
|
17
|
+
db: Kysely<DB>,
|
|
18
|
+
table: TB,
|
|
19
|
+
column_name: CN,
|
|
20
|
+
column_type: Expression<unknown>,
|
|
21
|
+
col: (col: ColumnDefinitionBuilder) => ColumnDefinitionBuilder,
|
|
22
|
+
eb: ((eb: ExpressionBuilder<DB, ExtractTableAlias<DB, TB>>) => AliasedExpression<any, CN>) | null = null,
|
|
23
|
+
) {
|
|
24
|
+
await pragma_foreign_keys(db, false);
|
|
25
|
+
await db
|
|
26
|
+
.transaction()
|
|
27
|
+
.execute(async (trx) => {
|
|
28
|
+
const alter_table = tmpTableName(table);
|
|
29
|
+
const schema = await get_table_schema(trx, table);
|
|
30
|
+
|
|
31
|
+
if (!schema)
|
|
32
|
+
throw new Error(`Table ${table} does not exist`);
|
|
33
|
+
|
|
34
|
+
let columns_after: typeof schema.columns | null = null;
|
|
35
|
+
let columns_before: typeof schema.columns | null = null;
|
|
36
|
+
|
|
37
|
+
const prev_column_idx = schema.columns.findIndex(c => c.name == column_name);
|
|
38
|
+
|
|
39
|
+
if (prev_column_idx === -1)
|
|
40
|
+
throw new Error(`Column ${column_name} does not exist`);
|
|
41
|
+
|
|
42
|
+
columns_after = schema.columns.slice(0, prev_column_idx);
|
|
43
|
+
columns_before = schema.columns.slice(prev_column_idx + 1);
|
|
44
|
+
|
|
45
|
+
let builder = trx.schema.createTable(alter_table);
|
|
46
|
+
builder = itAddColumn(builder, columns_after);
|
|
47
|
+
builder = builder.addColumn(column_name, column_type, col);
|
|
48
|
+
builder = itAddColumn(builder, columns_before);
|
|
49
|
+
|
|
50
|
+
const create_schema = SQLParser.parse(builder.compile().sql)[0];
|
|
51
|
+
|
|
52
|
+
if (create_schema?.type != 'CREATE_TABLE')
|
|
53
|
+
throw new Error('Invalid builded SQL');
|
|
54
|
+
|
|
55
|
+
const old_column = schema.columns.find(c => c.name == column_name);
|
|
56
|
+
const new_column = create_schema.columns.find(c => c.name == column_name);
|
|
57
|
+
|
|
58
|
+
if (!old_column || !new_column)
|
|
59
|
+
throw new Error('new/old column not found');
|
|
60
|
+
|
|
61
|
+
if (JSON.stringify(old_column) == JSON.stringify(new_column))
|
|
62
|
+
return; // passthrough if column settings are the same
|
|
63
|
+
|
|
64
|
+
await builder.execute();
|
|
65
|
+
|
|
66
|
+
await trx
|
|
67
|
+
.insertInto(alter_table)
|
|
68
|
+
.columns([
|
|
69
|
+
...columns_after.map(c => c.name),
|
|
70
|
+
column_name,
|
|
71
|
+
...columns_before.map(c => c.name),
|
|
72
|
+
] as any)
|
|
73
|
+
.expression(() => (trx as any)
|
|
74
|
+
.selectFrom(table)
|
|
75
|
+
.select([
|
|
76
|
+
...columns_after.map(c => `${table}.${c.name}`),
|
|
77
|
+
...(eb ? [ eb ] : [ `${table}.${column_name}` ]),
|
|
78
|
+
...columns_before.map(c => `${table}.${c.name}`),
|
|
79
|
+
]))
|
|
80
|
+
.executeTakeFirstOrThrow();
|
|
81
|
+
|
|
82
|
+
await trx.schema
|
|
83
|
+
.dropTable(table)
|
|
84
|
+
.execute();
|
|
85
|
+
|
|
86
|
+
await trx.schema
|
|
87
|
+
.alterTable(alter_table)
|
|
88
|
+
.renameTo(table)
|
|
89
|
+
.execute();
|
|
90
|
+
});
|
|
91
|
+
await pragma_foreign_keys(db, true);
|
|
92
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { itAddColumn, tmpTableName } from './utils';
|
|
2
|
+
import { get_table_schema } from '../utils';
|
|
3
|
+
import { pragma_foreign_keys } from './pragma';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import type { Kysely, AnyColumn } from 'kysely';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Alter column order
|
|
11
|
+
*/
|
|
12
|
+
export async function alter_column_order<DB, TB extends Extract<keyof DB, string>>(
|
|
13
|
+
db: Kysely<DB>,
|
|
14
|
+
table: TB,
|
|
15
|
+
columns_name: AnyColumn<DB, TB>[],
|
|
16
|
+
) {
|
|
17
|
+
await pragma_foreign_keys(db, false);
|
|
18
|
+
await db
|
|
19
|
+
.transaction()
|
|
20
|
+
.execute(async (trx) => {
|
|
21
|
+
const alter_table = tmpTableName(table);
|
|
22
|
+
const schema = await get_table_schema(trx, table);
|
|
23
|
+
|
|
24
|
+
if (!schema)
|
|
25
|
+
throw new Error(`Table ${table} does not exist`);
|
|
26
|
+
|
|
27
|
+
const schema_columns = new Map(schema.columns.map((c, i) => [ c.name, i ]));
|
|
28
|
+
const new_columns = new Map(columns_name .map((c, i) => [ c as any, i ]));
|
|
29
|
+
|
|
30
|
+
if (schema_columns.size != new_columns.size || schema_columns.size != columns_name.length)
|
|
31
|
+
throw new Error(`Table ${table} column count mismatch`);
|
|
32
|
+
|
|
33
|
+
if (![ ...schema_columns.entries() ].some(([ c_name, idx ]) => new_columns.get(c_name) != idx))
|
|
34
|
+
return; // passthrough if column order is the same
|
|
35
|
+
|
|
36
|
+
let builder = trx.schema.createTable(alter_table);
|
|
37
|
+
builder = itAddColumn(builder, columns_name.map(c =>
|
|
38
|
+
schema.columns[schema_columns.get(c)!],
|
|
39
|
+
));
|
|
40
|
+
await builder.execute();
|
|
41
|
+
|
|
42
|
+
await trx
|
|
43
|
+
.insertInto(alter_table)
|
|
44
|
+
.columns(columns_name)
|
|
45
|
+
.expression(eb => (eb as any)
|
|
46
|
+
.selectFrom(table)
|
|
47
|
+
.select(columns_name.map(c => `${table}.${c}`)))
|
|
48
|
+
.executeTakeFirstOrThrow();
|
|
49
|
+
|
|
50
|
+
await trx.schema
|
|
51
|
+
.dropTable(table)
|
|
52
|
+
.execute();
|
|
53
|
+
|
|
54
|
+
await trx.schema
|
|
55
|
+
.alterTable(alter_table)
|
|
56
|
+
.renameTo(table)
|
|
57
|
+
.execute();
|
|
58
|
+
});
|
|
59
|
+
await pragma_foreign_keys(db, true);
|
|
60
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { sql } from 'kysely';
|
|
2
|
+
import type { Kysely } from 'kysely';
|
|
3
|
+
|
|
4
|
+
export async function pragma_foreign_keys(db: Kysely<any>, enable: boolean) {
|
|
5
|
+
return sql`PRAGMA foreign_keys = ${sql.lit(enable ? 1 : 0)}`.execute(db);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function pragma_defer_foreign_keys(db: Kysely<any>, enable: boolean) {
|
|
9
|
+
return sql`PRAGMA defer_foreign_keys = ${sql.lit(enable ? 1 : 0)}`.execute(db);
|
|
10
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { sql } from 'kysely';
|
|
2
|
+
|
|
3
|
+
import type { SQLParser } from '../SQLParser';
|
|
4
|
+
import type { ColumnDefinitionBuilder, CreateTableBuilder } from 'kysely';
|
|
5
|
+
|
|
6
|
+
export type ExtractTableAlias<DB, TE> = TE extends `${string} as ${infer TA}`
|
|
7
|
+
? TA extends keyof DB
|
|
8
|
+
? TA
|
|
9
|
+
: never
|
|
10
|
+
: TE extends keyof DB
|
|
11
|
+
? TE
|
|
12
|
+
: never;
|
|
13
|
+
|
|
14
|
+
function columnDefinition(col: ColumnDefinitionBuilder, columnDef: ReturnType<typeof SQLParser['sqliteColumnsDef']>[0]) {
|
|
15
|
+
if (columnDef.primary)
|
|
16
|
+
col = col.primaryKey();
|
|
17
|
+
|
|
18
|
+
if (columnDef.unique)
|
|
19
|
+
col = col.unique();
|
|
20
|
+
|
|
21
|
+
if (columnDef.auto_increment)
|
|
22
|
+
col = col.autoIncrement();
|
|
23
|
+
|
|
24
|
+
if (columnDef.not_null)
|
|
25
|
+
col = col.notNull();
|
|
26
|
+
|
|
27
|
+
if (columnDef.default !== null) {
|
|
28
|
+
if (typeof columnDef.default == 'string' && columnDef.default[0] == '(') {
|
|
29
|
+
col = col.defaultTo(sql.raw(columnDef.default));
|
|
30
|
+
} else {
|
|
31
|
+
col = col.defaultTo(sql.lit(columnDef.default));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (columnDef.references) {
|
|
36
|
+
if (!columnDef.references.columns.length || columnDef.references.columns.length > 1)
|
|
37
|
+
throw new Error('Only one column can be referenced');
|
|
38
|
+
|
|
39
|
+
const column_name = columnDef.references.columns[0];
|
|
40
|
+
const schema_name = [
|
|
41
|
+
...columnDef.references.table.scheme_parts,
|
|
42
|
+
columnDef.references.table.name,
|
|
43
|
+
column_name,
|
|
44
|
+
].join('.');
|
|
45
|
+
|
|
46
|
+
col = col.references(schema_name);
|
|
47
|
+
|
|
48
|
+
if (columnDef.references.constraints) {
|
|
49
|
+
col = col.modifyEnd(sql.raw(` ${columnDef.references.constraints}`));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return col;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function itAddColumn<TABLE_NAME extends string>(builder: CreateTableBuilder<TABLE_NAME, never>, columns: ReturnType<typeof SQLParser['sqliteColumnsDef']>) {
|
|
57
|
+
for (const column of columns) {
|
|
58
|
+
builder = builder.addColumn(
|
|
59
|
+
column.name,
|
|
60
|
+
column.type.toLowerCase() as Lowercase<typeof column.type>,
|
|
61
|
+
col => columnDefinition(col, column),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return builder;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function tmpTableName(table: string) {
|
|
69
|
+
return `alter_${Math.floor(Date.now() / 1000)}_${table}` as any;
|
|
70
|
+
}
|
package/src/defaults.ts
ADDED