metal-orm 1.0.42 → 1.0.43
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 +22 -7
- package/dist/index.cjs +130 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +121 -96
- package/dist/index.d.ts +121 -96
- package/dist/index.js +128 -74
- package/dist/index.js.map +1 -1
- package/package.json +8 -2
- package/scripts/run-eslint.mjs +34 -0
- package/src/codegen/typescript.ts +32 -15
- package/src/core/ast/builders.ts +7 -2
- package/src/core/ast/expression-builders.ts +0 -2
- package/src/core/ast/expression-nodes.ts +14 -5
- package/src/core/ast/expression-visitor.ts +11 -8
- package/src/core/ast/join-node.ts +1 -1
- package/src/core/ast/query.ts +6 -6
- package/src/core/ast/window-functions.ts +10 -2
- package/src/core/ddl/dialects/base-schema-dialect.ts +30 -3
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +4 -0
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +2 -0
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +13 -1
- package/src/core/ddl/dialects/render-reference.test.ts +69 -0
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +9 -0
- package/src/core/ddl/introspect/mssql.ts +42 -8
- package/src/core/ddl/introspect/mysql.ts +30 -6
- package/src/core/ddl/introspect/postgres.ts +88 -34
- package/src/core/ddl/introspect/run-select.ts +6 -4
- package/src/core/ddl/introspect/sqlite.ts +56 -11
- package/src/core/ddl/introspect/types.ts +0 -1
- package/src/core/ddl/introspect/utils.ts +3 -3
- package/src/core/ddl/schema-dialect.ts +1 -0
- package/src/core/ddl/schema-generator.ts +4 -12
- package/src/core/ddl/sql-writing.ts +4 -4
- package/src/core/dialect/abstract.ts +18 -6
- package/src/core/dialect/base/function-table-formatter.ts +3 -2
- package/src/core/dialect/base/join-compiler.ts +5 -3
- package/src/core/dialect/base/returning-strategy.ts +1 -0
- package/src/core/dialect/base/sql-dialect.ts +3 -3
- package/src/core/dialect/mssql/functions.ts +24 -25
- package/src/core/dialect/mssql/index.ts +1 -4
- package/src/core/dialect/mysql/functions.ts +0 -1
- package/src/core/dialect/postgres/functions.ts +33 -34
- package/src/core/dialect/postgres/index.ts +1 -0
- package/src/core/dialect/sqlite/functions.ts +18 -19
- package/src/core/dialect/sqlite/index.ts +2 -0
- package/src/core/execution/db-executor.ts +1 -1
- package/src/core/execution/executors/mysql-executor.ts +2 -2
- package/src/core/execution/executors/postgres-executor.ts +1 -1
- package/src/core/execution/pooling/pool.ts +2 -0
- package/src/core/functions/datetime.ts +1 -1
- package/src/core/functions/numeric.ts +1 -1
- package/src/core/functions/text.ts +1 -1
- package/src/decorators/bootstrap.ts +27 -8
- package/src/decorators/column.ts +3 -11
- package/src/decorators/decorator-metadata.ts +3 -9
- package/src/decorators/entity.ts +21 -5
- package/src/decorators/relations.ts +2 -11
- package/src/orm/entity-context.ts +8 -8
- package/src/orm/entity-meta.ts +8 -8
- package/src/orm/entity-metadata.ts +11 -9
- package/src/orm/entity.ts +28 -29
- package/src/orm/execute.ts +4 -4
- package/src/orm/hydration.ts +42 -39
- package/src/orm/identity-map.ts +1 -1
- package/src/orm/lazy-batch.ts +9 -9
- package/src/orm/orm-session.ts +24 -23
- package/src/orm/orm.ts +2 -5
- package/src/orm/relation-change-processor.ts +12 -11
- package/src/orm/relations/belongs-to.ts +11 -11
- package/src/orm/relations/has-many.ts +10 -10
- package/src/orm/relations/has-one.ts +8 -7
- package/src/orm/relations/many-to-many.ts +13 -13
- package/src/orm/runtime-types.ts +4 -4
- package/src/orm/save-graph.ts +31 -25
- package/src/orm/unit-of-work.ts +17 -17
- package/src/query-builder/delete.ts +4 -3
- package/src/query-builder/hydration-manager.ts +6 -5
- package/src/query-builder/insert.ts +12 -8
- package/src/query-builder/query-ast-service.ts +2 -2
- package/src/query-builder/raw-column-parser.ts +2 -1
- package/src/query-builder/select-helpers.ts +2 -2
- package/src/query-builder/select.ts +31 -31
- package/src/query-builder/update.ts +4 -3
- package/src/schema/column.ts +26 -26
- package/src/schema/table.ts +47 -18
- package/src/schema/types.ts +22 -22
|
@@ -3,13 +3,37 @@ import { queryRows, shouldIncludeTable } from './utils.js';
|
|
|
3
3
|
import { DatabaseSchema, DatabaseTable, DatabaseIndex, DatabaseColumn } from '../schema-types.js';
|
|
4
4
|
import { DbExecutor } from '../../execution/db-executor.js';
|
|
5
5
|
|
|
6
|
+
type MysqlColumnRow = {
|
|
7
|
+
table_schema: string;
|
|
8
|
+
table_name: string;
|
|
9
|
+
column_name: string;
|
|
10
|
+
data_type: string;
|
|
11
|
+
is_nullable: string;
|
|
12
|
+
column_default: string | null;
|
|
13
|
+
extra: string | null;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type MysqlPrimaryKeyRow = {
|
|
17
|
+
table_schema: string;
|
|
18
|
+
table_name: string;
|
|
19
|
+
column_name: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type MysqlIndexRow = {
|
|
23
|
+
table_schema: string;
|
|
24
|
+
table_name: string;
|
|
25
|
+
index_name: string;
|
|
26
|
+
non_unique: number;
|
|
27
|
+
cols: string | null;
|
|
28
|
+
};
|
|
29
|
+
|
|
6
30
|
export const mysqlIntrospector: SchemaIntrospector = {
|
|
7
31
|
async introspect(ctx: { executor: DbExecutor }, options: IntrospectOptions): Promise<DatabaseSchema> {
|
|
8
32
|
const schema = options.schema;
|
|
9
33
|
const filterClause = schema ? 'table_schema = ?' : 'table_schema = database()';
|
|
10
34
|
const params = schema ? [schema] : [];
|
|
11
35
|
|
|
12
|
-
const columnRows = await queryRows(
|
|
36
|
+
const columnRows = (await queryRows(
|
|
13
37
|
ctx.executor,
|
|
14
38
|
`
|
|
15
39
|
SELECT table_schema, table_name, column_name, data_type, is_nullable, column_default, extra
|
|
@@ -18,9 +42,9 @@ export const mysqlIntrospector: SchemaIntrospector = {
|
|
|
18
42
|
ORDER BY table_name, ordinal_position
|
|
19
43
|
`,
|
|
20
44
|
params
|
|
21
|
-
);
|
|
45
|
+
)) as MysqlColumnRow[];
|
|
22
46
|
|
|
23
|
-
const pkRows = await queryRows(
|
|
47
|
+
const pkRows = (await queryRows(
|
|
24
48
|
ctx.executor,
|
|
25
49
|
`
|
|
26
50
|
SELECT table_schema, table_name, column_name
|
|
@@ -29,7 +53,7 @@ export const mysqlIntrospector: SchemaIntrospector = {
|
|
|
29
53
|
ORDER BY ordinal_position
|
|
30
54
|
`,
|
|
31
55
|
params
|
|
32
|
-
);
|
|
56
|
+
)) as MysqlPrimaryKeyRow[];
|
|
33
57
|
|
|
34
58
|
const pkMap = new Map<string, string[]>();
|
|
35
59
|
pkRows.forEach(r => {
|
|
@@ -39,7 +63,7 @@ export const mysqlIntrospector: SchemaIntrospector = {
|
|
|
39
63
|
pkMap.set(key, list);
|
|
40
64
|
});
|
|
41
65
|
|
|
42
|
-
const indexRows = await queryRows(
|
|
66
|
+
const indexRows = (await queryRows(
|
|
43
67
|
ctx.executor,
|
|
44
68
|
`
|
|
45
69
|
SELECT
|
|
@@ -53,7 +77,7 @@ export const mysqlIntrospector: SchemaIntrospector = {
|
|
|
53
77
|
GROUP BY table_schema, table_name, index_name, non_unique
|
|
54
78
|
`,
|
|
55
79
|
params
|
|
56
|
-
);
|
|
80
|
+
)) as MysqlIndexRow[];
|
|
57
81
|
|
|
58
82
|
const tablesByKey = new Map<string, DatabaseTable>();
|
|
59
83
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { SchemaIntrospector, IntrospectOptions } from './types.js';
|
|
2
|
-
import {
|
|
2
|
+
import { shouldIncludeTable } from './utils.js';
|
|
3
3
|
import { DatabaseSchema, DatabaseTable, DatabaseIndex, DatabaseColumn } from '../schema-types.js';
|
|
4
|
-
import type {
|
|
4
|
+
import type { ReferentialAction } from '../../../schema/column.js';
|
|
5
5
|
import type { IntrospectContext } from './context.js';
|
|
6
6
|
import { PgInformationSchemaColumns } from './catalogs/postgres.js';
|
|
7
|
-
import { PgKeyColumnUsage, PgTableConstraints, PgConstraintColumnUsage, PgReferentialConstraints
|
|
7
|
+
import { PgKeyColumnUsage, PgTableConstraints, PgConstraintColumnUsage, PgReferentialConstraints } from './catalogs/postgres.js';
|
|
8
8
|
import { SelectQueryBuilder } from '../../../query-builder/select.js';
|
|
9
9
|
import { eq, and } from '../../ast/expression-builders.js';
|
|
10
10
|
import type { SelectQueryNode, TableNode } from '../../ast/query.js';
|
|
@@ -13,13 +13,67 @@ import type { ColumnNode, ExpressionNode } from '../../ast/expression-nodes.js';
|
|
|
13
13
|
import { fnTable } from '../../ast/builders.js';
|
|
14
14
|
import { runSelect, runSelectNode } from './run-select.js';
|
|
15
15
|
|
|
16
|
+
type ColumnIntrospectRow = {
|
|
17
|
+
table_schema: string;
|
|
18
|
+
table_name: string;
|
|
19
|
+
column_name: string;
|
|
20
|
+
data_type: string;
|
|
21
|
+
is_nullable: string;
|
|
22
|
+
column_default: string | null;
|
|
23
|
+
ordinal_position: number | null;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type PrimaryKeyIntrospectRow = {
|
|
27
|
+
table_schema: string;
|
|
28
|
+
table_name: string;
|
|
29
|
+
column_name: string;
|
|
30
|
+
ordinal_position: number | null;
|
|
31
|
+
constraint_name: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type ForeignKeyIntrospectRow = {
|
|
35
|
+
table_schema: string;
|
|
36
|
+
table_name: string;
|
|
37
|
+
column_name: string;
|
|
38
|
+
constraint_name: string;
|
|
39
|
+
foreign_table_schema: string;
|
|
40
|
+
foreign_table_name: string;
|
|
41
|
+
foreign_column_name: string;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
type ForeignKeyEntry = {
|
|
45
|
+
table: string;
|
|
46
|
+
column: string;
|
|
47
|
+
onDelete?: ReferentialAction;
|
|
48
|
+
onUpdate?: ReferentialAction;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
type IndexQueryRow = {
|
|
52
|
+
table_schema: string;
|
|
53
|
+
table_name: string;
|
|
54
|
+
index_name: string;
|
|
55
|
+
is_unique: boolean;
|
|
56
|
+
predicate: string | null;
|
|
57
|
+
attname: string | null;
|
|
58
|
+
ord: number | null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
type IndexGroup = {
|
|
62
|
+
table_schema: string;
|
|
63
|
+
table_name: string;
|
|
64
|
+
index_name: string;
|
|
65
|
+
is_unique: boolean;
|
|
66
|
+
predicate: string | null;
|
|
67
|
+
cols: { ord: number; att: string | null }[];
|
|
68
|
+
};
|
|
69
|
+
|
|
16
70
|
export const postgresIntrospector: SchemaIntrospector = {
|
|
17
71
|
async introspect(ctx: IntrospectContext, options: IntrospectOptions): Promise<DatabaseSchema> {
|
|
18
72
|
const schema = options.schema || 'public';
|
|
19
73
|
const tables: DatabaseTable[] = [];
|
|
20
74
|
|
|
21
75
|
// Columns query
|
|
22
|
-
const qbColumns = new SelectQueryBuilder(PgInformationSchemaColumns
|
|
76
|
+
const qbColumns = new SelectQueryBuilder(PgInformationSchemaColumns)
|
|
23
77
|
.select({
|
|
24
78
|
table_schema: PgInformationSchemaColumns.columns.table_schema,
|
|
25
79
|
table_name: PgInformationSchemaColumns.columns.table_name,
|
|
@@ -33,10 +87,10 @@ export const postgresIntrospector: SchemaIntrospector = {
|
|
|
33
87
|
.orderBy(PgInformationSchemaColumns.columns.table_name)
|
|
34
88
|
.orderBy(PgInformationSchemaColumns.columns.ordinal_position);
|
|
35
89
|
|
|
36
|
-
const columnRows = await runSelect(qbColumns, ctx);
|
|
90
|
+
const columnRows = await runSelect<ColumnIntrospectRow>(qbColumns, ctx);
|
|
37
91
|
|
|
38
92
|
// Primary key columns query
|
|
39
|
-
const qbPk = new SelectQueryBuilder(PgKeyColumnUsage
|
|
93
|
+
const qbPk = new SelectQueryBuilder(PgKeyColumnUsage)
|
|
40
94
|
.select({
|
|
41
95
|
table_schema: PgKeyColumnUsage.columns.table_schema,
|
|
42
96
|
table_name: PgKeyColumnUsage.columns.table_name,
|
|
@@ -44,13 +98,13 @@ export const postgresIntrospector: SchemaIntrospector = {
|
|
|
44
98
|
ordinal_position: PgKeyColumnUsage.columns.ordinal_position,
|
|
45
99
|
constraint_name: PgKeyColumnUsage.columns.constraint_name
|
|
46
100
|
})
|
|
47
|
-
.innerJoin(PgTableConstraints
|
|
101
|
+
.innerJoin(PgTableConstraints, eq(PgTableConstraints.columns.constraint_name, PgKeyColumnUsage.columns.constraint_name))
|
|
48
102
|
.where(eq(PgTableConstraints.columns.constraint_type, 'PRIMARY KEY'))
|
|
49
103
|
.where(eq(PgKeyColumnUsage.columns.table_schema, schema))
|
|
50
104
|
.orderBy(PgKeyColumnUsage.columns.table_name)
|
|
51
105
|
.orderBy(PgKeyColumnUsage.columns.ordinal_position);
|
|
52
106
|
|
|
53
|
-
const pkRows = await runSelect(qbPk, ctx);
|
|
107
|
+
const pkRows = await runSelect<PrimaryKeyIntrospectRow>(qbPk, ctx);
|
|
54
108
|
|
|
55
109
|
// Build primary key map (grouped by table, ordered by ordinal_position)
|
|
56
110
|
const pkMap = new Map<string, string[]>();
|
|
@@ -67,7 +121,7 @@ export const postgresIntrospector: SchemaIntrospector = {
|
|
|
67
121
|
}
|
|
68
122
|
|
|
69
123
|
// Foreign key columns query
|
|
70
|
-
const qbFk = new SelectQueryBuilder(PgKeyColumnUsage
|
|
124
|
+
const qbFk = new SelectQueryBuilder(PgKeyColumnUsage)
|
|
71
125
|
.select({
|
|
72
126
|
table_schema: PgKeyColumnUsage.columns.table_schema,
|
|
73
127
|
table_name: PgKeyColumnUsage.columns.table_name,
|
|
@@ -77,16 +131,16 @@ export const postgresIntrospector: SchemaIntrospector = {
|
|
|
77
131
|
foreign_table_name: PgConstraintColumnUsage.columns.table_name,
|
|
78
132
|
foreign_column_name: PgConstraintColumnUsage.columns.column_name
|
|
79
133
|
})
|
|
80
|
-
.innerJoin(PgTableConstraints
|
|
81
|
-
.innerJoin(PgConstraintColumnUsage
|
|
82
|
-
.innerJoin(PgReferentialConstraints
|
|
134
|
+
.innerJoin(PgTableConstraints, eq(PgTableConstraints.columns.constraint_name, PgKeyColumnUsage.columns.constraint_name))
|
|
135
|
+
.innerJoin(PgConstraintColumnUsage, eq(PgConstraintColumnUsage.columns.constraint_name, PgTableConstraints.columns.constraint_name))
|
|
136
|
+
.innerJoin(PgReferentialConstraints, eq(PgReferentialConstraints.columns.constraint_name, PgTableConstraints.columns.constraint_name))
|
|
83
137
|
.where(eq(PgTableConstraints.columns.constraint_type, 'FOREIGN KEY'))
|
|
84
138
|
.where(eq(PgKeyColumnUsage.columns.table_schema, schema));
|
|
85
139
|
|
|
86
|
-
const fkRows = await runSelect(qbFk, ctx);
|
|
140
|
+
const fkRows = await runSelect<ForeignKeyIntrospectRow>(qbFk, ctx);
|
|
87
141
|
|
|
88
142
|
// Build foreign key map
|
|
89
|
-
const fkMap = new Map<string,
|
|
143
|
+
const fkMap = new Map<string, ForeignKeyEntry[]>();
|
|
90
144
|
for (const r of fkRows) {
|
|
91
145
|
const key = `${r.table_schema}.${r.table_name}.${r.column_name}`;
|
|
92
146
|
const existing = fkMap.get(key) ?? [];
|
|
@@ -138,10 +192,10 @@ export const postgresIntrospector: SchemaIntrospector = {
|
|
|
138
192
|
{
|
|
139
193
|
type: 'Join',
|
|
140
194
|
kind: 'INNER',
|
|
141
|
-
table: fnTable('unnest', [{ type: 'Column', table: 'i', name: 'indkey' } as ColumnNode], 'arr', {
|
|
142
|
-
lateral: true,
|
|
143
|
-
withOrdinality: true,
|
|
144
|
-
columnAliases: ['attnum', 'idx']
|
|
195
|
+
table: fnTable('unnest', [{ type: 'Column', table: 'i', name: 'indkey' } as ColumnNode], 'arr', {
|
|
196
|
+
lateral: true,
|
|
197
|
+
withOrdinality: true,
|
|
198
|
+
columnAliases: ['attnum', 'idx']
|
|
145
199
|
}),
|
|
146
200
|
condition: { type: 'BinaryExpression', left: { type: 'Literal', value: 1 }, operator: '=', right: { type: 'Literal', value: 1 } } as unknown as ExpressionNode
|
|
147
201
|
} as JoinNode,
|
|
@@ -158,23 +212,23 @@ export const postgresIntrospector: SchemaIntrospector = {
|
|
|
158
212
|
],
|
|
159
213
|
where: and(
|
|
160
214
|
eq({ table: 'ns', name: 'nspname' }, schema) as ExpressionNode,
|
|
161
|
-
eq({ table: 'i', name: 'indisprimary' },
|
|
215
|
+
eq({ table: 'i', name: 'indisprimary' }, false) as ExpressionNode
|
|
162
216
|
) as ExpressionNode
|
|
163
217
|
};
|
|
164
218
|
|
|
165
|
-
const indexQueryRows = await runSelectNode(indexQuery, ctx);
|
|
166
|
-
|
|
219
|
+
const indexQueryRows = await runSelectNode<IndexQueryRow>(indexQuery, ctx);
|
|
220
|
+
|
|
167
221
|
// Aggregate index rows by table/index to build final index list
|
|
168
|
-
const indexGrouped = new Map<string,
|
|
222
|
+
const indexGrouped = new Map<string, IndexGroup>();
|
|
169
223
|
for (const r of indexQueryRows) {
|
|
170
224
|
const key = `${r.table_schema}.${r.table_name}.${r.index_name}`;
|
|
171
|
-
const entry = indexGrouped.get(key) ?? {
|
|
172
|
-
table_schema: r.table_schema,
|
|
173
|
-
table_name: r.table_name,
|
|
174
|
-
index_name: r.index_name,
|
|
175
|
-
is_unique: r.is_unique,
|
|
176
|
-
predicate: r.predicate,
|
|
177
|
-
cols: []
|
|
225
|
+
const entry = indexGrouped.get(key) ?? {
|
|
226
|
+
table_schema: r.table_schema,
|
|
227
|
+
table_name: r.table_name,
|
|
228
|
+
index_name: r.index_name,
|
|
229
|
+
is_unique: r.is_unique,
|
|
230
|
+
predicate: r.predicate,
|
|
231
|
+
cols: []
|
|
178
232
|
};
|
|
179
233
|
entry.cols.push({ ord: r.ord ?? 0, att: r.attname ?? null });
|
|
180
234
|
indexGrouped.set(key, entry);
|
|
@@ -215,11 +269,11 @@ export const postgresIntrospector: SchemaIntrospector = {
|
|
|
215
269
|
default: r.column_default ?? undefined,
|
|
216
270
|
references: fk
|
|
217
271
|
? {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
272
|
+
table: fk.table,
|
|
273
|
+
column: fk.column,
|
|
274
|
+
onDelete: fk.onDelete,
|
|
275
|
+
onUpdate: fk.onUpdate
|
|
276
|
+
}
|
|
223
277
|
: undefined
|
|
224
278
|
};
|
|
225
279
|
cols.columns.push(column);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { SelectQueryBuilder } from '../../../query-builder/select.js';
|
|
2
1
|
import type { IntrospectContext } from './context.js';
|
|
2
|
+
import type { SelectQueryNode } from '../../ast/query.js';
|
|
3
3
|
|
|
4
4
|
import { toRows } from './utils.js';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
type SelectQuerySource = { getAST(): SelectQueryNode };
|
|
7
|
+
|
|
8
|
+
export async function runSelect<T = Record<string, unknown>>(
|
|
9
|
+
qb: SelectQuerySource,
|
|
8
10
|
ctx: IntrospectContext
|
|
9
11
|
): Promise<T[]> {
|
|
10
12
|
const ast = qb.getAST();
|
|
@@ -17,7 +19,7 @@ export async function runSelect<T = Record<string, any>>(
|
|
|
17
19
|
|
|
18
20
|
export default runSelect;
|
|
19
21
|
|
|
20
|
-
export async function runSelectNode<T = Record<string,
|
|
22
|
+
export async function runSelectNode<T = Record<string, unknown>>(ast: SelectQueryNode, ctx: IntrospectContext): Promise<T[]> {
|
|
21
23
|
const compiled = ctx.dialect.compileSelect(ast);
|
|
22
24
|
const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
|
|
23
25
|
const [first] = results;
|
|
@@ -1,24 +1,69 @@
|
|
|
1
1
|
import { SchemaIntrospector, IntrospectOptions } from './types.js';
|
|
2
2
|
import { queryRows, shouldIncludeTable } from './utils.js';
|
|
3
3
|
import { DatabaseSchema, DatabaseTable, DatabaseIndex } from '../schema-types.js';
|
|
4
|
+
import { ReferentialAction } from '../../../schema/column.js';
|
|
4
5
|
import { DbExecutor } from '../../execution/db-executor.js';
|
|
5
6
|
|
|
7
|
+
type SqliteTableRow = {
|
|
8
|
+
name: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type SqliteTableInfoRow = {
|
|
12
|
+
name: string;
|
|
13
|
+
type: string;
|
|
14
|
+
notnull: number;
|
|
15
|
+
dflt_value: string | null;
|
|
16
|
+
pk: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type SqliteForeignKeyRow = {
|
|
20
|
+
table: string;
|
|
21
|
+
from: string;
|
|
22
|
+
to: string;
|
|
23
|
+
on_delete: string | null;
|
|
24
|
+
on_update: string | null;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
type SqliteIndexListRow = {
|
|
28
|
+
name: string;
|
|
29
|
+
unique: number;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type SqliteIndexInfoRow = {
|
|
33
|
+
name: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const toReferentialAction = (value: string | null | undefined): ReferentialAction | undefined => {
|
|
37
|
+
if (!value) return undefined;
|
|
38
|
+
const normalized = value.toUpperCase();
|
|
39
|
+
if (
|
|
40
|
+
normalized === 'NO ACTION' ||
|
|
41
|
+
normalized === 'RESTRICT' ||
|
|
42
|
+
normalized === 'CASCADE' ||
|
|
43
|
+
normalized === 'SET NULL' ||
|
|
44
|
+
normalized === 'SET DEFAULT'
|
|
45
|
+
) {
|
|
46
|
+
return normalized as ReferentialAction;
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
};
|
|
50
|
+
|
|
6
51
|
const escapeSingleQuotes = (name: string) => name.replace(/'/g, "''");
|
|
7
52
|
|
|
8
53
|
export const sqliteIntrospector: SchemaIntrospector = {
|
|
9
54
|
async introspect(ctx: { executor: DbExecutor }, options: IntrospectOptions): Promise<DatabaseSchema> {
|
|
10
55
|
const tables: DatabaseTable[] = [];
|
|
11
|
-
const tableRows = await queryRows(
|
|
56
|
+
const tableRows = (await queryRows(
|
|
12
57
|
ctx.executor,
|
|
13
58
|
`SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%';`
|
|
14
|
-
);
|
|
59
|
+
)) as SqliteTableRow[];
|
|
15
60
|
|
|
16
61
|
for (const row of tableRows) {
|
|
17
|
-
const name = row.name
|
|
62
|
+
const name = row.name;
|
|
18
63
|
if (!shouldIncludeTable(name, options)) continue;
|
|
19
64
|
const table: DatabaseTable = { name, columns: [], primaryKey: [], indexes: [] };
|
|
20
65
|
|
|
21
|
-
const cols = await queryRows(ctx.executor, `PRAGMA table_info('${escapeSingleQuotes(name)}');`);
|
|
66
|
+
const cols = (await queryRows(ctx.executor, `PRAGMA table_info('${escapeSingleQuotes(name)}');`)) as SqliteTableInfoRow[];
|
|
22
67
|
cols.forEach(c => {
|
|
23
68
|
table.columns.push({
|
|
24
69
|
name: c.name,
|
|
@@ -33,26 +78,26 @@ export const sqliteIntrospector: SchemaIntrospector = {
|
|
|
33
78
|
}
|
|
34
79
|
});
|
|
35
80
|
|
|
36
|
-
const fkRows = await queryRows(ctx.executor, `PRAGMA foreign_key_list('${escapeSingleQuotes(name)}');`);
|
|
81
|
+
const fkRows = (await queryRows(ctx.executor, `PRAGMA foreign_key_list('${escapeSingleQuotes(name)}');`)) as SqliteForeignKeyRow[];
|
|
37
82
|
fkRows.forEach(fk => {
|
|
38
83
|
const col = table.columns.find(c => c.name === fk.from);
|
|
39
84
|
if (col) {
|
|
40
85
|
col.references = {
|
|
41
86
|
table: fk.table,
|
|
42
87
|
column: fk.to,
|
|
43
|
-
onDelete: fk.on_delete
|
|
44
|
-
onUpdate: fk.on_update
|
|
88
|
+
onDelete: toReferentialAction(fk.on_delete),
|
|
89
|
+
onUpdate: toReferentialAction(fk.on_update)
|
|
45
90
|
};
|
|
46
91
|
}
|
|
47
92
|
});
|
|
48
93
|
|
|
49
|
-
const idxList = await queryRows(ctx.executor, `PRAGMA index_list('${escapeSingleQuotes(name)}');`);
|
|
94
|
+
const idxList = (await queryRows(ctx.executor, `PRAGMA index_list('${escapeSingleQuotes(name)}');`)) as SqliteIndexListRow[];
|
|
50
95
|
for (const idx of idxList) {
|
|
51
|
-
const idxName = idx.name
|
|
52
|
-
const columnsInfo = await queryRows(ctx.executor, `PRAGMA index_info('${escapeSingleQuotes(idxName)}');`);
|
|
96
|
+
const idxName = idx.name;
|
|
97
|
+
const columnsInfo = (await queryRows(ctx.executor, `PRAGMA index_info('${escapeSingleQuotes(idxName)}');`)) as SqliteIndexInfoRow[];
|
|
53
98
|
const idxEntry: DatabaseIndex = {
|
|
54
99
|
name: idxName,
|
|
55
|
-
columns: columnsInfo.map(ci => ({ column: ci.name
|
|
100
|
+
columns: columnsInfo.map(ci => ({ column: ci.name })),
|
|
56
101
|
unique: idx.unique === 1
|
|
57
102
|
};
|
|
58
103
|
table.indexes!.push(idxEntry);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { DbExecutor, QueryResult } from '../../execution/db-executor.js';
|
|
2
2
|
import { IntrospectOptions } from './types.js';
|
|
3
3
|
|
|
4
|
-
export const toRows = (result: QueryResult | undefined): Record<string,
|
|
4
|
+
export const toRows = (result: QueryResult | undefined): Record<string, unknown>[] => {
|
|
5
5
|
if (!result) return [];
|
|
6
6
|
return result.values.map(row =>
|
|
7
|
-
result.columns.reduce<Record<string,
|
|
7
|
+
result.columns.reduce<Record<string, unknown>>((acc, col, idx) => {
|
|
8
8
|
acc[col] = row[idx];
|
|
9
9
|
return acc;
|
|
10
10
|
}, {})
|
|
@@ -15,7 +15,7 @@ export const queryRows = async (
|
|
|
15
15
|
executor: DbExecutor,
|
|
16
16
|
sql: string,
|
|
17
17
|
params: unknown[] = []
|
|
18
|
-
): Promise<Record<string,
|
|
18
|
+
): Promise<Record<string, unknown>[]> => {
|
|
19
19
|
const [first] = await executor.executeSql(sql, params);
|
|
20
20
|
return toRows(first);
|
|
21
21
|
};
|
|
@@ -30,6 +30,7 @@ export interface SchemaDialect {
|
|
|
30
30
|
|
|
31
31
|
// Capability flags
|
|
32
32
|
supportsPartialIndexes(): boolean;
|
|
33
|
+
preferInlinePkAutoincrement(column: ColumnDef, table: TableDef, pk: string[]): boolean;
|
|
33
34
|
|
|
34
35
|
// DDL operations
|
|
35
36
|
dropColumnSql?(table: DatabaseTable, column: string): string[];
|
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
import type { TableDef
|
|
2
|
-
import type { ColumnDef
|
|
1
|
+
import type { TableDef } from '../../schema/table.js';
|
|
2
|
+
import type { ColumnDef } from '../../schema/column.js';
|
|
3
3
|
import type { SchemaDialect } from './schema-dialect.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
formatLiteral,
|
|
7
|
-
renderIndexColumns,
|
|
8
|
-
quoteQualified,
|
|
9
|
-
resolvePrimaryKey,
|
|
10
|
-
Quoter
|
|
11
|
-
} from './sql-writing.js';
|
|
12
|
-
import { DatabaseTable, DatabaseColumn, ColumnDiff } from './schema-types.js';
|
|
4
|
+
import { resolvePrimaryKey } from './sql-writing.js';
|
|
13
5
|
import { DialectName } from './schema-dialect.js';
|
|
14
6
|
|
|
15
7
|
export interface SchemaGenerateResult {
|
|
@@ -60,7 +52,7 @@ export const generateCreateTableSql = (
|
|
|
60
52
|
const inlinePkColumns = new Set<string>();
|
|
61
53
|
|
|
62
54
|
const columnLines = Object.values(table.columns).map(col => {
|
|
63
|
-
const includePk =
|
|
55
|
+
const includePk = dialect.preferInlinePkAutoincrement(col, table, pk) && pk.includes(col.name);
|
|
64
56
|
if (includePk) {
|
|
65
57
|
inlinePkColumns.add(col.name);
|
|
66
58
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TableDef, IndexColumn } from '../../schema/table.js';
|
|
2
|
-
import type { RawDefaultValue } from '../../schema/column.js';
|
|
2
|
+
import type { ColumnDef, RawDefaultValue } from '../../schema/column.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Minimal surface for anything that can quote identifiers.
|
|
@@ -161,10 +161,10 @@ export const resolvePrimaryKey = (table: TableDef): string[] => {
|
|
|
161
161
|
return table.primaryKey;
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
const columns = Object.values(table.columns ?? {});
|
|
164
|
+
const columns = Object.values(table.columns ?? {}) as ColumnDef[];
|
|
165
165
|
|
|
166
166
|
// `primary` / `name` are domain-level properties of ColumnDef.
|
|
167
167
|
return columns
|
|
168
|
-
.filter(
|
|
169
|
-
.map(
|
|
168
|
+
.filter(col => col.primary)
|
|
169
|
+
.map(col => col.name);
|
|
170
170
|
};
|
|
@@ -157,8 +157,9 @@ export abstract class Dialect
|
|
|
157
157
|
|
|
158
158
|
protected compileReturning(
|
|
159
159
|
returning: ColumnNode[] | undefined,
|
|
160
|
-
|
|
160
|
+
_ctx: CompilerContext
|
|
161
161
|
): string {
|
|
162
|
+
void _ctx;
|
|
162
163
|
if (!returning || returning.length === 0) return '';
|
|
163
164
|
throw new Error('RETURNING is not supported by this dialect.');
|
|
164
165
|
}
|
|
@@ -213,7 +214,8 @@ export abstract class Dialect
|
|
|
213
214
|
* @param index - Parameter index
|
|
214
215
|
* @returns Formatted placeholder string
|
|
215
216
|
*/
|
|
216
|
-
protected formatPlaceholder(
|
|
217
|
+
protected formatPlaceholder(_index: number): string {
|
|
218
|
+
void _index;
|
|
217
219
|
return '?';
|
|
218
220
|
}
|
|
219
221
|
|
|
@@ -221,7 +223,8 @@ export abstract class Dialect
|
|
|
221
223
|
* Whether the current dialect supports a given set operation.
|
|
222
224
|
* Override in concrete dialects to restrict support.
|
|
223
225
|
*/
|
|
224
|
-
protected supportsSetOperation(
|
|
226
|
+
protected supportsSetOperation(_kind: SetOperationKind): boolean {
|
|
227
|
+
void _kind;
|
|
225
228
|
return true;
|
|
226
229
|
}
|
|
227
230
|
|
|
@@ -376,6 +379,7 @@ export abstract class Dialect
|
|
|
376
379
|
if (isOperandNode(term)) {
|
|
377
380
|
return this.compileOperand(term, ctx);
|
|
378
381
|
}
|
|
382
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
379
383
|
const expr = this.compileExpression(term as any, ctx);
|
|
380
384
|
return `(${expr})`;
|
|
381
385
|
}
|
|
@@ -438,15 +442,22 @@ export abstract class Dialect
|
|
|
438
442
|
private registerDefaultOperandCompilers(): void {
|
|
439
443
|
this.registerOperandCompiler('Literal', (literal: LiteralNode, ctx) => ctx.addParameter(literal.value));
|
|
440
444
|
|
|
441
|
-
this.registerOperandCompiler('AliasRef', (alias: AliasRefNode, _ctx) =>
|
|
445
|
+
this.registerOperandCompiler('AliasRef', (alias: AliasRefNode, _ctx) => {
|
|
446
|
+
void _ctx;
|
|
447
|
+
return this.quoteIdentifier(alias.name);
|
|
448
|
+
});
|
|
442
449
|
|
|
443
450
|
this.registerOperandCompiler('Column', (column: ColumnNode, _ctx) => {
|
|
451
|
+
void _ctx;
|
|
444
452
|
return `${this.quoteIdentifier(column.table)}.${this.quoteIdentifier(column.name)}`;
|
|
445
453
|
});
|
|
446
454
|
this.registerOperandCompiler('Function', (fnNode: FunctionNode, ctx) =>
|
|
447
455
|
this.compileFunctionOperand(fnNode, ctx)
|
|
448
456
|
);
|
|
449
|
-
this.registerOperandCompiler('JsonPath', (path: JsonPathNode, _ctx) =>
|
|
457
|
+
this.registerOperandCompiler('JsonPath', (path: JsonPathNode, _ctx) => {
|
|
458
|
+
void _ctx;
|
|
459
|
+
return this.compileJsonPath(path);
|
|
460
|
+
});
|
|
450
461
|
|
|
451
462
|
this.registerOperandCompiler('ScalarSubquery', (node: ScalarSubqueryNode, ctx) => {
|
|
452
463
|
const sql = this.compileSelectAst(node.query, ctx).trim().replace(/;$/, '');
|
|
@@ -499,7 +510,8 @@ export abstract class Dialect
|
|
|
499
510
|
}
|
|
500
511
|
|
|
501
512
|
// Default fallback, should be overridden by dialects if supported
|
|
502
|
-
protected compileJsonPath(
|
|
513
|
+
protected compileJsonPath(_node: JsonPathNode): string {
|
|
514
|
+
void _node;
|
|
503
515
|
throw new Error("JSON Path not supported by this dialect");
|
|
504
516
|
}
|
|
505
517
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CompilerContext } from '../abstract.js';
|
|
2
|
+
import { OperandNode } from '../../ast/expression.js';
|
|
2
3
|
import { SqlDialectBase } from './sql-dialect.js';
|
|
3
4
|
|
|
4
5
|
export interface FunctionTableNode {
|
|
@@ -57,9 +58,9 @@ export class FunctionTableFormatter {
|
|
|
57
58
|
*/
|
|
58
59
|
private static formatArgs(fn: FunctionTableNode, ctx?: CompilerContext, dialect?: SqlDialectBase): string {
|
|
59
60
|
return (fn.args || [])
|
|
60
|
-
.map((a:
|
|
61
|
+
.map((a: OperandNode) => {
|
|
61
62
|
if (ctx && dialect) {
|
|
62
|
-
return (dialect as
|
|
63
|
+
return (dialect as unknown as { compileOperand(n: OperandNode, c: CompilerContext): string }).compileOperand(a, ctx);
|
|
63
64
|
}
|
|
64
65
|
return String(a);
|
|
65
66
|
})
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { CompilerContext } from '../abstract.js';
|
|
2
2
|
import { JoinNode } from '../../ast/join.js';
|
|
3
|
+
import { TableSourceNode } from '../../ast/query.js';
|
|
4
|
+
import { ExpressionNode } from '../../ast/expression.js';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Compiler for JOIN clauses in SELECT statements.
|
|
@@ -9,12 +11,12 @@ export class JoinCompiler {
|
|
|
9
11
|
static compileJoins(
|
|
10
12
|
joins: JoinNode[] | undefined,
|
|
11
13
|
ctx: CompilerContext,
|
|
12
|
-
compileFrom: (from:
|
|
13
|
-
compileExpression: (expr:
|
|
14
|
+
compileFrom: (from: TableSourceNode, ctx: CompilerContext) => string,
|
|
15
|
+
compileExpression: (expr: ExpressionNode, ctx: CompilerContext) => string
|
|
14
16
|
): string {
|
|
15
17
|
if (!joins || joins.length === 0) return '';
|
|
16
18
|
const parts = joins.map(j => {
|
|
17
|
-
const table = compileFrom(j.table
|
|
19
|
+
const table = compileFrom(j.table, ctx);
|
|
18
20
|
const cond = compileExpression(j.condition, ctx);
|
|
19
21
|
return `${j.kind} JOIN ${table} ON ${cond}`;
|
|
20
22
|
});
|
|
@@ -35,6 +35,7 @@ export class NoReturningStrategy implements ReturningStrategy {
|
|
|
35
35
|
* @throws Error indicating RETURNING is not supported.
|
|
36
36
|
*/
|
|
37
37
|
compileReturning(returning: ColumnNode[] | undefined, _ctx: CompilerContext): string {
|
|
38
|
+
void _ctx;
|
|
38
39
|
if (!returning || returning.length === 0) return '';
|
|
39
40
|
throw new Error('RETURNING is not supported by this dialect.');
|
|
40
41
|
}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
OrderByNode,
|
|
12
12
|
TableNode
|
|
13
13
|
} from '../../ast/query.js';
|
|
14
|
-
import { ColumnNode } from '../../ast/expression.js';
|
|
14
|
+
import { ColumnNode, OperandNode } from '../../ast/expression.js';
|
|
15
15
|
import { FunctionTableFormatter } from './function-table-formatter.js';
|
|
16
16
|
import { PaginationStrategy, StandardLimitOffsetPagination } from './pagination-strategy.js';
|
|
17
17
|
import { CteCompiler } from './cte-compiler.js';
|
|
@@ -134,7 +134,7 @@ export abstract class SqlDialectBase extends Dialect {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
private compileUpdateAssignments(
|
|
137
|
-
assignments: { column: ColumnNode; value:
|
|
137
|
+
assignments: { column: ColumnNode; value: OperandNode }[],
|
|
138
138
|
table: TableNode,
|
|
139
139
|
ctx: CompilerContext
|
|
140
140
|
): string {
|
|
@@ -190,7 +190,7 @@ export abstract class SqlDialectBase extends Dialect {
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
protected compileFrom(ast: SelectQueryNode['from'], ctx?: CompilerContext): string {
|
|
193
|
-
const tableSource = ast
|
|
193
|
+
const tableSource = ast;
|
|
194
194
|
if (tableSource.type === 'FunctionTable') {
|
|
195
195
|
return this.compileFunctionTable(tableSource, ctx);
|
|
196
196
|
}
|