metal-orm 1.0.13 → 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 +75 -82
- 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
|
@@ -1,179 +1,211 @@
|
|
|
1
|
-
import { TableDef } from '../../schema/table.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
| '
|
|
13
|
-
| '
|
|
14
|
-
| '
|
|
15
|
-
| '
|
|
16
|
-
| '
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
1
|
+
import { TableDef } from '../../schema/table.js';
|
|
2
|
+
import { ColumnDef } from '../../schema/column.js';
|
|
3
|
+
import type { DbExecutor } from '../execution/db-executor.js';
|
|
4
|
+
import { SchemaDialect } from './schema-dialect.js';
|
|
5
|
+
import { deriveIndexName } from './naming-strategy.js';
|
|
6
|
+
import { generateCreateTableSql, renderColumnDefinition } from './schema-generator.js';
|
|
7
|
+
import { ColumnDiff, DatabaseColumn, DatabaseSchema, DatabaseTable } from './schema-types.js';
|
|
8
|
+
|
|
9
|
+
export type SchemaChangeKind =
|
|
10
|
+
| 'createTable'
|
|
11
|
+
| 'dropTable'
|
|
12
|
+
| 'addColumn'
|
|
13
|
+
| 'dropColumn'
|
|
14
|
+
| 'alterColumn'
|
|
15
|
+
| 'addIndex'
|
|
16
|
+
| 'dropIndex';
|
|
17
|
+
|
|
18
|
+
export interface SchemaChange {
|
|
19
|
+
kind: SchemaChangeKind;
|
|
20
|
+
table: string;
|
|
21
|
+
description: string;
|
|
22
|
+
statements: string[];
|
|
23
|
+
safe: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface SchemaPlan {
|
|
27
|
+
changes: SchemaChange[];
|
|
28
|
+
warnings: string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SchemaDiffOptions {
|
|
32
|
+
/** Allow destructive operations (drops) */
|
|
33
|
+
allowDestructive?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const tableKey = (name: string, schema?: string) => (schema ? `${schema}.${name}` : name);
|
|
37
|
+
|
|
38
|
+
const mapTables = (schema: DatabaseSchema) => {
|
|
39
|
+
const map = new Map<string, DatabaseTable>();
|
|
40
|
+
for (const table of schema.tables) {
|
|
41
|
+
map.set(tableKey(table.name, table.schema), table);
|
|
42
|
+
}
|
|
43
|
+
return map;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const buildAddColumnSql = (table: TableDef, colName: string, dialect: SchemaDialect): string => {
|
|
47
|
+
const column = table.columns[colName];
|
|
48
|
+
const rendered = renderColumnDefinition(table, column, dialect);
|
|
49
|
+
return `ALTER TABLE ${dialect.formatTableName(table)} ADD ${rendered.sql};`;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const normalizeType = (value: string | undefined): string => (value || '').toLowerCase().replace(/\s+/g, ' ').trim();
|
|
53
|
+
const normalizeDefault = (value: unknown): string | undefined => {
|
|
54
|
+
if (value === undefined || value === null) return undefined;
|
|
55
|
+
return String(value).trim();
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const diffColumn = (expected: ColumnDef, actual: DatabaseColumn, dialect: SchemaDialect): ColumnDiff => {
|
|
59
|
+
const expectedType = normalizeType(dialect.renderColumnType(expected));
|
|
60
|
+
const actualType = normalizeType(actual.type);
|
|
61
|
+
const expectedDefault =
|
|
62
|
+
expected.default !== undefined ? normalizeDefault(dialect.renderDefault(expected.default, expected)) : undefined;
|
|
63
|
+
const actualDefault = normalizeDefault(actual.default);
|
|
64
|
+
return {
|
|
65
|
+
typeChanged: expectedType !== actualType,
|
|
66
|
+
nullabilityChanged: !!expected.notNull !== !!actual.notNull,
|
|
67
|
+
defaultChanged: expectedDefault !== actualDefault,
|
|
68
|
+
autoIncrementChanged: !!expected.autoIncrement !== !!actual.autoIncrement
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const diffSchema = (
|
|
73
|
+
expectedTables: TableDef[],
|
|
74
|
+
actualSchema: DatabaseSchema,
|
|
75
|
+
dialect: SchemaDialect,
|
|
76
|
+
options: SchemaDiffOptions = {}
|
|
77
|
+
): SchemaPlan => {
|
|
78
|
+
const allowDestructive = options.allowDestructive ?? false;
|
|
79
|
+
const plan: SchemaPlan = { changes: [], warnings: [] };
|
|
80
|
+
|
|
81
|
+
const actualMap = mapTables(actualSchema);
|
|
82
|
+
|
|
83
|
+
// Create missing tables and indexes
|
|
84
|
+
for (const table of expectedTables) {
|
|
85
|
+
const key = tableKey(table.name, table.schema);
|
|
86
|
+
const actual = actualMap.get(key);
|
|
87
|
+
if (!actual) {
|
|
88
|
+
const { tableSql, indexSql } = generateCreateTableSql(table, dialect);
|
|
89
|
+
plan.changes.push({
|
|
90
|
+
kind: 'createTable',
|
|
91
|
+
table: key,
|
|
92
|
+
description: `Create table ${key}`,
|
|
93
|
+
statements: [tableSql, ...indexSql],
|
|
94
|
+
safe: true
|
|
95
|
+
});
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Columns
|
|
100
|
+
const actualCols = new Map(actual.columns.map(c => [c.name, c]));
|
|
101
|
+
for (const colName of Object.keys(table.columns)) {
|
|
102
|
+
if (!actualCols.has(colName)) {
|
|
103
|
+
plan.changes.push({
|
|
104
|
+
kind: 'addColumn',
|
|
105
|
+
table: key,
|
|
106
|
+
description: `Add column ${colName} to ${key}`,
|
|
107
|
+
statements: [buildAddColumnSql(table, colName, dialect)],
|
|
108
|
+
safe: true
|
|
109
|
+
});
|
|
110
|
+
} else {
|
|
111
|
+
const expectedCol = table.columns[colName];
|
|
112
|
+
const actualCol = actualCols.get(colName)!;
|
|
113
|
+
const colDiff = diffColumn(expectedCol, actualCol, dialect);
|
|
114
|
+
const shouldAlter =
|
|
115
|
+
colDiff.typeChanged || colDiff.nullabilityChanged || colDiff.defaultChanged || colDiff.autoIncrementChanged;
|
|
116
|
+
if (shouldAlter) {
|
|
117
|
+
const statements = dialect.alterColumnSql?.(table, expectedCol, actualCol, colDiff) ?? [];
|
|
118
|
+
if (statements.length > 0) {
|
|
119
|
+
plan.changes.push({
|
|
120
|
+
kind: 'alterColumn',
|
|
121
|
+
table: key,
|
|
122
|
+
description: `Alter column ${colName} on ${key}`,
|
|
123
|
+
statements,
|
|
124
|
+
safe: true
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const warning = dialect.warnAlterColumn?.(table, expectedCol, actualCol, colDiff);
|
|
128
|
+
if (warning) plan.warnings.push(warning);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
for (const colName of actualCols.keys()) {
|
|
133
|
+
if (!table.columns[colName]) {
|
|
134
|
+
plan.changes.push({
|
|
135
|
+
kind: 'dropColumn',
|
|
136
|
+
table: key,
|
|
137
|
+
description: `Drop column ${colName} from ${key}`,
|
|
138
|
+
statements: allowDestructive ? dialect.dropColumnSql(actual, colName) : [],
|
|
139
|
+
safe: false
|
|
140
|
+
});
|
|
141
|
+
const warning = dialect.warnDropColumn?.(actual, colName);
|
|
142
|
+
if (warning) plan.warnings.push(warning);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Indexes (naive: based on name or derived name)
|
|
147
|
+
const expectedIndexes = table.indexes ?? [];
|
|
148
|
+
const actualIndexes = actual.indexes ?? [];
|
|
149
|
+
const actualIndexMap = new Map(actualIndexes.map(idx => [idx.name, idx]));
|
|
150
|
+
|
|
151
|
+
for (const idx of expectedIndexes) {
|
|
152
|
+
const name = idx.name || deriveIndexName(table, idx);
|
|
153
|
+
if (!actualIndexMap.has(name)) {
|
|
154
|
+
plan.changes.push({
|
|
155
|
+
kind: 'addIndex',
|
|
156
|
+
table: key,
|
|
157
|
+
description: `Create index ${name} on ${key}`,
|
|
158
|
+
statements: [dialect.renderIndex(table, { ...idx, name })],
|
|
159
|
+
safe: true
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (const idx of actualIndexes) {
|
|
165
|
+
if (idx.name && !expectedIndexes.find(expected => (expected.name || deriveIndexName(table, expected)) === idx.name)) {
|
|
166
|
+
plan.changes.push({
|
|
167
|
+
kind: 'dropIndex',
|
|
168
|
+
table: key,
|
|
169
|
+
description: `Drop index ${idx.name} on ${key}`,
|
|
170
|
+
statements: allowDestructive ? dialect.dropIndexSql(actual, idx.name) : [],
|
|
171
|
+
safe: false
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Extra tables
|
|
178
|
+
for (const actual of actualSchema.tables) {
|
|
179
|
+
const key = tableKey(actual.name, actual.schema);
|
|
180
|
+
if (!expectedTables.find(t => tableKey(t.name, t.schema) === key)) {
|
|
181
|
+
plan.changes.push({
|
|
182
|
+
kind: 'dropTable',
|
|
183
|
+
table: key,
|
|
184
|
+
description: `Drop table ${key}`,
|
|
185
|
+
statements: allowDestructive ? dialect.dropTableSql(actual) : [],
|
|
186
|
+
safe: false
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return plan;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export interface SynchronizeOptions extends SchemaDiffOptions {
|
|
195
|
+
dryRun?: boolean;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export const synchronizeSchema = async (
|
|
199
|
+
expectedTables: TableDef[],
|
|
200
|
+
actualSchema: DatabaseSchema,
|
|
201
|
+
dialect: SchemaDialect,
|
|
202
|
+
executor: DbExecutor,
|
|
203
|
+
options: SynchronizeOptions = {}
|
|
204
|
+
): Promise<SchemaPlan> => {
|
|
205
|
+
const plan = diffSchema(expectedTables, actualSchema, dialect, options);
|
|
206
|
+
if (!options.dryRun) {
|
|
207
|
+
const { executeSchemaPlan } = await import('./schema-plan-executor.js');
|
|
208
|
+
await executeSchemaPlan(plan, executor, options);
|
|
209
|
+
}
|
|
210
|
+
return plan;
|
|
211
|
+
};
|
|
@@ -1,98 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export interface SchemaDialect {
|
|
15
|
-
name: DialectName;
|
|
16
|
-
quoteIdentifier(id: string): string;
|
|
17
|
-
formatTableName(table: TableDef | DatabaseTable): string;
|
|
18
|
-
renderColumnType(column: ColumnDef): string;
|
|
19
|
-
renderDefault(value: unknown, column: ColumnDef): string;
|
|
20
|
-
renderAutoIncrement(column: ColumnDef, table: TableDef): string | undefined;
|
|
21
|
-
renderReference(ref: ForeignKeyReference, table: TableDef): string;
|
|
22
|
-
renderIndex(table: TableDef, index: IndexDef): string;
|
|
23
|
-
renderTableOptions(table: TableDef): string | undefined;
|
|
24
|
-
supportsPartialIndexes(): boolean;
|
|
25
|
-
preferInlinePkAutoincrement?(column: ColumnDef, table: TableDef, pk: string[]): boolean;
|
|
26
|
-
dropColumnSql(table: DatabaseTable, column: string): string[];
|
|
27
|
-
dropIndexSql(table: DatabaseTable, index: string): string[];
|
|
28
|
-
dropTableSql(table: DatabaseTable): string[];
|
|
29
|
-
warnDropColumn?(table: DatabaseTable, column: string): string | undefined;
|
|
30
|
-
}
|
|
1
|
+
import type { TableDef, IndexDef, IndexColumn } from '../../schema/table.js';
|
|
2
|
+
import type { ColumnDef, ForeignKeyReference } from '../../schema/column.js';
|
|
3
|
+
import type { SchemaDialect, DialectName } from './schema-dialect.js';
|
|
4
|
+
import { deriveIndexName } from './naming-strategy.js';
|
|
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';
|
|
31
13
|
|
|
32
14
|
export interface SchemaGenerateResult {
|
|
33
15
|
tableSql: string;
|
|
34
16
|
indexSql: string[];
|
|
35
17
|
}
|
|
36
18
|
|
|
37
|
-
export const escapeLiteral = (value: string): string => value.replace(/'/g, "''");
|
|
38
|
-
|
|
39
|
-
const isRawDefault = (value: unknown): value is RawDefaultValue => {
|
|
40
|
-
return !!value && typeof value === 'object' && 'raw' in (value as any) && typeof (value as any).raw === 'string';
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export const formatLiteral = (value: unknown, dialect: DialectName): string => {
|
|
44
|
-
if (isRawDefault(value)) return value.raw;
|
|
45
|
-
if (value === null) return 'NULL';
|
|
46
|
-
if (typeof value === 'number') return Number.isFinite(value) ? String(value) : 'NULL';
|
|
47
|
-
if (typeof value === 'boolean') {
|
|
48
|
-
if (dialect === 'mysql' || dialect === 'sqlite' || dialect === 'mssql') {
|
|
49
|
-
return value ? '1' : '0';
|
|
50
|
-
}
|
|
51
|
-
return value ? 'TRUE' : 'FALSE';
|
|
52
|
-
}
|
|
53
|
-
if (value instanceof Date) return `'${escapeLiteral(value.toISOString())}'`;
|
|
54
|
-
if (typeof value === 'string') return `'${escapeLiteral(value)}'`;
|
|
55
|
-
return `'${escapeLiteral(JSON.stringify(value))}'`;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export const resolvePrimaryKey = (table: TableDef): string[] => {
|
|
59
|
-
if (table.primaryKey && table.primaryKey.length > 0) {
|
|
60
|
-
return table.primaryKey;
|
|
61
|
-
}
|
|
62
|
-
const cols = Object.values(table.columns);
|
|
63
|
-
return cols.filter(c => c.primary).map(c => c.name);
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
export const quoteQualified = (dialect: SchemaDialect, identifier: string): string => {
|
|
67
|
-
if (identifier.includes('.')) {
|
|
68
|
-
return identifier
|
|
69
|
-
.split('.')
|
|
70
|
-
.map(part => dialect.quoteIdentifier(part))
|
|
71
|
-
.join('.');
|
|
72
|
-
}
|
|
73
|
-
return dialect.quoteIdentifier(identifier);
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
export const renderIndexColumns = (dialect: SchemaDialect, columns: (string | IndexColumn)[]) => {
|
|
77
|
-
return columns
|
|
78
|
-
.map(col => {
|
|
79
|
-
if (typeof col === 'string') return dialect.quoteIdentifier(col);
|
|
80
|
-
const parts = [dialect.quoteIdentifier(col.column)];
|
|
81
|
-
if (col.order) parts.push(col.order);
|
|
82
|
-
if (col.nulls) parts.push(`NULLS ${col.nulls}`);
|
|
83
|
-
return parts.join(' ');
|
|
84
|
-
})
|
|
85
|
-
.join(', ');
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
export const deriveIndexName = (table: TableDef, index: IndexDef): string => {
|
|
89
|
-
const base = (index.columns || [])
|
|
90
|
-
.map(col => (typeof col === 'string' ? col : col.column))
|
|
91
|
-
.join('_');
|
|
92
|
-
const suffix = index.unique ? 'uniq' : 'idx';
|
|
93
|
-
return `${table.name}_${base}_${suffix}`;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
19
|
export interface RenderColumnOptions {
|
|
97
20
|
includePrimary?: boolean;
|
|
98
21
|
}
|
|
@@ -136,7 +59,7 @@ export const generateCreateTableSql = (
|
|
|
136
59
|
const inlinePkColumns = new Set<string>();
|
|
137
60
|
|
|
138
61
|
const columnLines = Object.values(table.columns).map(col => {
|
|
139
|
-
const includePk = dialect.preferInlinePkAutoincrement?.(col, table, pk) && pk.includes(col.name);
|
|
62
|
+
const includePk = (dialect as any).preferInlinePkAutoincrement?.(col, table, pk) && pk.includes(col.name);
|
|
140
63
|
if (includePk) {
|
|
141
64
|
inlinePkColumns.add(col.name);
|
|
142
65
|
}
|
|
@@ -227,3 +150,6 @@ const orderTablesByDependencies = (tables: TableDef[]): TableDef[] => {
|
|
|
227
150
|
tables.forEach(t => visit(t.name, new Set()));
|
|
228
151
|
return ordered;
|
|
229
152
|
};
|
|
153
|
+
|
|
154
|
+
// Re-export DialectName for backward compatibility
|
|
155
|
+
export { DialectName };
|
|
@@ -1,32 +1,25 @@
|
|
|
1
|
-
import { DialectName } from './schema-generator.js';
|
|
2
|
-
import { DatabaseSchema } from './schema-types.js';
|
|
3
|
-
import { DbExecutor } from '
|
|
4
|
-
import type { IntrospectOptions, SchemaIntrospector } from './introspect/types.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
executor:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (!handler) {
|
|
27
|
-
throw new Error(`Unsupported dialect for introspection: ${dialect}`);
|
|
28
|
-
}
|
|
29
|
-
return handler.introspect(executor, options);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export type { IntrospectOptions, SchemaIntrospector };
|
|
1
|
+
import { DialectName } from './schema-generator.js';
|
|
2
|
+
import { DatabaseSchema } from './schema-types.js';
|
|
3
|
+
import { DbExecutor } from '../execution/db-executor.js';
|
|
4
|
+
import type { IntrospectOptions, SchemaIntrospector, IntrospectContext } from './introspect/types.js';
|
|
5
|
+
import { getSchemaIntrospector } from './introspect/registry.js';
|
|
6
|
+
import { DialectFactory } from '../dialect/dialect-factory.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Introspects an existing database schema using the dialect-specific strategy.
|
|
10
|
+
*/
|
|
11
|
+
export const introspectSchema = async (
|
|
12
|
+
executor: DbExecutor,
|
|
13
|
+
dialect: DialectName,
|
|
14
|
+
options: IntrospectOptions = {}
|
|
15
|
+
): Promise<DatabaseSchema> => {
|
|
16
|
+
const handler = getSchemaIntrospector(dialect);
|
|
17
|
+
if (!handler) {
|
|
18
|
+
throw new Error(`Unsupported dialect for introspection: ${dialect}`);
|
|
19
|
+
}
|
|
20
|
+
const dialectInstance = DialectFactory.create(dialect);
|
|
21
|
+
const ctx: IntrospectContext = { executor, dialect: dialectInstance };
|
|
22
|
+
return handler.introspect(ctx, options);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type { IntrospectOptions, SchemaIntrospector };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DbExecutor } from '../execution/db-executor.js';
|
|
2
|
+
import type { SchemaPlan, SynchronizeOptions } from './schema-diff.js';
|
|
3
|
+
|
|
4
|
+
export const executeSchemaPlan = async (
|
|
5
|
+
plan: SchemaPlan,
|
|
6
|
+
executor: DbExecutor,
|
|
7
|
+
options: SynchronizeOptions = {}
|
|
8
|
+
): Promise<void> => {
|
|
9
|
+
for (const change of plan.changes) {
|
|
10
|
+
if (!change.statements.length) continue;
|
|
11
|
+
if (!change.safe && !options.allowDestructive) continue;
|
|
12
|
+
for (const stmt of change.statements) {
|
|
13
|
+
if (!stmt.trim()) continue;
|
|
14
|
+
await executor.executeSql(stmt);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
};
|