metal-orm 1.0.14 → 1.0.15
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 +40 -45
- package/dist/decorators/index.cjs +1600 -27
- package/dist/decorators/index.cjs.map +1 -1
- package/dist/decorators/index.d.cts +6 -2
- package/dist/decorators/index.d.ts +6 -2
- package/dist/decorators/index.js +1599 -27
- package/dist/decorators/index.js.map +1 -1
- package/dist/index.cjs +4608 -3429
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +511 -159
- package/dist/index.d.ts +511 -159
- package/dist/index.js +4526 -3415
- package/dist/index.js.map +1 -1
- package/dist/{select-CCp1oz9p.d.cts → select-Bkv8g8u_.d.cts} +193 -67
- package/dist/{select-CCp1oz9p.d.ts → select-Bkv8g8u_.d.ts} +193 -67
- package/package.json +1 -1
- package/src/codegen/typescript.ts +38 -35
- package/src/core/ast/adapters.ts +21 -0
- package/src/core/ast/aggregate-functions.ts +13 -13
- package/src/core/ast/builders.ts +56 -43
- package/src/core/ast/expression-builders.ts +34 -34
- package/src/core/ast/expression-nodes.ts +18 -16
- package/src/core/ast/expression-visitor.ts +122 -69
- package/src/core/ast/expression.ts +6 -4
- package/src/core/ast/join-metadata.ts +15 -0
- package/src/core/ast/join-node.ts +22 -20
- package/src/core/ast/join.ts +5 -5
- package/src/core/ast/query.ts +52 -88
- package/src/core/ast/types.ts +20 -0
- package/src/core/ast/window-functions.ts +55 -55
- package/src/core/ddl/dialects/base-schema-dialect.ts +20 -6
- package/src/core/ddl/dialects/mssql-schema-dialect.ts +32 -8
- package/src/core/ddl/dialects/mysql-schema-dialect.ts +21 -10
- package/src/core/ddl/dialects/postgres-schema-dialect.ts +52 -7
- package/src/core/ddl/dialects/sqlite-schema-dialect.ts +23 -9
- package/src/core/ddl/introspect/catalogs/index.ts +1 -0
- package/src/core/ddl/introspect/catalogs/postgres.ts +143 -0
- package/src/core/ddl/introspect/context.ts +9 -0
- package/src/core/ddl/introspect/functions/postgres.ts +26 -0
- package/src/core/ddl/introspect/mssql.ts +149 -149
- package/src/core/ddl/introspect/mysql.ts +99 -99
- package/src/core/ddl/introspect/postgres.ts +245 -154
- package/src/core/ddl/introspect/registry.ts +26 -0
- package/src/core/ddl/introspect/run-select.ts +25 -0
- package/src/core/ddl/introspect/sqlite.ts +7 -7
- package/src/core/ddl/introspect/types.ts +23 -19
- package/src/core/ddl/introspect/utils.ts +1 -1
- package/src/core/ddl/naming-strategy.ts +10 -0
- package/src/core/ddl/schema-dialect.ts +41 -0
- package/src/core/ddl/schema-diff.ts +211 -179
- package/src/core/ddl/schema-generator.ts +16 -90
- package/src/core/ddl/schema-introspect.ts +25 -32
- package/src/core/ddl/schema-plan-executor.ts +17 -0
- package/src/core/ddl/schema-types.ts +46 -39
- package/src/core/ddl/sql-writing.ts +170 -0
- package/src/core/dialect/abstract.ts +144 -126
- package/src/core/dialect/base/cte-compiler.ts +33 -0
- package/src/core/dialect/base/function-table-formatter.ts +132 -0
- package/src/core/dialect/base/groupby-compiler.ts +21 -0
- package/src/core/dialect/base/join-compiler.ts +26 -0
- package/src/core/dialect/base/orderby-compiler.ts +21 -0
- package/src/core/dialect/base/pagination-strategy.ts +32 -0
- package/src/core/dialect/base/returning-strategy.ts +56 -0
- package/src/core/dialect/base/sql-dialect.ts +181 -204
- package/src/core/dialect/dialect-factory.ts +91 -0
- package/src/core/dialect/mssql/functions.ts +101 -0
- package/src/core/dialect/mssql/index.ts +128 -126
- package/src/core/dialect/mysql/functions.ts +101 -0
- package/src/core/dialect/mysql/index.ts +20 -18
- package/src/core/dialect/postgres/functions.ts +95 -0
- package/src/core/dialect/postgres/index.ts +30 -28
- package/src/core/dialect/sqlite/functions.ts +115 -0
- package/src/core/dialect/sqlite/index.ts +30 -28
- package/src/core/driver/database-driver.ts +11 -0
- package/src/core/driver/mssql-driver.ts +20 -0
- package/src/core/driver/mysql-driver.ts +20 -0
- package/src/core/driver/postgres-driver.ts +20 -0
- package/src/core/driver/sqlite-driver.ts +20 -0
- package/src/core/execution/db-executor.ts +63 -0
- package/src/core/execution/executors/mssql-executor.ts +39 -0
- package/src/core/execution/executors/mysql-executor.ts +47 -0
- package/src/core/execution/executors/postgres-executor.ts +32 -0
- package/src/core/execution/executors/sqlite-executor.ts +31 -0
- package/src/core/functions/datetime.ts +132 -0
- package/src/core/functions/numeric.ts +179 -0
- package/src/core/functions/standard-strategy.ts +47 -0
- package/src/core/functions/text.ts +147 -0
- package/src/core/functions/types.ts +18 -0
- package/src/core/hydration/types.ts +57 -0
- package/src/decorators/bootstrap.ts +10 -0
- package/src/decorators/relations.ts +15 -0
- package/src/index.ts +30 -19
- package/src/orm/entity-metadata.ts +7 -0
- package/src/orm/entity.ts +58 -27
- package/src/orm/hydration.ts +25 -17
- package/src/orm/lazy-batch.ts +46 -2
- package/src/orm/orm-context.ts +60 -60
- package/src/orm/query-logger.ts +1 -1
- package/src/orm/relation-change-processor.ts +43 -2
- package/src/orm/relations/has-one.ts +139 -0
- package/src/orm/transaction-runner.ts +1 -1
- package/src/orm/unit-of-work.ts +60 -60
- package/src/query-builder/delete.ts +22 -5
- package/src/query-builder/hydration-manager.ts +2 -1
- package/src/query-builder/hydration-planner.ts +8 -7
- package/src/query-builder/insert.ts +22 -5
- package/src/query-builder/relation-conditions.ts +9 -8
- package/src/query-builder/relation-service.ts +3 -2
- package/src/query-builder/select.ts +66 -61
- package/src/query-builder/update.ts +22 -5
- package/src/schema/column.ts +246 -246
- package/src/schema/relation.ts +35 -1
- package/src/schema/table.ts +28 -28
- package/src/schema/types.ts +41 -31
- package/src/orm/db-executor.ts +0 -11
package/src/core/ast/join.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TableNode } from './query.js';
|
|
1
|
+
import { TableNode, FunctionTableNode } from './query.js';
|
|
2
2
|
import { ExpressionNode } from './expression.js';
|
|
3
3
|
import { JoinKind } from '../sql/sql.js';
|
|
4
4
|
|
|
@@ -10,9 +10,9 @@ export interface JoinNode {
|
|
|
10
10
|
/** Type of join (INNER, LEFT, RIGHT, etc.) */
|
|
11
11
|
kind: JoinKind;
|
|
12
12
|
/** Table to join */
|
|
13
|
-
table: TableNode;
|
|
14
|
-
|
|
13
|
+
table: TableNode | FunctionTableNode;
|
|
14
|
+
/** Join condition expression */
|
|
15
15
|
condition: ExpressionNode;
|
|
16
|
-
/** Optional
|
|
17
|
-
|
|
16
|
+
/** Optional metadata for non-SQL concerns (e.g., relation name) */
|
|
17
|
+
meta?: Record<string, unknown>;
|
|
18
18
|
}
|
package/src/core/ast/query.ts
CHANGED
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
OperandNode
|
|
9
9
|
} from './expression.js';
|
|
10
10
|
import { JoinNode } from './join.js';
|
|
11
|
-
import { RelationType } from '../../schema/relation.js';
|
|
12
11
|
import { OrderDirection } from '../sql/sql.js';
|
|
13
12
|
|
|
14
13
|
/**
|
|
@@ -24,6 +23,27 @@ export interface TableNode {
|
|
|
24
23
|
alias?: string;
|
|
25
24
|
}
|
|
26
25
|
|
|
26
|
+
/**
|
|
27
|
+
* AST node representing a function used as a table source (table-valued function)
|
|
28
|
+
*/
|
|
29
|
+
export interface FunctionTableNode {
|
|
30
|
+
type: 'FunctionTable';
|
|
31
|
+
/** Function name */
|
|
32
|
+
name: string;
|
|
33
|
+
/** Optional schema for the function (some dialects) */
|
|
34
|
+
schema?: string;
|
|
35
|
+
/** Function arguments as operand nodes */
|
|
36
|
+
args?: any[]; // use any to avoid circular import here; caller should supply OperandNode
|
|
37
|
+
/** Optional alias for the function table */
|
|
38
|
+
alias?: string;
|
|
39
|
+
/** LATERAL flag */
|
|
40
|
+
lateral?: boolean;
|
|
41
|
+
/** WITH ORDINALITY flag */
|
|
42
|
+
withOrdinality?: boolean;
|
|
43
|
+
/** Optional column aliases */
|
|
44
|
+
columnAliases?: string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
27
47
|
/**
|
|
28
48
|
* AST node representing an ORDER BY clause
|
|
29
49
|
*/
|
|
@@ -36,101 +56,45 @@ export interface OrderByNode {
|
|
|
36
56
|
}
|
|
37
57
|
|
|
38
58
|
/**
|
|
39
|
-
*
|
|
40
|
-
*/
|
|
41
|
-
export interface HydrationPivotPlan {
|
|
42
|
-
table: string;
|
|
43
|
-
primaryKey: string;
|
|
44
|
-
aliasPrefix: string;
|
|
45
|
-
columns: string[];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Plan for hydrating relationship data
|
|
59
|
+
* AST node representing a Common Table Expression (CTE)
|
|
50
60
|
*/
|
|
51
|
-
export interface
|
|
52
|
-
|
|
61
|
+
export interface CommonTableExpressionNode {
|
|
62
|
+
type: 'CommonTableExpression';
|
|
63
|
+
/** CTE name */
|
|
53
64
|
name: string;
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
/** Target table primary key */
|
|
61
|
-
targetPrimaryKey: string;
|
|
62
|
-
/** Foreign key column */
|
|
63
|
-
foreignKey: string;
|
|
64
|
-
/** Local key column */
|
|
65
|
-
localKey: string;
|
|
66
|
-
/** Columns to include */
|
|
67
|
-
columns: string[];
|
|
68
|
-
/** Optional pivot plan for many-to-many relationships */
|
|
69
|
-
pivot?: HydrationPivotPlan;
|
|
65
|
+
/** Optional column names */
|
|
66
|
+
columns?: string[];
|
|
67
|
+
/** CTE query */
|
|
68
|
+
query: SelectQueryNode;
|
|
69
|
+
/** Whether the CTE is recursive */
|
|
70
|
+
recursive: boolean;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
/**
|
|
73
|
-
*
|
|
74
|
+
* Supported set operation kinds for compound SELECT queries
|
|
74
75
|
*/
|
|
75
|
-
export
|
|
76
|
-
/** Root table name */
|
|
77
|
-
rootTable: string;
|
|
78
|
-
/** Root table primary key */
|
|
79
|
-
rootPrimaryKey: string;
|
|
80
|
-
/** Root table columns */
|
|
81
|
-
rootColumns: string[];
|
|
82
|
-
/** Relationship hydration plans */
|
|
83
|
-
relations: HydrationRelationPlan[];
|
|
84
|
-
}
|
|
76
|
+
export type SetOperationKind = 'UNION' | 'UNION ALL' | 'INTERSECT' | 'EXCEPT';
|
|
85
77
|
|
|
86
78
|
/**
|
|
87
|
-
*
|
|
79
|
+
* AST node representing a set operation (UNION, INTERSECT, etc.)
|
|
88
80
|
*/
|
|
89
|
-
export interface
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
export interface SetOperationNode {
|
|
82
|
+
type: 'SetOperation';
|
|
83
|
+
/** Operator to combine queries */
|
|
84
|
+
operator: SetOperationKind;
|
|
85
|
+
/** Right-hand query in the compound expression */
|
|
86
|
+
query: SelectQueryNode;
|
|
92
87
|
}
|
|
93
88
|
|
|
94
89
|
/**
|
|
95
|
-
* AST node representing a
|
|
90
|
+
* AST node representing a complete SELECT query
|
|
96
91
|
*/
|
|
97
|
-
export interface
|
|
98
|
-
type: '
|
|
99
|
-
/** CTE name */
|
|
100
|
-
name: string;
|
|
101
|
-
/** Optional column names */
|
|
102
|
-
columns?: string[];
|
|
103
|
-
/** CTE query */
|
|
104
|
-
query: SelectQueryNode;
|
|
105
|
-
/** Whether the CTE is recursive */
|
|
106
|
-
recursive: boolean;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Supported set operation kinds for compound SELECT queries
|
|
111
|
-
*/
|
|
112
|
-
export type SetOperationKind = 'UNION' | 'UNION ALL' | 'INTERSECT' | 'EXCEPT';
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* AST node representing a set operation (UNION, INTERSECT, etc.)
|
|
116
|
-
*/
|
|
117
|
-
export interface SetOperationNode {
|
|
118
|
-
type: 'SetOperation';
|
|
119
|
-
/** Operator to combine queries */
|
|
120
|
-
operator: SetOperationKind;
|
|
121
|
-
/** Right-hand query in the compound expression */
|
|
122
|
-
query: SelectQueryNode;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* AST node representing a complete SELECT query
|
|
127
|
-
*/
|
|
128
|
-
export interface SelectQueryNode {
|
|
129
|
-
type: 'SelectQuery';
|
|
92
|
+
export interface SelectQueryNode {
|
|
93
|
+
type: 'SelectQuery';
|
|
130
94
|
/** Optional CTEs (WITH clauses) */
|
|
131
95
|
ctes?: CommonTableExpressionNode[];
|
|
132
|
-
/** FROM clause table */
|
|
133
|
-
from: TableNode;
|
|
96
|
+
/** FROM clause table (either a Table or a FunctionTable) */
|
|
97
|
+
from: TableNode | FunctionTableNode;
|
|
134
98
|
/** SELECT clause columns */
|
|
135
99
|
columns: (ColumnNode | FunctionNode | ScalarSubqueryNode | CaseExpressionNode | WindowFunctionNode)[];
|
|
136
100
|
/** JOIN clauses */
|
|
@@ -147,13 +111,13 @@ export interface SelectQueryNode {
|
|
|
147
111
|
limit?: number;
|
|
148
112
|
/** Optional OFFSET clause */
|
|
149
113
|
offset?: number;
|
|
150
|
-
/** Optional query metadata */
|
|
151
|
-
meta?:
|
|
152
|
-
/** Optional DISTINCT clause */
|
|
153
|
-
distinct?: ColumnNode[];
|
|
154
|
-
/** Optional set operations chaining this query with others */
|
|
155
|
-
setOps?: SetOperationNode[];
|
|
156
|
-
}
|
|
114
|
+
/** Optional query metadata */
|
|
115
|
+
meta?: Record<string, unknown>;
|
|
116
|
+
/** Optional DISTINCT clause */
|
|
117
|
+
distinct?: ColumnNode[];
|
|
118
|
+
/** Optional set operations chaining this query with others */
|
|
119
|
+
setOps?: SetOperationNode[];
|
|
120
|
+
}
|
|
157
121
|
|
|
158
122
|
export interface InsertQueryNode {
|
|
159
123
|
type: 'InsertQuery';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal column reference used by AST builders.
|
|
3
|
+
* Accepts any object with a name and optional table/alias fields
|
|
4
|
+
* (schema ColumnDef/TableDef remain structurally compatible).
|
|
5
|
+
*/
|
|
6
|
+
export interface ColumnRef {
|
|
7
|
+
name: string;
|
|
8
|
+
table?: string;
|
|
9
|
+
alias?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Minimal table reference used by AST builders.
|
|
14
|
+
* Keeps AST decoupled from full schema TableDef shape.
|
|
15
|
+
*/
|
|
16
|
+
export interface TableRef {
|
|
17
|
+
name: string;
|
|
18
|
+
schema?: string;
|
|
19
|
+
alias?: string;
|
|
20
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
1
|
+
import { ColumnNode, LiteralNode, JsonPathNode, WindowFunctionNode } from './expression-nodes.js';
|
|
2
|
+
import { columnOperand } from './expression-builders.js';
|
|
3
|
+
import { OrderDirection } from '../sql/sql.js';
|
|
4
|
+
import { OrderByNode } from './query.js';
|
|
5
|
+
import { ColumnRef } from './types.js';
|
|
6
6
|
|
|
7
7
|
const buildWindowFunction = (
|
|
8
8
|
name: string,
|
|
@@ -50,21 +50,21 @@ export const denseRank = (): WindowFunctionNode => buildWindowFunction('DENSE_RA
|
|
|
50
50
|
* @param n - Number of buckets
|
|
51
51
|
* @returns Window function node for NTILE
|
|
52
52
|
*/
|
|
53
|
-
export const ntile = (n: number): WindowFunctionNode =>
|
|
54
|
-
buildWindowFunction('NTILE', [{ type: 'Literal', value: n }]);
|
|
53
|
+
export const ntile = (n: number): WindowFunctionNode =>
|
|
54
|
+
buildWindowFunction('NTILE', [{ type: 'Literal', value: n }]);
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Creates a LAG window function
|
|
58
58
|
* @param col - Column to lag
|
|
59
59
|
* @param offset - Offset (defaults to 1)
|
|
60
|
-
* @param defaultValue - Default value if no row exists
|
|
61
|
-
* @returns Window function node for LAG
|
|
62
|
-
*/
|
|
63
|
-
export const lag = (col:
|
|
64
|
-
const args: (ColumnNode | LiteralNode | JsonPathNode)[] = [
|
|
65
|
-
columnOperand(col),
|
|
66
|
-
{ type: 'Literal', value: offset }
|
|
67
|
-
];
|
|
60
|
+
* @param defaultValue - Default value if no row exists
|
|
61
|
+
* @returns Window function node for LAG
|
|
62
|
+
*/
|
|
63
|
+
export const lag = (col: ColumnRef | ColumnNode, offset: number = 1, defaultValue?: any): WindowFunctionNode => {
|
|
64
|
+
const args: (ColumnNode | LiteralNode | JsonPathNode)[] = [
|
|
65
|
+
columnOperand(col),
|
|
66
|
+
{ type: 'Literal', value: offset }
|
|
67
|
+
];
|
|
68
68
|
if (defaultValue !== undefined) {
|
|
69
69
|
args.push({ type: 'Literal', value: defaultValue });
|
|
70
70
|
}
|
|
@@ -75,14 +75,14 @@ export const lag = (col: ColumnDef | ColumnNode, offset: number = 1, defaultValu
|
|
|
75
75
|
* Creates a LEAD window function
|
|
76
76
|
* @param col - Column to lead
|
|
77
77
|
* @param offset - Offset (defaults to 1)
|
|
78
|
-
* @param defaultValue - Default value if no row exists
|
|
79
|
-
* @returns Window function node for LEAD
|
|
80
|
-
*/
|
|
81
|
-
export const lead = (col:
|
|
82
|
-
const args: (ColumnNode | LiteralNode | JsonPathNode)[] = [
|
|
83
|
-
columnOperand(col),
|
|
84
|
-
{ type: 'Literal', value: offset }
|
|
85
|
-
];
|
|
78
|
+
* @param defaultValue - Default value if no row exists
|
|
79
|
+
* @returns Window function node for LEAD
|
|
80
|
+
*/
|
|
81
|
+
export const lead = (col: ColumnRef | ColumnNode, offset: number = 1, defaultValue?: any): WindowFunctionNode => {
|
|
82
|
+
const args: (ColumnNode | LiteralNode | JsonPathNode)[] = [
|
|
83
|
+
columnOperand(col),
|
|
84
|
+
{ type: 'Literal', value: offset }
|
|
85
|
+
];
|
|
86
86
|
if (defaultValue !== undefined) {
|
|
87
87
|
args.push({ type: 'Literal', value: defaultValue });
|
|
88
88
|
}
|
|
@@ -91,19 +91,19 @@ export const lead = (col: ColumnDef | ColumnNode, offset: number = 1, defaultVal
|
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* Creates a FIRST_VALUE window function
|
|
94
|
-
* @param col - Column to get first value from
|
|
95
|
-
* @returns Window function node for FIRST_VALUE
|
|
96
|
-
*/
|
|
97
|
-
export const firstValue = (col:
|
|
98
|
-
buildWindowFunction('FIRST_VALUE', [columnOperand(col)]);
|
|
94
|
+
* @param col - Column to get first value from
|
|
95
|
+
* @returns Window function node for FIRST_VALUE
|
|
96
|
+
*/
|
|
97
|
+
export const firstValue = (col: ColumnRef | ColumnNode): WindowFunctionNode =>
|
|
98
|
+
buildWindowFunction('FIRST_VALUE', [columnOperand(col)]);
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
101
|
* Creates a LAST_VALUE window function
|
|
102
|
-
* @param col - Column to get last value from
|
|
103
|
-
* @returns Window function node for LAST_VALUE
|
|
104
|
-
*/
|
|
105
|
-
export const lastValue = (col:
|
|
106
|
-
buildWindowFunction('LAST_VALUE', [columnOperand(col)]);
|
|
102
|
+
* @param col - Column to get last value from
|
|
103
|
+
* @returns Window function node for LAST_VALUE
|
|
104
|
+
*/
|
|
105
|
+
export const lastValue = (col: ColumnRef | ColumnNode): WindowFunctionNode =>
|
|
106
|
+
buildWindowFunction('LAST_VALUE', [columnOperand(col)]);
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
109
|
* Creates a custom window function
|
|
@@ -113,28 +113,28 @@ export const lastValue = (col: ColumnDef | ColumnNode): WindowFunctionNode =>
|
|
|
113
113
|
* @param orderBy - Optional ORDER BY clauses
|
|
114
114
|
* @returns Window function node
|
|
115
115
|
*/
|
|
116
|
-
export const windowFunction = (
|
|
117
|
-
name: string,
|
|
118
|
-
args: (
|
|
119
|
-
partitionBy?: (
|
|
120
|
-
orderBy?: { column:
|
|
121
|
-
): WindowFunctionNode => {
|
|
122
|
-
const nodeArgs = args.map(arg => {
|
|
123
|
-
if (typeof (arg as LiteralNode).value !== 'undefined') {
|
|
124
|
-
return arg as LiteralNode;
|
|
125
|
-
}
|
|
126
|
-
if ('path' in arg) {
|
|
127
|
-
return arg as JsonPathNode;
|
|
128
|
-
}
|
|
129
|
-
return columnOperand(arg as
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
const partitionNodes = partitionBy?.map(col => columnOperand(col)) ?? undefined;
|
|
133
|
-
const orderNodes: OrderByNode[] | undefined = orderBy?.map(o => ({
|
|
134
|
-
type: 'OrderBy',
|
|
135
|
-
column: columnOperand(o.column),
|
|
136
|
-
direction: o.direction
|
|
137
|
-
}));
|
|
116
|
+
export const windowFunction = (
|
|
117
|
+
name: string,
|
|
118
|
+
args: (ColumnRef | ColumnNode | LiteralNode | JsonPathNode)[] = [],
|
|
119
|
+
partitionBy?: (ColumnRef | ColumnNode)[],
|
|
120
|
+
orderBy?: { column: ColumnRef | ColumnNode; direction: OrderDirection }[]
|
|
121
|
+
): WindowFunctionNode => {
|
|
122
|
+
const nodeArgs = args.map(arg => {
|
|
123
|
+
if (typeof (arg as LiteralNode).value !== 'undefined') {
|
|
124
|
+
return arg as LiteralNode;
|
|
125
|
+
}
|
|
126
|
+
if ('path' in arg) {
|
|
127
|
+
return arg as JsonPathNode;
|
|
128
|
+
}
|
|
129
|
+
return columnOperand(arg as ColumnRef | ColumnNode);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const partitionNodes = partitionBy?.map(col => columnOperand(col)) ?? undefined;
|
|
133
|
+
const orderNodes: OrderByNode[] | undefined = orderBy?.map(o => ({
|
|
134
|
+
type: 'OrderBy',
|
|
135
|
+
column: columnOperand(o.column),
|
|
136
|
+
direction: o.direction
|
|
137
|
+
}));
|
|
138
138
|
|
|
139
139
|
return buildWindowFunction(name, nodeArgs, partitionNodes, orderNodes);
|
|
140
140
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { SchemaDialect, DialectName
|
|
1
|
+
import { SchemaDialect, DialectName } from '../schema-dialect.js';
|
|
2
|
+
import { formatLiteral, quoteQualified, createLiteralFormatter, LiteralFormatter } from '../sql-writing.js';
|
|
2
3
|
import { ColumnDef, ForeignKeyReference } from '../../../schema/column.js';
|
|
3
4
|
import { IndexDef, TableDef } from '../../../schema/table.js';
|
|
4
|
-
import { DatabaseTable } from '../schema-types.js';
|
|
5
|
+
import { DatabaseTable, DatabaseColumn, ColumnDiff } from '../schema-types.js';
|
|
5
6
|
|
|
6
7
|
type TableLike = { name: string; schema?: string };
|
|
7
8
|
|
|
@@ -10,7 +11,7 @@ type TableLike = { name: string; schema?: string };
|
|
|
10
11
|
* Concrete dialects only override the small surface area instead of reimplementing everything.
|
|
11
12
|
*/
|
|
12
13
|
export abstract class BaseSchemaDialect implements SchemaDialect {
|
|
13
|
-
abstract name: DialectName;
|
|
14
|
+
abstract readonly name: DialectName;
|
|
14
15
|
abstract quoteIdentifier(id: string): string;
|
|
15
16
|
abstract renderColumnType(column: ColumnDef): string;
|
|
16
17
|
abstract renderAutoIncrement(column: ColumnDef, table: TableDef): string | undefined;
|
|
@@ -24,8 +25,11 @@ export abstract class BaseSchemaDialect implements SchemaDialect {
|
|
|
24
25
|
}
|
|
25
26
|
return this.quoteIdentifier(table.name);
|
|
26
27
|
}
|
|
28
|
+
// Each dialect should provide its own formatter
|
|
29
|
+
abstract get literalFormatter(): LiteralFormatter;
|
|
30
|
+
|
|
27
31
|
renderDefault(value: unknown, _column: ColumnDef): string {
|
|
28
|
-
return formatLiteral(
|
|
32
|
+
return formatLiteral(this.literalFormatter, value);
|
|
29
33
|
}
|
|
30
34
|
renderReference(ref: ForeignKeyReference, _table: TableDef): string {
|
|
31
35
|
const parts = ['REFERENCES', quoteQualified(this, ref.table), `(${this.quoteIdentifier(ref.column)})`];
|
|
@@ -40,9 +44,19 @@ export abstract class BaseSchemaDialect implements SchemaDialect {
|
|
|
40
44
|
dropTableSql(table: DatabaseTable): string[] {
|
|
41
45
|
return [`DROP TABLE IF EXISTS ${this.formatTableName(table)};`];
|
|
42
46
|
}
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
dropColumnSql(table: DatabaseTable, column: string): string[] {
|
|
48
|
+
return [`ALTER TABLE ${this.formatTableName(table)} DROP COLUMN ${this.quoteIdentifier(column)};`];
|
|
49
|
+
}
|
|
50
|
+
dropIndexSql(table: DatabaseTable, index: string): string[] {
|
|
51
|
+
return [`DROP INDEX ${this.quoteIdentifier(index)};`];
|
|
52
|
+
}
|
|
45
53
|
warnDropColumn(_table: DatabaseTable, _column: string): string | undefined {
|
|
46
54
|
return undefined;
|
|
47
55
|
}
|
|
56
|
+
alterColumnSql?(table: TableDef, column: ColumnDef, actualColumn: DatabaseColumn, diff: ColumnDiff): string[] {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
warnAlterColumn?(_table: TableDef, _column: ColumnDef, _actual: DatabaseColumn, _diff: ColumnDiff): string | undefined {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
48
62
|
}
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import { BaseSchemaDialect } from './base-schema-dialect.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
renderIndexColumns,
|
|
5
|
-
DialectName,
|
|
6
|
-
formatLiteral
|
|
7
|
-
} from '../schema-generator.js';
|
|
2
|
+
import { deriveIndexName } from '../naming-strategy.js';
|
|
3
|
+
import { renderIndexColumns, createLiteralFormatter } from '../sql-writing.js';
|
|
8
4
|
import { ColumnDef } from '../../../schema/column.js';
|
|
9
5
|
import { IndexDef, TableDef } from '../../../schema/table.js';
|
|
10
|
-
import { DatabaseTable } from '../schema-types.js';
|
|
6
|
+
import { ColumnDiff, DatabaseColumn, DatabaseTable } from '../schema-types.js';
|
|
7
|
+
import { DialectName } from '../schema-dialect.js';
|
|
11
8
|
|
|
12
9
|
export class MSSqlSchemaDialect extends BaseSchemaDialect {
|
|
13
10
|
name: DialectName = 'mssql';
|
|
14
11
|
|
|
12
|
+
private _literalFormatter = createLiteralFormatter({
|
|
13
|
+
booleanTrue: '1',
|
|
14
|
+
booleanFalse: '0',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
get literalFormatter() {
|
|
18
|
+
return this._literalFormatter;
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
quoteIdentifier(id: string): string {
|
|
16
22
|
return `[${id.replace(/]/g, ']]')}]`;
|
|
17
23
|
}
|
|
@@ -68,7 +74,7 @@ export class MSSqlSchemaDialect extends BaseSchemaDialect {
|
|
|
68
74
|
}
|
|
69
75
|
|
|
70
76
|
renderDefault(value: unknown): string {
|
|
71
|
-
return formatLiteral(value
|
|
77
|
+
return this.literalFormatter.formatLiteral(value);
|
|
72
78
|
}
|
|
73
79
|
|
|
74
80
|
renderAutoIncrement(column: ColumnDef): string | undefined {
|
|
@@ -94,4 +100,22 @@ export class MSSqlSchemaDialect extends BaseSchemaDialect {
|
|
|
94
100
|
dropIndexSql(table: DatabaseTable, index: string): string[] {
|
|
95
101
|
return [`DROP INDEX ${this.quoteIdentifier(index)} ON ${this.formatTableName(table)};`];
|
|
96
102
|
}
|
|
103
|
+
|
|
104
|
+
alterColumnSql(table: TableDef, column: ColumnDef, _actual: DatabaseColumn, diff: ColumnDiff): string[] {
|
|
105
|
+
const stmts: string[] = [];
|
|
106
|
+
if (diff.typeChanged || diff.nullabilityChanged) {
|
|
107
|
+
const nullability = column.notNull ? 'NOT NULL' : 'NULL';
|
|
108
|
+
stmts.push(
|
|
109
|
+
`ALTER TABLE ${this.formatTableName(table)} ALTER COLUMN ${this.quoteIdentifier(column.name)} ${this.renderColumnType(column)} ${nullability};`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return stmts;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
warnAlterColumn(_table: TableDef, _column: ColumnDef, _actual: DatabaseColumn, diff: ColumnDiff): string | undefined {
|
|
116
|
+
if (diff.defaultChanged || diff.autoIncrementChanged) {
|
|
117
|
+
return 'Altering defaults or identity on MSSQL is not automated (requires dropping/adding default or identity constraints manually).';
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
97
121
|
}
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { BaseSchemaDialect } from './base-schema-dialect.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
renderIndexColumns,
|
|
5
|
-
DialectName,
|
|
6
|
-
formatLiteral,
|
|
7
|
-
escapeLiteral
|
|
8
|
-
} from '../schema-generator.js';
|
|
2
|
+
import { deriveIndexName } from '../naming-strategy.js';
|
|
3
|
+
import { renderIndexColumns, escapeSqlString, createLiteralFormatter } from '../sql-writing.js';
|
|
9
4
|
import { ColumnDef } from '../../../schema/column.js';
|
|
10
5
|
import { IndexDef, TableDef } from '../../../schema/table.js';
|
|
11
|
-
import { DatabaseTable } from '../schema-types.js';
|
|
6
|
+
import { ColumnDiff, DatabaseColumn, DatabaseTable } from '../schema-types.js';
|
|
7
|
+
import { renderColumnDefinition } from '../schema-generator.js';
|
|
8
|
+
import { DialectName } from '../schema-dialect.js';
|
|
12
9
|
|
|
13
10
|
export class MySqlSchemaDialect extends BaseSchemaDialect {
|
|
14
11
|
name: DialectName = 'mysql';
|
|
15
12
|
|
|
13
|
+
private _literalFormatter = createLiteralFormatter({
|
|
14
|
+
booleanTrue: '1',
|
|
15
|
+
booleanFalse: '0',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
get literalFormatter() {
|
|
19
|
+
return this._literalFormatter;
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
quoteIdentifier(id: string): string {
|
|
17
23
|
return `\`${id}\``;
|
|
18
24
|
}
|
|
@@ -66,7 +72,7 @@ export class MySqlSchemaDialect extends BaseSchemaDialect {
|
|
|
66
72
|
case 'ENUM':
|
|
67
73
|
case 'enum':
|
|
68
74
|
return column.args && Array.isArray(column.args) && column.args.length
|
|
69
|
-
? `ENUM(${column.args.map((v: string) => `'${
|
|
75
|
+
? `ENUM(${column.args.map((v: string) => `'${escapeSqlString(v)}'`).join(',')})`
|
|
70
76
|
: 'ENUM';
|
|
71
77
|
default:
|
|
72
78
|
return String(column.type).toUpperCase();
|
|
@@ -74,7 +80,7 @@ export class MySqlSchemaDialect extends BaseSchemaDialect {
|
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
renderDefault(value: unknown): string {
|
|
77
|
-
return formatLiteral(value
|
|
83
|
+
return this.literalFormatter.formatLiteral(value);
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
renderAutoIncrement(column: ColumnDef): string | undefined {
|
|
@@ -106,4 +112,9 @@ export class MySqlSchemaDialect extends BaseSchemaDialect {
|
|
|
106
112
|
dropIndexSql(table: DatabaseTable, index: string): string[] {
|
|
107
113
|
return [`DROP INDEX ${this.quoteIdentifier(index)} ON ${this.formatTableName(table)};`];
|
|
108
114
|
}
|
|
115
|
+
|
|
116
|
+
alterColumnSql(table: TableDef, column: ColumnDef, _actual: DatabaseColumn, _diff: ColumnDiff): string[] {
|
|
117
|
+
const rendered = renderColumnDefinition(table, column, this);
|
|
118
|
+
return [`ALTER TABLE ${this.formatTableName(table)} MODIFY COLUMN ${rendered.sql};`];
|
|
119
|
+
}
|
|
109
120
|
}
|
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import { BaseSchemaDialect } from './base-schema-dialect.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
renderIndexColumns,
|
|
5
|
-
DialectName
|
|
6
|
-
} from '../schema-generator.js';
|
|
2
|
+
import { deriveIndexName } from '../naming-strategy.js';
|
|
3
|
+
import { renderIndexColumns, createLiteralFormatter } from '../sql-writing.js';
|
|
7
4
|
import { ColumnDef } from '../../../schema/column.js';
|
|
8
5
|
import { IndexDef, TableDef } from '../../../schema/table.js';
|
|
9
|
-
import { DatabaseTable } from '../schema-types.js';
|
|
6
|
+
import { ColumnDiff, DatabaseColumn, DatabaseTable } from '../schema-types.js';
|
|
7
|
+
import { DialectName } from '../schema-dialect.js';
|
|
10
8
|
|
|
11
9
|
export class PostgresSchemaDialect extends BaseSchemaDialect {
|
|
12
|
-
name: DialectName = 'postgres';
|
|
10
|
+
readonly name: DialectName = 'postgres';
|
|
11
|
+
|
|
12
|
+
private _literalFormatter = createLiteralFormatter({
|
|
13
|
+
booleanTrue: 'TRUE',
|
|
14
|
+
booleanFalse: 'FALSE',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
get literalFormatter() {
|
|
18
|
+
return this._literalFormatter;
|
|
19
|
+
}
|
|
13
20
|
|
|
14
21
|
quoteIdentifier(id: string): string {
|
|
15
22
|
return `"${id}"`;
|
|
@@ -96,4 +103,42 @@ export class PostgresSchemaDialect extends BaseSchemaDialect {
|
|
|
96
103
|
: this.quoteIdentifier(index);
|
|
97
104
|
return [`DROP INDEX IF EXISTS ${qualified};`];
|
|
98
105
|
}
|
|
106
|
+
|
|
107
|
+
alterColumnSql(table: TableDef, column: ColumnDef, actualColumn: DatabaseColumn, diff: ColumnDiff): string[] {
|
|
108
|
+
const stmts: string[] = [];
|
|
109
|
+
const tableName = this.formatTableName(table);
|
|
110
|
+
const colName = this.quoteIdentifier(column.name);
|
|
111
|
+
|
|
112
|
+
if (diff.typeChanged) {
|
|
113
|
+
stmts.push(`ALTER TABLE ${tableName} ALTER COLUMN ${colName} TYPE ${this.renderColumnType(column)};`);
|
|
114
|
+
}
|
|
115
|
+
if (diff.defaultChanged) {
|
|
116
|
+
if (column.default === undefined) {
|
|
117
|
+
stmts.push(`ALTER TABLE ${tableName} ALTER COLUMN ${colName} DROP DEFAULT;`);
|
|
118
|
+
} else {
|
|
119
|
+
stmts.push(
|
|
120
|
+
`ALTER TABLE ${tableName} ALTER COLUMN ${colName} SET DEFAULT ${this.renderDefault(column.default, column)};`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (diff.nullabilityChanged) {
|
|
125
|
+
stmts.push(`ALTER TABLE ${tableName} ALTER COLUMN ${colName} ${column.notNull ? 'SET' : 'DROP'} NOT NULL;`);
|
|
126
|
+
}
|
|
127
|
+
if (diff.autoIncrementChanged) {
|
|
128
|
+
if (column.autoIncrement) {
|
|
129
|
+
const strategy = column.generated === 'always' ? 'ALWAYS' : 'BY DEFAULT';
|
|
130
|
+
stmts.push(`ALTER TABLE ${tableName} ALTER COLUMN ${colName} ADD GENERATED ${strategy} AS IDENTITY;`);
|
|
131
|
+
} else {
|
|
132
|
+
stmts.push(`ALTER TABLE ${tableName} ALTER COLUMN ${colName} DROP IDENTITY IF EXISTS;`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return stmts;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
warnAlterColumn(_table: TableDef, _column: ColumnDef, _actual: DatabaseColumn, diff: ColumnDiff): string | undefined {
|
|
139
|
+
if (diff.autoIncrementChanged) {
|
|
140
|
+
return 'Altering identity properties may fail if an existing sequence is attached; verify generated column state.';
|
|
141
|
+
}
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
99
144
|
}
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { BaseSchemaDialect } from './base-schema-dialect.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
renderIndexColumns,
|
|
5
|
-
DialectName,
|
|
6
|
-
formatLiteral,
|
|
7
|
-
resolvePrimaryKey
|
|
8
|
-
} from '../schema-generator.js';
|
|
2
|
+
import { deriveIndexName } from '../naming-strategy.js';
|
|
3
|
+
import { renderIndexColumns, resolvePrimaryKey, createLiteralFormatter } from '../sql-writing.js';
|
|
9
4
|
import { ColumnDef } from '../../../schema/column.js';
|
|
10
5
|
import { IndexDef, TableDef } from '../../../schema/table.js';
|
|
11
|
-
import { DatabaseTable } from '../schema-types.js';
|
|
6
|
+
import { ColumnDiff, DatabaseColumn, DatabaseTable } from '../schema-types.js';
|
|
7
|
+
import { DialectName } from '../schema-dialect.js';
|
|
12
8
|
|
|
13
9
|
export class SQLiteSchemaDialect extends BaseSchemaDialect {
|
|
14
10
|
name: DialectName = 'sqlite';
|
|
15
11
|
|
|
12
|
+
private _literalFormatter = createLiteralFormatter({
|
|
13
|
+
booleanTrue: '1',
|
|
14
|
+
booleanFalse: '0',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
get literalFormatter() {
|
|
18
|
+
return this._literalFormatter;
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
quoteIdentifier(id: string): string {
|
|
17
22
|
return `"${id}"`;
|
|
18
23
|
}
|
|
@@ -75,7 +80,7 @@ export class SQLiteSchemaDialect extends BaseSchemaDialect {
|
|
|
75
80
|
}
|
|
76
81
|
|
|
77
82
|
renderDefault(value: unknown): string {
|
|
78
|
-
return formatLiteral(value
|
|
83
|
+
return this.literalFormatter.formatLiteral(value);
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
renderIndex(table: TableDef, index: IndexDef): string {
|
|
@@ -100,4 +105,13 @@ export class SQLiteSchemaDialect extends BaseSchemaDialect {
|
|
|
100
105
|
const key = table.schema ? `${table.schema}.${table.name}` : table.name;
|
|
101
106
|
return `Dropping columns on SQLite requires table rebuild (column ${column} on ${key}).`;
|
|
102
107
|
}
|
|
108
|
+
|
|
109
|
+
alterColumnSql(_table: TableDef, _column: ColumnDef, _actual: DatabaseColumn, _diff: ColumnDiff): string[] {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
warnAlterColumn(table: TableDef, column: ColumnDef, _actual: DatabaseColumn, _diff: ColumnDiff): string | undefined {
|
|
114
|
+
const key = table.schema ? `${table.schema}.${table.name}` : table.name;
|
|
115
|
+
return `SQLite ALTER COLUMN is not supported; rebuild table ${key} to change column ${column.name}.`;
|
|
116
|
+
}
|
|
103
117
|
}
|