metal-orm 1.0.43 → 1.0.45
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 +700 -557
- package/dist/index.cjs +896 -476
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1146 -275
- package/dist/index.d.ts +1146 -275
- package/dist/index.js +896 -474
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/ast/adapters.ts +8 -2
- package/src/core/ast/builders.ts +105 -81
- package/src/core/ast/expression-builders.ts +430 -390
- package/src/core/ast/expression-visitor.ts +47 -8
- package/src/core/ast/helpers.ts +23 -0
- package/src/core/ast/join-node.ts +17 -1
- package/src/core/ddl/dialects/base-schema-dialect.ts +7 -1
- package/src/core/ddl/dialects/index.ts +1 -0
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +1 -0
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +1 -0
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +1 -0
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +1 -0
- package/src/core/ddl/introspect/catalogs/index.ts +1 -0
- package/src/core/ddl/introspect/catalogs/postgres.ts +2 -0
- package/src/core/ddl/introspect/context.ts +6 -0
- package/src/core/ddl/introspect/functions/postgres.ts +13 -0
- package/src/core/ddl/introspect/mssql.ts +11 -0
- package/src/core/ddl/introspect/mysql.ts +2 -0
- package/src/core/ddl/introspect/postgres.ts +14 -0
- package/src/core/ddl/introspect/registry.ts +14 -0
- package/src/core/ddl/introspect/run-select.ts +13 -0
- package/src/core/ddl/introspect/sqlite.ts +22 -0
- package/src/core/ddl/introspect/utils.ts +18 -0
- package/src/core/ddl/naming-strategy.ts +6 -0
- package/src/core/ddl/schema-dialect.ts +19 -6
- package/src/core/ddl/schema-diff.ts +22 -0
- package/src/core/ddl/schema-generator.ts +22 -0
- package/src/core/ddl/schema-plan-executor.ts +6 -0
- package/src/core/ddl/schema-types.ts +6 -0
- package/src/core/dialect/abstract.ts +2 -2
- package/src/core/execution/pooling/pool.ts +12 -7
- package/src/core/functions/datetime.ts +57 -33
- package/src/core/functions/numeric.ts +95 -30
- package/src/core/functions/standard-strategy.ts +35 -0
- package/src/core/functions/text.ts +83 -22
- package/src/core/functions/types.ts +23 -8
- package/src/decorators/bootstrap.ts +16 -4
- package/src/decorators/column.ts +17 -0
- package/src/decorators/decorator-metadata.ts +27 -0
- package/src/decorators/entity.ts +8 -0
- package/src/decorators/index.ts +3 -0
- package/src/decorators/relations.ts +32 -0
- package/src/orm/als.ts +34 -9
- package/src/orm/entity-context.ts +54 -0
- package/src/orm/entity-metadata.ts +122 -9
- package/src/orm/execute.ts +15 -0
- package/src/orm/lazy-batch.ts +158 -98
- package/src/orm/relations/has-many.ts +44 -0
- package/src/orm/save-graph.ts +45 -0
- package/src/query/index.ts +74 -0
- package/src/query/target.ts +46 -0
- package/src/query-builder/delete-query-state.ts +30 -0
- package/src/query-builder/delete.ts +64 -19
- package/src/query-builder/hydration-manager.ts +46 -0
- package/src/query-builder/insert-query-state.ts +30 -0
- package/src/query-builder/insert.ts +46 -2
- package/src/query-builder/query-ast-service.ts +5 -0
- package/src/query-builder/query-resolution.ts +78 -0
- package/src/query-builder/raw-column-parser.ts +5 -0
- package/src/query-builder/relation-alias.ts +7 -0
- package/src/query-builder/relation-conditions.ts +61 -48
- package/src/query-builder/relation-service.ts +68 -63
- package/src/query-builder/relation-utils.ts +3 -0
- package/src/query-builder/select/cte-facet.ts +40 -0
- package/src/query-builder/select/from-facet.ts +80 -0
- package/src/query-builder/select/join-facet.ts +62 -0
- package/src/query-builder/select/predicate-facet.ts +103 -0
- package/src/query-builder/select/projection-facet.ts +69 -0
- package/src/query-builder/select/relation-facet.ts +81 -0
- package/src/query-builder/select/setop-facet.ts +36 -0
- package/src/query-builder/select-helpers.ts +13 -0
- package/src/query-builder/select-query-builder-deps.ts +19 -1
- package/src/query-builder/select-query-state.ts +2 -1
- package/src/query-builder/select.ts +795 -1163
- package/src/query-builder/update-query-state.ts +52 -0
- package/src/query-builder/update.ts +69 -19
- package/src/schema/table-guards.ts +31 -0
|
@@ -50,15 +50,49 @@ export interface OperandVisitor<R> {
|
|
|
50
50
|
type ExpressionDispatch = <R>(node: ExpressionNode, visitor: ExpressionVisitor<R>) => R;
|
|
51
51
|
type OperandDispatch = <R>(node: OperandNode, visitor: OperandVisitor<R>) => R;
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Registry class for managing dispatchers in an immutable way
|
|
55
|
+
*/
|
|
56
|
+
class DispatcherRegistry<T> {
|
|
57
|
+
private readonly dispatchers: ReadonlyMap<string, T>;
|
|
58
|
+
|
|
59
|
+
constructor(dispatchers: Map<string, T> = new Map()) {
|
|
60
|
+
this.dispatchers = dispatchers;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Registers a new dispatcher and returns a new registry instance
|
|
65
|
+
*/
|
|
66
|
+
register(type: string, dispatcher: T): DispatcherRegistry<T> {
|
|
67
|
+
const newMap = new Map(this.dispatchers);
|
|
68
|
+
newMap.set(type, dispatcher);
|
|
69
|
+
return new DispatcherRegistry(newMap);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Gets a dispatcher for the given type
|
|
74
|
+
*/
|
|
75
|
+
get(type: string): T | undefined {
|
|
76
|
+
return this.dispatchers.get(type);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Returns a new empty registry
|
|
81
|
+
*/
|
|
82
|
+
clear(): DispatcherRegistry<T> {
|
|
83
|
+
return new DispatcherRegistry();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let expressionRegistry = new DispatcherRegistry<ExpressionDispatch>();
|
|
88
|
+
let operandRegistry = new DispatcherRegistry<OperandDispatch>();
|
|
55
89
|
|
|
56
90
|
/**
|
|
57
91
|
* Registers a dispatcher for a custom expression node type.
|
|
58
92
|
* Allows new node kinds without modifying the core switch.
|
|
59
93
|
*/
|
|
60
94
|
export const registerExpressionDispatcher = (type: string, dispatcher: ExpressionDispatch): void => {
|
|
61
|
-
|
|
95
|
+
expressionRegistry = expressionRegistry.register(type, dispatcher);
|
|
62
96
|
};
|
|
63
97
|
|
|
64
98
|
/**
|
|
@@ -66,14 +100,19 @@ export const registerExpressionDispatcher = (type: string, dispatcher: Expressio
|
|
|
66
100
|
* Allows new node kinds without modifying the core switch.
|
|
67
101
|
*/
|
|
68
102
|
export const registerOperandDispatcher = (type: string, dispatcher: OperandDispatch): void => {
|
|
69
|
-
|
|
103
|
+
operandRegistry = operandRegistry.register(type, dispatcher);
|
|
70
104
|
};
|
|
71
105
|
|
|
72
106
|
/**
|
|
73
107
|
* Clears all registered dispatchers. Primarily for tests.
|
|
74
108
|
*/
|
|
75
|
-
export const clearExpressionDispatchers = (): void =>
|
|
76
|
-
|
|
109
|
+
export const clearExpressionDispatchers = (): void => {
|
|
110
|
+
expressionRegistry = expressionRegistry.clear();
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const clearOperandDispatchers = (): void => {
|
|
114
|
+
operandRegistry = operandRegistry.clear();
|
|
115
|
+
};
|
|
77
116
|
|
|
78
117
|
const getNodeType = (node: { type?: string } | null | undefined): string | undefined =>
|
|
79
118
|
typeof node === 'object' && node !== null && typeof node.type === 'string' ? node.type : undefined;
|
|
@@ -91,7 +130,7 @@ const unsupportedOperand = (node: OperandNode): never => {
|
|
|
91
130
|
* @param visitor - Visitor implementation
|
|
92
131
|
*/
|
|
93
132
|
export const visitExpression = <R>(node: ExpressionNode, visitor: ExpressionVisitor<R>): R => {
|
|
94
|
-
const dynamic =
|
|
133
|
+
const dynamic = expressionRegistry.get(node.type);
|
|
95
134
|
if (dynamic) return dynamic(node, visitor);
|
|
96
135
|
|
|
97
136
|
switch (node.type) {
|
|
@@ -129,7 +168,7 @@ export const visitExpression = <R>(node: ExpressionNode, visitor: ExpressionVisi
|
|
|
129
168
|
* @param visitor - Visitor implementation
|
|
130
169
|
*/
|
|
131
170
|
export const visitOperand = <R>(node: OperandNode, visitor: OperandVisitor<R>): R => {
|
|
132
|
-
const dynamic =
|
|
171
|
+
const dynamic = operandRegistry.get(node.type);
|
|
133
172
|
if (dynamic) return dynamic(node, visitor);
|
|
134
173
|
|
|
135
174
|
switch (node.type) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ColumnNode, LiteralNode } from './expression.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a column node for use in expressions
|
|
5
|
+
* @param table - Table name
|
|
6
|
+
* @param name - Column name
|
|
7
|
+
* @returns ColumnNode with the specified table and name
|
|
8
|
+
*/
|
|
9
|
+
export const createColumn = (table: string, name: string): ColumnNode => ({
|
|
10
|
+
type: 'Column',
|
|
11
|
+
table,
|
|
12
|
+
name
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates a literal value node for use in expressions
|
|
17
|
+
* @param val - Literal value (string or number)
|
|
18
|
+
* @returns LiteralNode with the specified value
|
|
19
|
+
*/
|
|
20
|
+
export const createLiteral = (val: string | number): LiteralNode => ({
|
|
21
|
+
type: 'Literal',
|
|
22
|
+
value: val
|
|
23
|
+
});
|
|
@@ -17,8 +17,24 @@ export const createJoinNode = (
|
|
|
17
17
|
type: 'Join',
|
|
18
18
|
kind,
|
|
19
19
|
table: typeof tableName === 'string'
|
|
20
|
-
? (
|
|
20
|
+
? (parseQualifiedTableRef(tableName) as TableNode)
|
|
21
21
|
: (tableName as TableSourceNode),
|
|
22
22
|
condition,
|
|
23
23
|
meta: relationName ? ({ relationName } as JoinMetadata) : undefined
|
|
24
24
|
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Parses a simple qualified reference like `schema.table` into a structured TableNode.
|
|
28
|
+
*
|
|
29
|
+
* Notes:
|
|
30
|
+
* - We intentionally only support a single dot here.
|
|
31
|
+
* - For multi-part qualification (server/db/schema/table), callers should pass a TableNode.
|
|
32
|
+
*/
|
|
33
|
+
const parseQualifiedTableRef = (ref: string): TableNode => {
|
|
34
|
+
const parts = ref.split('.');
|
|
35
|
+
if (parts.length === 2) {
|
|
36
|
+
const [schema, name] = parts;
|
|
37
|
+
return { type: 'Table', schema, name };
|
|
38
|
+
}
|
|
39
|
+
return { type: 'Table', name: ref };
|
|
40
|
+
};
|
|
@@ -4,6 +4,7 @@ import { ColumnDef, ForeignKeyReference } from '../../../schema/column.js';
|
|
|
4
4
|
import { IndexDef, TableDef } from '../../../schema/table.js';
|
|
5
5
|
import { DatabaseTable, DatabaseColumn, ColumnDiff } from '../schema-types.js';
|
|
6
6
|
|
|
7
|
+
/** A table-like object with name and optional schema. */
|
|
7
8
|
type TableLike = { name: string; schema?: string };
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -11,10 +12,15 @@ type TableLike = { name: string; schema?: string };
|
|
|
11
12
|
* Concrete dialects only override the small surface area instead of reimplementing everything.
|
|
12
13
|
*/
|
|
13
14
|
export abstract class BaseSchemaDialect implements SchemaDialect {
|
|
15
|
+
/** The name of the dialect. */
|
|
14
16
|
abstract readonly name: DialectName;
|
|
17
|
+
/** Quotes an identifier for use in SQL. */
|
|
15
18
|
abstract quoteIdentifier(id: string): string;
|
|
19
|
+
/** Renders the column type for SQL. */
|
|
16
20
|
abstract renderColumnType(column: ColumnDef): string;
|
|
21
|
+
/** Renders the auto-increment clause for SQL. */
|
|
17
22
|
abstract renderAutoIncrement(column: ColumnDef, table: TableDef): string | undefined;
|
|
23
|
+
/** Renders an index for SQL. */
|
|
18
24
|
abstract renderIndex(table: TableDef, index: IndexDef): string;
|
|
19
25
|
supportsPartialIndexes(): boolean {
|
|
20
26
|
return false;
|
|
@@ -25,7 +31,7 @@ export abstract class BaseSchemaDialect implements SchemaDialect {
|
|
|
25
31
|
}
|
|
26
32
|
return this.quoteIdentifier(table.name);
|
|
27
33
|
}
|
|
28
|
-
|
|
34
|
+
/** The literal formatter for the dialect. */
|
|
29
35
|
abstract get literalFormatter(): LiteralFormatter;
|
|
30
36
|
|
|
31
37
|
renderDefault(value: unknown, _column: ColumnDef): string {
|
|
@@ -6,6 +6,7 @@ import { IndexDef, TableDef } from '../../../schema/table.js';
|
|
|
6
6
|
import { ColumnDiff, DatabaseColumn, DatabaseTable } from '../schema-types.js';
|
|
7
7
|
import { DialectName } from '../schema-dialect.js';
|
|
8
8
|
|
|
9
|
+
/** MSSQL schema dialect implementation. */
|
|
9
10
|
export class MSSqlSchemaDialect extends BaseSchemaDialect {
|
|
10
11
|
name: DialectName = 'mssql';
|
|
11
12
|
|
|
@@ -7,6 +7,7 @@ import { ColumnDiff, DatabaseColumn, DatabaseTable } from '../schema-types.js';
|
|
|
7
7
|
import { renderColumnDefinition } from '../schema-generator.js';
|
|
8
8
|
import { DialectName } from '../schema-dialect.js';
|
|
9
9
|
|
|
10
|
+
/** MySQL schema dialect implementation. */
|
|
10
11
|
export class MySqlSchemaDialect extends BaseSchemaDialect {
|
|
11
12
|
name: DialectName = 'mysql';
|
|
12
13
|
|
|
@@ -6,6 +6,7 @@ import { IndexDef, TableDef } from '../../../schema/table.js';
|
|
|
6
6
|
import { ColumnDiff, DatabaseColumn, DatabaseTable } from '../schema-types.js';
|
|
7
7
|
import { DialectName } from '../schema-dialect.js';
|
|
8
8
|
|
|
9
|
+
/** PostgreSQL schema dialect implementation. */
|
|
9
10
|
export class PostgresSchemaDialect extends BaseSchemaDialect {
|
|
10
11
|
readonly name: DialectName = 'postgres';
|
|
11
12
|
|
|
@@ -6,6 +6,7 @@ import { IndexDef, TableDef } from '../../../schema/table.js';
|
|
|
6
6
|
import { ColumnDiff, DatabaseColumn, DatabaseTable } from '../schema-types.js';
|
|
7
7
|
import { DialectName } from '../schema-dialect.js';
|
|
8
8
|
|
|
9
|
+
/** SQLite schema dialect implementation. */
|
|
9
10
|
export class SQLiteSchemaDialect extends BaseSchemaDialect {
|
|
10
11
|
name: DialectName = 'sqlite';
|
|
11
12
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineTable } from '../../../../schema/table.js';
|
|
2
2
|
import { col } from '../../../../schema/column.js';
|
|
3
3
|
|
|
4
|
+
/** Table definition for information_schema.columns, providing metadata about table columns. */
|
|
4
5
|
export const PgInformationSchemaColumns = defineTable(
|
|
5
6
|
'columns',
|
|
6
7
|
{
|
|
@@ -134,6 +135,7 @@ export const PgReferentialConstraints = defineTable(
|
|
|
134
135
|
{ schema: 'information_schema' }
|
|
135
136
|
);
|
|
136
137
|
|
|
138
|
+
/** Default export containing commonly used PostgreSQL catalog table definitions. */
|
|
137
139
|
export default {
|
|
138
140
|
PgInformationSchemaColumns,
|
|
139
141
|
PgClass,
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import type { Dialect } from '../../dialect/abstract.js';
|
|
2
2
|
import type { DbExecutor } from '../../execution/db-executor.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Context for schema introspection operations.
|
|
6
|
+
* Provides the necessary components to perform database schema introspection.
|
|
7
|
+
*/
|
|
4
8
|
export interface IntrospectContext {
|
|
9
|
+
/** The database dialect used for introspection. */
|
|
5
10
|
dialect: Dialect;
|
|
11
|
+
/** The database executor for running introspection queries. */
|
|
6
12
|
executor: DbExecutor;
|
|
7
13
|
}
|
|
8
14
|
|
|
@@ -13,10 +13,23 @@ const fn = (name: string, args: OperandInput[]): FunctionNode => ({
|
|
|
13
13
|
args: args.map(toOperand)
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Creates a pg_get_expr function call AST node.
|
|
18
|
+
* @param expr - The expression.
|
|
19
|
+
* @param relid - The relation ID.
|
|
20
|
+
* @returns The function node.
|
|
21
|
+
*/
|
|
16
22
|
export const pgGetExpr = (expr: OperandInput, relid: OperandInput): FunctionNode =>
|
|
17
23
|
fn('pg_get_expr', [expr, relid]);
|
|
18
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Creates a format_type function call AST node.
|
|
27
|
+
* @param typeOid - The type OID.
|
|
28
|
+
* @param typmod - The type modifier.
|
|
29
|
+
* @returns The function node.
|
|
30
|
+
*/
|
|
19
31
|
export const formatType = (typeOid: OperandInput, typmod: OperandInput): FunctionNode =>
|
|
20
32
|
fn('format_type', [typeOid, typmod]);
|
|
21
33
|
|
|
34
|
+
/** Default export with PostgreSQL-specific function helpers. */
|
|
22
35
|
export default { pgGetExpr, formatType };
|
|
@@ -3,6 +3,7 @@ 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
|
+
/** Row type for MSSQL column information. */
|
|
6
7
|
type MssqlColumnRow = {
|
|
7
8
|
table_schema: string;
|
|
8
9
|
table_name: string;
|
|
@@ -13,6 +14,7 @@ type MssqlColumnRow = {
|
|
|
13
14
|
column_default: string | null;
|
|
14
15
|
};
|
|
15
16
|
|
|
17
|
+
/** Row type for MSSQL primary key information. */
|
|
16
18
|
type MssqlPrimaryKeyRow = {
|
|
17
19
|
table_schema: string;
|
|
18
20
|
table_name: string;
|
|
@@ -20,6 +22,7 @@ type MssqlPrimaryKeyRow = {
|
|
|
20
22
|
key_ordinal: number;
|
|
21
23
|
};
|
|
22
24
|
|
|
25
|
+
/** Row type for MSSQL index information. */
|
|
23
26
|
type MssqlIndexRow = {
|
|
24
27
|
table_schema: string;
|
|
25
28
|
table_name: string;
|
|
@@ -29,6 +32,7 @@ type MssqlIndexRow = {
|
|
|
29
32
|
filter_definition: string | null;
|
|
30
33
|
};
|
|
31
34
|
|
|
35
|
+
/** Row type for MSSQL index column information. */
|
|
32
36
|
type MssqlIndexColumnRow = {
|
|
33
37
|
table_schema: string;
|
|
34
38
|
table_name: string;
|
|
@@ -37,7 +41,14 @@ type MssqlIndexColumnRow = {
|
|
|
37
41
|
key_ordinal: number;
|
|
38
42
|
};
|
|
39
43
|
|
|
44
|
+
/** MSSQL schema introspector implementation. */
|
|
40
45
|
export const mssqlIntrospector: SchemaIntrospector = {
|
|
46
|
+
/**
|
|
47
|
+
* Introspects the MSSQL database schema.
|
|
48
|
+
* @param ctx - The introspection context containing the database executor.
|
|
49
|
+
* @param options - Options for introspection, such as schema filter.
|
|
50
|
+
* @returns A promise that resolves to the introspected database schema.
|
|
51
|
+
*/
|
|
41
52
|
async introspect(ctx: { executor: DbExecutor }, options: IntrospectOptions): Promise<DatabaseSchema> {
|
|
42
53
|
const schema = options.schema;
|
|
43
54
|
const filterSchema = schema ? 'sch.name = @p1' : '1=1';
|
|
@@ -3,6 +3,7 @@ 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
|
+
/** Row type for MySQL column information. */
|
|
6
7
|
type MysqlColumnRow = {
|
|
7
8
|
table_schema: string;
|
|
8
9
|
table_name: string;
|
|
@@ -27,6 +28,7 @@ type MysqlIndexRow = {
|
|
|
27
28
|
cols: string | null;
|
|
28
29
|
};
|
|
29
30
|
|
|
31
|
+
/** MySQL schema introspector. */
|
|
30
32
|
export const mysqlIntrospector: SchemaIntrospector = {
|
|
31
33
|
async introspect(ctx: { executor: DbExecutor }, options: IntrospectOptions): Promise<DatabaseSchema> {
|
|
32
34
|
const schema = options.schema;
|
|
@@ -13,6 +13,7 @@ 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
|
+
/** Row type for PostgreSQL column introspection from information_schema.columns. */
|
|
16
17
|
type ColumnIntrospectRow = {
|
|
17
18
|
table_schema: string;
|
|
18
19
|
table_name: string;
|
|
@@ -23,6 +24,7 @@ type ColumnIntrospectRow = {
|
|
|
23
24
|
ordinal_position: number | null;
|
|
24
25
|
};
|
|
25
26
|
|
|
27
|
+
/** Row type for PostgreSQL primary key introspection from key_column_usage and table_constraints. */
|
|
26
28
|
type PrimaryKeyIntrospectRow = {
|
|
27
29
|
table_schema: string;
|
|
28
30
|
table_name: string;
|
|
@@ -31,6 +33,7 @@ type PrimaryKeyIntrospectRow = {
|
|
|
31
33
|
constraint_name: string;
|
|
32
34
|
};
|
|
33
35
|
|
|
36
|
+
/** Row type for PostgreSQL foreign key introspection from various constraint tables. */
|
|
34
37
|
type ForeignKeyIntrospectRow = {
|
|
35
38
|
table_schema: string;
|
|
36
39
|
table_name: string;
|
|
@@ -41,6 +44,7 @@ type ForeignKeyIntrospectRow = {
|
|
|
41
44
|
foreign_column_name: string;
|
|
42
45
|
};
|
|
43
46
|
|
|
47
|
+
/** Represents a foreign key reference entry with optional referential actions. */
|
|
44
48
|
type ForeignKeyEntry = {
|
|
45
49
|
table: string;
|
|
46
50
|
column: string;
|
|
@@ -48,6 +52,7 @@ type ForeignKeyEntry = {
|
|
|
48
52
|
onUpdate?: ReferentialAction;
|
|
49
53
|
};
|
|
50
54
|
|
|
55
|
+
/** Row type for PostgreSQL index query results from pg_catalog tables. */
|
|
51
56
|
type IndexQueryRow = {
|
|
52
57
|
table_schema: string;
|
|
53
58
|
table_name: string;
|
|
@@ -58,6 +63,7 @@ type IndexQueryRow = {
|
|
|
58
63
|
ord: number | null;
|
|
59
64
|
};
|
|
60
65
|
|
|
66
|
+
/** Grouped index information used for aggregating index columns. */
|
|
61
67
|
type IndexGroup = {
|
|
62
68
|
table_schema: string;
|
|
63
69
|
table_name: string;
|
|
@@ -67,7 +73,15 @@ type IndexGroup = {
|
|
|
67
73
|
cols: { ord: number; att: string | null }[];
|
|
68
74
|
};
|
|
69
75
|
|
|
76
|
+
/** PostgreSQL schema introspector. */
|
|
70
77
|
export const postgresIntrospector: SchemaIntrospector = {
|
|
78
|
+
/**
|
|
79
|
+
* Introspects the PostgreSQL database schema by querying information_schema and pg_catalog.
|
|
80
|
+
* Builds tables with columns, primary keys, foreign keys, and indexes.
|
|
81
|
+
* @param ctx - The introspection context with database executor.
|
|
82
|
+
* @param options - Options for schema selection and table filtering.
|
|
83
|
+
* @returns A promise resolving to the complete database schema.
|
|
84
|
+
*/
|
|
71
85
|
async introspect(ctx: IntrospectContext, options: IntrospectOptions): Promise<DatabaseSchema> {
|
|
72
86
|
const schema = options.schema || 'public';
|
|
73
87
|
const tables: DatabaseTable[] = [];
|
|
@@ -5,8 +5,12 @@ import { mysqlIntrospector } from './mysql.js';
|
|
|
5
5
|
import { sqliteIntrospector } from './sqlite.js';
|
|
6
6
|
import { mssqlIntrospector } from './mssql.js';
|
|
7
7
|
|
|
8
|
+
/** Registry mapping dialect names to their corresponding schema introspectors. */
|
|
8
9
|
const registry = new Map<DialectName, SchemaIntrospector>();
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Registers the built-in schema introspectors for all supported database dialects.
|
|
13
|
+
*/
|
|
10
14
|
const registerBuiltInIntrospectors = () => {
|
|
11
15
|
registry.set('postgres', postgresIntrospector);
|
|
12
16
|
registry.set('mysql', mysqlIntrospector);
|
|
@@ -16,10 +20,20 @@ const registerBuiltInIntrospectors = () => {
|
|
|
16
20
|
|
|
17
21
|
registerBuiltInIntrospectors();
|
|
18
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Registers a schema introspector for a dialect.
|
|
25
|
+
* @param dialect - The dialect name.
|
|
26
|
+
* @param introspector - The schema introspector.
|
|
27
|
+
*/
|
|
19
28
|
export const registerSchemaIntrospector = (dialect: DialectName, introspector: SchemaIntrospector): void => {
|
|
20
29
|
registry.set(dialect, introspector);
|
|
21
30
|
};
|
|
22
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Gets the schema introspector for a dialect.
|
|
34
|
+
* @param dialect - The dialect name.
|
|
35
|
+
* @returns The schema introspector or undefined if not found.
|
|
36
|
+
*/
|
|
23
37
|
export const getSchemaIntrospector = (dialect: DialectName): SchemaIntrospector | undefined => {
|
|
24
38
|
return registry.get(dialect);
|
|
25
39
|
};
|
|
@@ -3,8 +3,15 @@ import type { SelectQueryNode } from '../../ast/query.js';
|
|
|
3
3
|
|
|
4
4
|
import { toRows } from './utils.js';
|
|
5
5
|
|
|
6
|
+
/** A source that can provide a select query AST. */
|
|
6
7
|
type SelectQuerySource = { getAST(): SelectQueryNode };
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Runs a select query from a query builder and returns the results.
|
|
11
|
+
* @param qb - The query builder.
|
|
12
|
+
* @param ctx - The introspection context.
|
|
13
|
+
* @returns The query results.
|
|
14
|
+
*/
|
|
8
15
|
export async function runSelect<T = Record<string, unknown>>(
|
|
9
16
|
qb: SelectQuerySource,
|
|
10
17
|
ctx: IntrospectContext
|
|
@@ -19,6 +26,12 @@ export async function runSelect<T = Record<string, unknown>>(
|
|
|
19
26
|
|
|
20
27
|
export default runSelect;
|
|
21
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Runs a select query from an AST node and returns the results.
|
|
31
|
+
* @param ast - The select query AST.
|
|
32
|
+
* @param ctx - The introspection context.
|
|
33
|
+
* @returns The query results.
|
|
34
|
+
*/
|
|
22
35
|
export async function runSelectNode<T = Record<string, unknown>>(ast: SelectQueryNode, ctx: IntrospectContext): Promise<T[]> {
|
|
23
36
|
const compiled = ctx.dialect.compileSelect(ast);
|
|
24
37
|
const results = await ctx.executor.executeSql(compiled.sql, compiled.params);
|
|
@@ -4,10 +4,12 @@ import { DatabaseSchema, DatabaseTable, DatabaseIndex } from '../schema-types.js
|
|
|
4
4
|
import { ReferentialAction } from '../../../schema/column.js';
|
|
5
5
|
import { DbExecutor } from '../../execution/db-executor.js';
|
|
6
6
|
|
|
7
|
+
/** Row type for SQLite table list from sqlite_master. */
|
|
7
8
|
type SqliteTableRow = {
|
|
8
9
|
name: string;
|
|
9
10
|
};
|
|
10
11
|
|
|
12
|
+
/** Row type for SQLite table column information from PRAGMA table_info. */
|
|
11
13
|
type SqliteTableInfoRow = {
|
|
12
14
|
name: string;
|
|
13
15
|
type: string;
|
|
@@ -16,6 +18,7 @@ type SqliteTableInfoRow = {
|
|
|
16
18
|
pk: number;
|
|
17
19
|
};
|
|
18
20
|
|
|
21
|
+
/** Row type for SQLite foreign key information from PRAGMA foreign_key_list. */
|
|
19
22
|
type SqliteForeignKeyRow = {
|
|
20
23
|
table: string;
|
|
21
24
|
from: string;
|
|
@@ -24,15 +27,22 @@ type SqliteForeignKeyRow = {
|
|
|
24
27
|
on_update: string | null;
|
|
25
28
|
};
|
|
26
29
|
|
|
30
|
+
/** Row type for SQLite index list from PRAGMA index_list. */
|
|
27
31
|
type SqliteIndexListRow = {
|
|
28
32
|
name: string;
|
|
29
33
|
unique: number;
|
|
30
34
|
};
|
|
31
35
|
|
|
36
|
+
/** Row type for SQLite index column information from PRAGMA index_info. */
|
|
32
37
|
type SqliteIndexInfoRow = {
|
|
33
38
|
name: string;
|
|
34
39
|
};
|
|
35
40
|
|
|
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
|
+
*/
|
|
36
46
|
const toReferentialAction = (value: string | null | undefined): ReferentialAction | undefined => {
|
|
37
47
|
if (!value) return undefined;
|
|
38
48
|
const normalized = value.toUpperCase();
|
|
@@ -48,9 +58,21 @@ const toReferentialAction = (value: string | null | undefined): ReferentialActio
|
|
|
48
58
|
return undefined;
|
|
49
59
|
};
|
|
50
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Escapes single quotes in a string for safe inclusion in SQL queries.
|
|
63
|
+
* @param name - The string to escape.
|
|
64
|
+
* @returns The escaped string with single quotes doubled.
|
|
65
|
+
*/
|
|
51
66
|
const escapeSingleQuotes = (name: string) => name.replace(/'/g, "''");
|
|
52
67
|
|
|
68
|
+
/** SQLite schema introspector. */
|
|
53
69
|
export const sqliteIntrospector: SchemaIntrospector = {
|
|
70
|
+
/**
|
|
71
|
+
* Introspects the SQLite database schema by querying sqlite_master and various PRAGMAs.
|
|
72
|
+
* @param ctx - The database execution context containing the DbExecutor.
|
|
73
|
+
* @param options - Options controlling which tables and schemas to include.
|
|
74
|
+
* @returns A promise that resolves to the introspected DatabaseSchema.
|
|
75
|
+
*/
|
|
54
76
|
async introspect(ctx: { executor: DbExecutor }, options: IntrospectOptions): Promise<DatabaseSchema> {
|
|
55
77
|
const tables: DatabaseTable[] = [];
|
|
56
78
|
const tableRows = (await queryRows(
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { DbExecutor, QueryResult } from '../../execution/db-executor.js';
|
|
2
2
|
import { IntrospectOptions } from './types.js';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Converts a query result to an array of row objects.
|
|
6
|
+
* @param result - The query result.
|
|
7
|
+
* @returns The array of rows.
|
|
8
|
+
*/
|
|
4
9
|
export const toRows = (result: QueryResult | undefined): Record<string, unknown>[] => {
|
|
5
10
|
if (!result) return [];
|
|
6
11
|
return result.values.map(row =>
|
|
@@ -11,6 +16,13 @@ export const toRows = (result: QueryResult | undefined): Record<string, unknown>
|
|
|
11
16
|
);
|
|
12
17
|
};
|
|
13
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Executes a SQL query and returns the rows.
|
|
21
|
+
* @param executor - The database executor.
|
|
22
|
+
* @param sql - The SQL query.
|
|
23
|
+
* @param params - The query parameters.
|
|
24
|
+
* @returns The array of rows.
|
|
25
|
+
*/
|
|
14
26
|
export const queryRows = async (
|
|
15
27
|
executor: DbExecutor,
|
|
16
28
|
sql: string,
|
|
@@ -20,6 +32,12 @@ export const queryRows = async (
|
|
|
20
32
|
return toRows(first);
|
|
21
33
|
};
|
|
22
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Checks if a table should be included in introspection based on options.
|
|
37
|
+
* @param name - The table name.
|
|
38
|
+
* @param options - The introspection options.
|
|
39
|
+
* @returns True if the table should be included.
|
|
40
|
+
*/
|
|
23
41
|
export const shouldIncludeTable = (name: string, options: IntrospectOptions): boolean => {
|
|
24
42
|
if (options.includeTables && !options.includeTables.includes(name)) return false;
|
|
25
43
|
if (options.excludeTables && options.excludeTables.includes(name)) return false;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { TableDef, IndexDef } from '../../schema/table.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Derives the name for an index based on the table and index definition.
|
|
5
|
+
* @param table - The table definition.
|
|
6
|
+
* @param index - The index definition.
|
|
7
|
+
* @returns The derived index name.
|
|
8
|
+
*/
|
|
3
9
|
export const deriveIndexName = (table: TableDef, index: IndexDef): string => {
|
|
4
10
|
const base = (index.columns ?? [])
|
|
5
11
|
.map(col => (typeof col === 'string' ? col : col.column))
|
|
@@ -2,6 +2,7 @@ import type { TableDef, IndexDef } from '../../schema/table.js';
|
|
|
2
2
|
import type { ColumnDef, ForeignKeyReference } from '../../schema/column.js';
|
|
3
3
|
import type { DatabaseTable, DatabaseColumn, ColumnDiff } from './schema-types.js';
|
|
4
4
|
|
|
5
|
+
/** The name of a database dialect. */
|
|
5
6
|
export type DialectName =
|
|
6
7
|
| 'postgres'
|
|
7
8
|
| 'mysql'
|
|
@@ -9,34 +10,46 @@ export type DialectName =
|
|
|
9
10
|
| 'mssql'
|
|
10
11
|
| (string & {});
|
|
11
12
|
|
|
13
|
+
/** Interface for schema dialect implementations that handle database-specific DDL operations. */
|
|
12
14
|
export interface SchemaDialect {
|
|
15
|
+
/** The name of the dialect. */
|
|
13
16
|
readonly name: DialectName;
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
/** Quotes an identifier for use in SQL. */
|
|
16
19
|
quoteIdentifier(id: string): string;
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
/** Formats the table name for SQL. */
|
|
19
22
|
formatTableName(table: TableDef | DatabaseTable): string;
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
/** Renders the column type for SQL. */
|
|
22
25
|
renderColumnType(column: ColumnDef): string;
|
|
26
|
+
/** Renders the default value for SQL. */
|
|
23
27
|
renderDefault(value: unknown, column: ColumnDef): string;
|
|
28
|
+
/** Renders the auto-increment clause for SQL. */
|
|
24
29
|
renderAutoIncrement(column: ColumnDef, table: TableDef): string | undefined;
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
/** Renders a foreign key reference for SQL. */
|
|
27
32
|
renderReference(ref: ForeignKeyReference, table: TableDef): string;
|
|
33
|
+
/** Renders an index for SQL. */
|
|
28
34
|
renderIndex(table: TableDef, index: IndexDef): string;
|
|
35
|
+
/** Renders table options for SQL. */
|
|
29
36
|
renderTableOptions(table: TableDef): string | undefined;
|
|
30
37
|
|
|
31
|
-
|
|
38
|
+
/** Checks if the dialect supports partial indexes. */
|
|
32
39
|
supportsPartialIndexes(): boolean;
|
|
40
|
+
/** Checks if the dialect prefers inline primary key auto-increment. */
|
|
33
41
|
preferInlinePkAutoincrement(column: ColumnDef, table: TableDef, pk: string[]): boolean;
|
|
34
42
|
|
|
35
|
-
|
|
43
|
+
/** Generates SQL to drop a column. */
|
|
36
44
|
dropColumnSql?(table: DatabaseTable, column: string): string[];
|
|
45
|
+
/** Generates SQL to drop an index. */
|
|
37
46
|
dropIndexSql?(table: DatabaseTable, index: string): string[];
|
|
47
|
+
/** Generates SQL to drop a table. */
|
|
38
48
|
dropTableSql?(table: DatabaseTable): string[];
|
|
49
|
+
/** Returns a warning message for dropping a column. */
|
|
39
50
|
warnDropColumn?(table: DatabaseTable, column: string): string | undefined;
|
|
51
|
+
/** Generates SQL to alter a column. */
|
|
40
52
|
alterColumnSql?(table: TableDef, column: ColumnDef, actualColumn: DatabaseColumn, diff: ColumnDiff): string[];
|
|
53
|
+
/** Returns a warning message for altering a column. */
|
|
41
54
|
warnAlterColumn?(table: TableDef, column: ColumnDef, actualColumn: DatabaseColumn, diff: ColumnDiff): string | undefined;
|
|
42
55
|
}
|