metal-orm 1.0.46 → 1.0.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1037 -336
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -6
- package/dist/index.d.ts +28 -6
- package/dist/index.js +1035 -336
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/generate-entities.mjs +74 -65
- package/scripts/naming-strategy.mjs +148 -0
- package/src/codegen/typescript.ts +22 -10
- package/src/core/ast/expression-builders.ts +13 -0
- package/src/core/ast/expression-nodes.ts +25 -5
- package/src/core/ast/expression-visitor.ts +5 -0
- package/src/core/ast/query.ts +9 -1
- package/src/core/ddl/introspect/catalogs/index.ts +4 -1
- package/src/core/ddl/introspect/catalogs/mssql.ts +126 -0
- package/src/core/ddl/introspect/catalogs/mysql.ts +89 -0
- package/src/core/ddl/introspect/catalogs/sqlite.ts +47 -0
- package/src/core/ddl/introspect/functions/mssql.ts +84 -0
- package/src/core/ddl/introspect/mssql.ts +471 -210
- package/src/core/ddl/introspect/mysql.ts +336 -125
- package/src/core/ddl/introspect/postgres.ts +44 -5
- package/src/core/ddl/introspect/run-select.ts +3 -8
- package/src/core/ddl/introspect/sqlite.ts +113 -60
- package/src/core/ddl/schema-types.ts +2 -1
- package/src/core/dialect/abstract.ts +12 -1
- package/src/core/dialect/mssql/index.ts +4 -10
- package/src/query-builder/query-ast-service.ts +3 -3
- package/src/query-builder/select-query-state.ts +2 -0
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { SchemaIntrospector, IntrospectOptions } from './types.js';
|
|
2
|
-
import {
|
|
2
|
+
import { shouldIncludeTable } from './utils.js';
|
|
3
3
|
import { DatabaseSchema, DatabaseTable, DatabaseIndex } from '../schema-types.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import type { IntrospectContext } from './context.js';
|
|
5
|
+
import { runSelectNode } from './run-select.js';
|
|
6
|
+
import type { SelectQueryNode, TableNode } from '../../ast/query.js';
|
|
7
|
+
import type { ColumnNode } from '../../ast/expression-nodes.js';
|
|
8
|
+
import { eq, notLike, and, valueToOperand } from '../../ast/expression-builders.js';
|
|
9
|
+
import { fnTable } from '../../ast/builders.js';
|
|
10
|
+
import type { ReferentialAction } from '../../../schema/column-types.js';
|
|
6
11
|
|
|
7
|
-
/** Row type for SQLite table list from sqlite_master. */
|
|
8
12
|
type SqliteTableRow = {
|
|
9
13
|
name: string;
|
|
10
14
|
};
|
|
11
15
|
|
|
12
|
-
/** Row type for SQLite table column information from PRAGMA table_info. */
|
|
13
16
|
type SqliteTableInfoRow = {
|
|
17
|
+
cid: number;
|
|
14
18
|
name: string;
|
|
15
19
|
type: string;
|
|
16
20
|
notnull: number;
|
|
@@ -18,31 +22,28 @@ type SqliteTableInfoRow = {
|
|
|
18
22
|
pk: number;
|
|
19
23
|
};
|
|
20
24
|
|
|
21
|
-
/** Row type for SQLite foreign key information from PRAGMA foreign_key_list. */
|
|
22
25
|
type SqliteForeignKeyRow = {
|
|
26
|
+
id: number;
|
|
27
|
+
seq: number;
|
|
23
28
|
table: string;
|
|
24
29
|
from: string;
|
|
25
30
|
to: string;
|
|
26
|
-
on_delete: string | null;
|
|
27
31
|
on_update: string | null;
|
|
32
|
+
on_delete: string | null;
|
|
28
33
|
};
|
|
29
34
|
|
|
30
|
-
/** Row type for SQLite index list from PRAGMA index_list. */
|
|
31
35
|
type SqliteIndexListRow = {
|
|
36
|
+
seq: number;
|
|
32
37
|
name: string;
|
|
33
38
|
unique: number;
|
|
34
39
|
};
|
|
35
40
|
|
|
36
|
-
/** Row type for SQLite index column information from PRAGMA index_info. */
|
|
37
41
|
type SqliteIndexInfoRow = {
|
|
42
|
+
seqno: number;
|
|
43
|
+
cid: number;
|
|
38
44
|
name: string;
|
|
39
45
|
};
|
|
40
46
|
|
|
41
|
-
/**
|
|
42
|
-
* Converts a SQLite referential action string to a ReferentialAction enum value.
|
|
43
|
-
* @param value - The string value from SQLite pragma (e.g., 'CASCADE', 'SET NULL').
|
|
44
|
-
* @returns The corresponding ReferentialAction enum value, or undefined if the value is invalid or null.
|
|
45
|
-
*/
|
|
46
47
|
const toReferentialAction = (value: string | null | undefined): ReferentialAction | undefined => {
|
|
47
48
|
if (!value) return undefined;
|
|
48
49
|
const normalized = value.toUpperCase();
|
|
@@ -58,53 +59,101 @@ const toReferentialAction = (value: string | null | undefined): ReferentialActio
|
|
|
58
59
|
return undefined;
|
|
59
60
|
};
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
const columnNode = (table: string, name: string, alias?: string): ColumnNode => ({
|
|
63
|
+
type: 'Column',
|
|
64
|
+
table,
|
|
65
|
+
name,
|
|
66
|
+
alias
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const buildPragmaQuery = (
|
|
70
|
+
name: string,
|
|
71
|
+
table: string,
|
|
72
|
+
alias: string,
|
|
73
|
+
columnAliases: string[]
|
|
74
|
+
): SelectQueryNode => ({
|
|
75
|
+
type: 'SelectQuery',
|
|
76
|
+
from: fnTable(name, [valueToOperand(table)], alias, { columnAliases }),
|
|
77
|
+
columns: columnAliases.map(column => columnNode(alias, column)),
|
|
78
|
+
joins: []
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const runPragma = async <T>(
|
|
82
|
+
name: string,
|
|
83
|
+
table: string,
|
|
84
|
+
alias: string,
|
|
85
|
+
columnAliases: string[],
|
|
86
|
+
ctx: IntrospectContext
|
|
87
|
+
): Promise<T[]> => {
|
|
88
|
+
const query = buildPragmaQuery(name, table, alias, columnAliases);
|
|
89
|
+
return (await runSelectNode<T>(query, ctx)) as T[];
|
|
90
|
+
};
|
|
67
91
|
|
|
68
|
-
/** SQLite schema introspector. */
|
|
69
92
|
export const sqliteIntrospector: SchemaIntrospector = {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
93
|
+
async introspect(ctx: IntrospectContext, options: IntrospectOptions): Promise<DatabaseSchema> {
|
|
94
|
+
const alias = 'sqlite_master';
|
|
95
|
+
const tablesQuery: SelectQueryNode = {
|
|
96
|
+
type: 'SelectQuery',
|
|
97
|
+
from: { type: 'Table', name: 'sqlite_master' } as TableNode,
|
|
98
|
+
columns: [columnNode(alias, 'name')],
|
|
99
|
+
joins: [],
|
|
100
|
+
where: and(
|
|
101
|
+
eq(columnNode(alias, 'type'), 'table'),
|
|
102
|
+
notLike(columnNode(alias, 'name'), 'sqlite_%')
|
|
103
|
+
)
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const tableRows = (await runSelectNode<SqliteTableRow>(tablesQuery, ctx)) as SqliteTableRow[];
|
|
77
107
|
const tables: DatabaseTable[] = [];
|
|
78
|
-
const tableRows = (await queryRows(
|
|
79
|
-
ctx.executor,
|
|
80
|
-
`SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%';`
|
|
81
|
-
)) as SqliteTableRow[];
|
|
82
108
|
|
|
83
109
|
for (const row of tableRows) {
|
|
84
|
-
const
|
|
85
|
-
if (!shouldIncludeTable(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
110
|
+
const tableName = row.name;
|
|
111
|
+
if (!shouldIncludeTable(tableName, options)) continue;
|
|
112
|
+
|
|
113
|
+
const tableInfo = await runPragma<SqliteTableInfoRow>(
|
|
114
|
+
'pragma_table_info',
|
|
115
|
+
tableName,
|
|
116
|
+
'ti',
|
|
117
|
+
['cid', 'name', 'type', 'notnull', 'dflt_value', 'pk'],
|
|
118
|
+
ctx
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const foreignKeys = await runPragma<SqliteForeignKeyRow>(
|
|
122
|
+
'pragma_foreign_key_list',
|
|
123
|
+
tableName,
|
|
124
|
+
'fk',
|
|
125
|
+
['id', 'seq', 'table', 'from', 'to', 'on_update', 'on_delete', 'match'],
|
|
126
|
+
ctx
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const indexList = await runPragma<SqliteIndexListRow>(
|
|
130
|
+
'pragma_index_list',
|
|
131
|
+
tableName,
|
|
132
|
+
'idx',
|
|
133
|
+
['seq', 'name', 'unique'],
|
|
134
|
+
ctx
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const tableEntry: DatabaseTable = { name: tableName, columns: [], primaryKey: [], indexes: [] };
|
|
138
|
+
|
|
139
|
+
tableInfo.forEach(info => {
|
|
140
|
+
tableEntry.columns.push({
|
|
141
|
+
name: info.name,
|
|
142
|
+
type: info.type,
|
|
143
|
+
notNull: info.notnull === 1,
|
|
144
|
+
default: info.dflt_value ?? undefined,
|
|
95
145
|
autoIncrement: false
|
|
96
146
|
});
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
147
|
+
if (info.pk && info.pk > 0) {
|
|
148
|
+
tableEntry.primaryKey = tableEntry.primaryKey || [];
|
|
149
|
+
tableEntry.primaryKey.push(info.name);
|
|
100
150
|
}
|
|
101
151
|
});
|
|
102
152
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
col.references = {
|
|
153
|
+
foreignKeys.forEach(fk => {
|
|
154
|
+
const column = tableEntry.columns.find(col => col.name === fk.from);
|
|
155
|
+
if (column) {
|
|
156
|
+
column.references = {
|
|
108
157
|
table: fk.table,
|
|
109
158
|
column: fk.to,
|
|
110
159
|
onDelete: toReferentialAction(fk.on_delete),
|
|
@@ -113,22 +162,26 @@ export const sqliteIntrospector: SchemaIntrospector = {
|
|
|
113
162
|
}
|
|
114
163
|
});
|
|
115
164
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
165
|
+
for (const idx of indexList) {
|
|
166
|
+
if (!idx.name) continue;
|
|
167
|
+
const indexColumns = await runPragma<SqliteIndexInfoRow>(
|
|
168
|
+
'pragma_index_info',
|
|
169
|
+
idx.name,
|
|
170
|
+
'info',
|
|
171
|
+
['seqno', 'cid', 'name'],
|
|
172
|
+
ctx
|
|
173
|
+
);
|
|
120
174
|
const idxEntry: DatabaseIndex = {
|
|
121
|
-
name:
|
|
122
|
-
columns:
|
|
175
|
+
name: idx.name,
|
|
176
|
+
columns: indexColumns.map(col => ({ column: col.name })),
|
|
123
177
|
unique: idx.unique === 1
|
|
124
178
|
};
|
|
125
|
-
|
|
179
|
+
tableEntry.indexes!.push(idxEntry);
|
|
126
180
|
}
|
|
127
181
|
|
|
128
|
-
tables.push(
|
|
182
|
+
tables.push(tableEntry);
|
|
129
183
|
}
|
|
130
184
|
|
|
131
185
|
return { tables };
|
|
132
186
|
}
|
|
133
187
|
};
|
|
134
|
-
|
|
@@ -19,6 +19,7 @@ export interface DatabaseColumn {
|
|
|
19
19
|
generated?: 'always' | 'byDefault';
|
|
20
20
|
unique?: boolean | string;
|
|
21
21
|
references?: ForeignKeyReference;
|
|
22
|
+
comment?: string;
|
|
22
23
|
check?: string;
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -44,10 +45,10 @@ export interface DatabaseTable {
|
|
|
44
45
|
primaryKey?: string[];
|
|
45
46
|
indexes?: DatabaseIndex[];
|
|
46
47
|
checks?: DatabaseCheck[];
|
|
48
|
+
comment?: string;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
/** Represents the overall database schema. */
|
|
50
52
|
export interface DatabaseSchema {
|
|
51
53
|
tables: DatabaseTable[];
|
|
52
54
|
}
|
|
53
|
-
|
|
@@ -21,10 +21,11 @@ import {
|
|
|
21
21
|
JsonPathNode,
|
|
22
22
|
ScalarSubqueryNode,
|
|
23
23
|
CaseExpressionNode,
|
|
24
|
+
CastExpressionNode,
|
|
24
25
|
WindowFunctionNode,
|
|
25
26
|
BetweenExpressionNode,
|
|
26
|
-
AliasRefNode,
|
|
27
27
|
ArithmeticExpressionNode,
|
|
28
|
+
AliasRefNode,
|
|
28
29
|
isOperandNode
|
|
29
30
|
} from '../ast/expression.js';
|
|
30
31
|
import { DialectName } from '../sql/sql.js';
|
|
@@ -476,6 +477,11 @@ export abstract class Dialect
|
|
|
476
477
|
return parts.join(' ');
|
|
477
478
|
});
|
|
478
479
|
|
|
480
|
+
this.registerOperandCompiler('Cast', (node: CastExpressionNode, ctx) => {
|
|
481
|
+
const value = this.compileOperand(node.expression, ctx);
|
|
482
|
+
return `CAST(${value} AS ${node.castType})`;
|
|
483
|
+
});
|
|
484
|
+
|
|
479
485
|
this.registerOperandCompiler('WindowFunction', (node: WindowFunctionNode, ctx) => {
|
|
480
486
|
let result = `${node.name}(`;
|
|
481
487
|
if (node.args.length > 0) {
|
|
@@ -507,6 +513,11 @@ export abstract class Dialect
|
|
|
507
513
|
|
|
508
514
|
return result;
|
|
509
515
|
});
|
|
516
|
+
this.registerOperandCompiler('ArithmeticExpression', (node: ArithmeticExpressionNode, ctx) => {
|
|
517
|
+
const left = this.compileOperand(node.left, ctx);
|
|
518
|
+
const right = this.compileOperand(node.right, ctx);
|
|
519
|
+
return `(${left} ${node.operator} ${right})`;
|
|
520
|
+
});
|
|
510
521
|
}
|
|
511
522
|
|
|
512
523
|
// Default fallback, should be overridden by dialects if supported
|
|
@@ -105,16 +105,10 @@ export class SqlServerDialect extends SqlDialectBase {
|
|
|
105
105
|
|
|
106
106
|
private compileSelectCoreForMssql(ast: SelectQueryNode, ctx: CompilerContext): string {
|
|
107
107
|
const columns = ast.columns.map(c => {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
expr = `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`;
|
|
113
|
-
} else if (c.type === 'ScalarSubquery') {
|
|
114
|
-
expr = this.compileOperand(c, ctx);
|
|
115
|
-
} else if (c.type === 'WindowFunction') {
|
|
116
|
-
expr = this.compileOperand(c, ctx);
|
|
117
|
-
}
|
|
108
|
+
// Default to full operand compilation for all projection node types (Function, Column, Cast, Case, Window, etc)
|
|
109
|
+
const expr = c.type === 'Column'
|
|
110
|
+
? `${this.quoteIdentifier(c.table)}.${this.quoteIdentifier(c.name)}`
|
|
111
|
+
: this.compileOperand(c as unknown as import('../../ast/expression.js').OperandNode, ctx);
|
|
118
112
|
|
|
119
113
|
if (c.alias) {
|
|
120
114
|
if (c.alias.includes('(')) return c.alias;
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
ExpressionNode,
|
|
16
16
|
FunctionNode,
|
|
17
17
|
CaseExpressionNode,
|
|
18
|
+
CastExpressionNode,
|
|
18
19
|
WindowFunctionNode,
|
|
19
20
|
ScalarSubqueryNode,
|
|
20
21
|
and,
|
|
@@ -57,7 +58,7 @@ export class QueryAstService {
|
|
|
57
58
|
* @returns Column selection result with updated state and added columns
|
|
58
59
|
*/
|
|
59
60
|
select(
|
|
60
|
-
columns: Record<string, ColumnDef | FunctionNode | CaseExpressionNode | WindowFunctionNode>
|
|
61
|
+
columns: Record<string, ColumnDef | FunctionNode | CaseExpressionNode | CastExpressionNode | WindowFunctionNode>
|
|
61
62
|
): ColumnSelectionResult {
|
|
62
63
|
const existingAliases = new Set(
|
|
63
64
|
this.state.ast.columns.map(c => (c as ColumnNode).alias || (c as ColumnNode).name)
|
|
@@ -69,7 +70,7 @@ export class QueryAstService {
|
|
|
69
70
|
if (existingAliases.has(alias)) return acc;
|
|
70
71
|
|
|
71
72
|
if (isExpressionSelectionNode(val)) {
|
|
72
|
-
acc.push({ ...(val as FunctionNode | CaseExpressionNode | WindowFunctionNode), alias } as ProjectionNode);
|
|
73
|
+
acc.push({ ...(val as FunctionNode | CaseExpressionNode | CastExpressionNode | WindowFunctionNode), alias } as ProjectionNode);
|
|
73
74
|
return acc;
|
|
74
75
|
}
|
|
75
76
|
|
|
@@ -284,4 +285,3 @@ export class QueryAstService {
|
|
|
284
285
|
}
|
|
285
286
|
|
|
286
287
|
}
|
|
287
|
-
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
FunctionNode,
|
|
15
15
|
ScalarSubqueryNode,
|
|
16
16
|
CaseExpressionNode,
|
|
17
|
+
CastExpressionNode,
|
|
17
18
|
WindowFunctionNode
|
|
18
19
|
} from '../core/ast/expression.js';
|
|
19
20
|
import { JoinNode } from '../core/ast/join.js';
|
|
@@ -26,6 +27,7 @@ export type ProjectionNode =
|
|
|
26
27
|
| FunctionNode
|
|
27
28
|
| ScalarSubqueryNode
|
|
28
29
|
| CaseExpressionNode
|
|
30
|
+
| CastExpressionNode
|
|
29
31
|
| WindowFunctionNode;
|
|
30
32
|
|
|
31
33
|
/**
|