metal-orm 1.1.10 → 1.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +106 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +106 -43
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/generate-entities/render.mjs +21 -12
- package/scripts/generate-entities/schema.mjs +87 -73
- package/scripts/generate-entities/tree-detection.mjs +67 -61
- package/src/bulk/bulk-delete-executor.ts +3 -5
- package/src/bulk/bulk-insert-executor.ts +7 -7
- package/src/bulk/bulk-update-executor.ts +2 -2
- package/src/bulk/bulk-upsert-executor.ts +5 -7
- package/src/core/ddl/introspect/mysql.ts +113 -36
- package/src/core/execution/executors/better-sqlite3-executor.ts +4 -4
package/package.json
CHANGED
|
@@ -5,15 +5,20 @@ import { buildSchemaMetadata } from './schema.mjs';
|
|
|
5
5
|
const escapeJsString = value => value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
6
6
|
|
|
7
7
|
const sanitizePropertyName = columnName => {
|
|
8
|
-
if (!columnName) return '';
|
|
8
|
+
if (typeof columnName !== 'string' || !columnName) return '';
|
|
9
9
|
return columnName
|
|
10
10
|
.replace(/\s+/g, '_')
|
|
11
11
|
.replace(/[^a-zA-Z0-9_$]/g, '_')
|
|
12
12
|
.replace(/^[0-9]/, '_$&');
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
const normalizeColumns = columns =>
|
|
16
|
+
(Array.isArray(columns) ? columns : []).filter(
|
|
17
|
+
column => column && typeof column.name === 'string' && column.name.trim().length > 0
|
|
18
|
+
);
|
|
19
|
+
|
|
15
20
|
const formatJsDoc = comment => {
|
|
16
|
-
if (
|
|
21
|
+
if (typeof comment !== 'string') return null;
|
|
17
22
|
const normalized = comment.replace(/\r\n?/g, '\n').trim();
|
|
18
23
|
if (!normalized) return null;
|
|
19
24
|
const lines = normalized.split('\n').map(line => line.replace(/\*\//g, '*\\/'));
|
|
@@ -152,7 +157,7 @@ const buildColumnRemarks = column => {
|
|
|
152
157
|
|
|
153
158
|
const buildColumnDoc = column => {
|
|
154
159
|
const entries = [];
|
|
155
|
-
if (column.comment) {
|
|
160
|
+
if (typeof column.comment === 'string' && column.comment) {
|
|
156
161
|
entries.push(column.comment);
|
|
157
162
|
}
|
|
158
163
|
const defaultValue = formatDefaultValueForDoc(column.default);
|
|
@@ -190,12 +195,13 @@ const renderColumnExpression = (column, tablePk, tableSchema, defaultSchema, pro
|
|
|
190
195
|
: `col.default(${expr}, ${def.code})`;
|
|
191
196
|
}
|
|
192
197
|
}
|
|
193
|
-
if (
|
|
198
|
+
if (
|
|
199
|
+
column.references &&
|
|
200
|
+
typeof column.references.table === 'string' &&
|
|
201
|
+
typeof column.references.column === 'string'
|
|
202
|
+
) {
|
|
194
203
|
const refTable = normalizeReferenceTable(column.references.table, tableSchema, defaultSchema);
|
|
195
|
-
const refParts = [
|
|
196
|
-
`table: '${escapeJsString(refTable)}'`,
|
|
197
|
-
`column: '${escapeJsString(column.references.column)}'`
|
|
198
|
-
];
|
|
204
|
+
const refParts = [`table: '${escapeJsString(refTable)}'`, `column: '${escapeJsString(column.references.column)}'`];
|
|
199
205
|
if (column.references.onDelete) refParts.push(`onDelete: '${escapeJsString(column.references.onDelete)}'`);
|
|
200
206
|
if (column.references.onUpdate) refParts.push(`onUpdate: '${escapeJsString(column.references.onUpdate)}'`);
|
|
201
207
|
expr = `col.references(${expr}, { ${refParts.join(', ')} })`;
|
|
@@ -277,9 +283,10 @@ const renderEntityClassLines = ({ table, className, naming, relations, resolveCl
|
|
|
277
283
|
lines.push(`@Entity(${entityOpts})`);
|
|
278
284
|
lines.push(`export class ${className} {`);
|
|
279
285
|
|
|
280
|
-
const
|
|
286
|
+
const tableColumns = normalizeColumns(table.columns);
|
|
287
|
+
const columnPropertyNames = new Set(tableColumns.map(col => sanitizePropertyName(col.name)));
|
|
281
288
|
|
|
282
|
-
for (const col of
|
|
289
|
+
for (const col of tableColumns) {
|
|
283
290
|
const propertyName = sanitizePropertyName(col.name);
|
|
284
291
|
const rendered = renderColumnExpression(col, table.primaryKey, table.schema, defaultSchema, propertyName);
|
|
285
292
|
appendJsDoc(lines, rendered.comment, ' ');
|
|
@@ -414,7 +421,9 @@ const computeTableUsage = (table, relations, defaultSchema, treeConfig) => {
|
|
|
414
421
|
needsTreeChildrenDecorator: false
|
|
415
422
|
};
|
|
416
423
|
|
|
417
|
-
|
|
424
|
+
const tableColumns = normalizeColumns(table.columns);
|
|
425
|
+
|
|
426
|
+
for (const col of tableColumns) {
|
|
418
427
|
usage.needsCol = true;
|
|
419
428
|
const rendered = renderColumnExpression(col, table.primaryKey, table.schema, defaultSchema);
|
|
420
429
|
if (rendered.decorator === 'PrimaryKey') {
|
|
@@ -552,7 +561,7 @@ export const renderEntityFile = (schema, options) => {
|
|
|
552
561
|
|
|
553
562
|
// Views only need col and Column decorator
|
|
554
563
|
for (const view of views) {
|
|
555
|
-
if (view.columns.length > 0) {
|
|
564
|
+
if (normalizeColumns(view.columns).length > 0) {
|
|
556
565
|
aggregateUsage.needsCol = true;
|
|
557
566
|
aggregateUsage.needsColumnDecorator = true;
|
|
558
567
|
}
|
|
@@ -1,33 +1,42 @@
|
|
|
1
|
-
import { detectTreeTable, mapTreeTables } from './tree-detection.mjs';
|
|
2
|
-
|
|
3
|
-
const normalizeName = name => (typeof name === 'string' && name.includes('.') ? name.split('.').pop() : name);
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
import { detectTreeTable, mapTreeTables } from './tree-detection.mjs';
|
|
2
|
+
|
|
3
|
+
const normalizeName = name => (typeof name === 'string' && name.includes('.') ? name.split('.').pop() : name);
|
|
4
|
+
const normalizeTableColumns = columns =>
|
|
5
|
+
(Array.isArray(columns) ? columns : []).filter(
|
|
6
|
+
col => col && typeof col.name === 'string' && col.name.trim().length > 0
|
|
7
|
+
);
|
|
8
|
+
const normalizeTables = tables => (Array.isArray(tables) ? tables : []);
|
|
9
|
+
|
|
10
|
+
export const mapRelations = (tables, naming) => {
|
|
11
|
+
const relationMap = new Map();
|
|
12
|
+
const relationKeys = new Map();
|
|
13
|
+
const fkIndex = new Map();
|
|
14
|
+
const uniqueSingleColumns = new Map();
|
|
15
|
+
const normalizedTables = normalizeTables(tables).filter(
|
|
16
|
+
table => table && typeof table.name === 'string' && table.name.trim().length > 0
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
for (const table of normalizedTables) {
|
|
20
|
+
const tableColumns = normalizeTableColumns(table.columns);
|
|
21
|
+
relationMap.set(table.name, []);
|
|
22
|
+
relationKeys.set(table.name, new Set());
|
|
23
|
+
for (const col of tableColumns) {
|
|
24
|
+
if (col.references) {
|
|
25
|
+
const list = fkIndex.get(table.name) || [];
|
|
26
|
+
list.push(col);
|
|
27
|
+
fkIndex.set(table.name, list);
|
|
28
|
+
}
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
const uniqueCols = new Set();
|
|
23
|
-
if (Array.isArray(table.primaryKey) && table.primaryKey.length === 1) {
|
|
24
|
-
uniqueCols.add(table.primaryKey[0]);
|
|
25
|
-
}
|
|
26
|
-
for (const col of
|
|
27
|
-
if (col.unique) uniqueCols.add(col.name);
|
|
28
|
-
}
|
|
29
|
-
for (const idx of table.indexes || []) {
|
|
30
|
-
if (!idx?.unique) continue;
|
|
32
|
+
if (Array.isArray(table.primaryKey) && table.primaryKey.length === 1) {
|
|
33
|
+
uniqueCols.add(table.primaryKey[0]);
|
|
34
|
+
}
|
|
35
|
+
for (const col of tableColumns) {
|
|
36
|
+
if (col.unique) uniqueCols.add(col.name);
|
|
37
|
+
}
|
|
38
|
+
for (const idx of table.indexes || []) {
|
|
39
|
+
if (!idx?.unique) continue;
|
|
31
40
|
if (!Array.isArray(idx.columns) || idx.columns.length !== 1 || !idx.columns[0]?.column) continue;
|
|
32
41
|
const columnName = idx.columns[0].column;
|
|
33
42
|
if (idx.where) {
|
|
@@ -38,15 +47,15 @@ export const mapRelations = (tables, naming) => {
|
|
|
38
47
|
uniqueCols.add(columnName);
|
|
39
48
|
}
|
|
40
49
|
uniqueSingleColumns.set(table.name, uniqueCols);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const findTable = name => {
|
|
44
|
-
const norm = normalizeName(name);
|
|
45
|
-
return
|
|
46
|
-
};
|
|
47
|
-
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const findTable = name => {
|
|
53
|
+
const norm = normalizeName(name);
|
|
54
|
+
return normalizedTables.find(t => t.name === name || t.name === norm);
|
|
55
|
+
};
|
|
56
|
+
|
|
48
57
|
const pivotTables = new Set();
|
|
49
|
-
for (const table of
|
|
58
|
+
for (const table of normalizedTables) {
|
|
50
59
|
const fkCols = fkIndex.get(table.name) || [];
|
|
51
60
|
const hasSelfReference = fkCols.some(c => normalizeName(c.references.table) === table.name);
|
|
52
61
|
const distinctTargets = Array.from(new Set(fkCols.map(c => normalizeName(c.references.table))));
|
|
@@ -84,11 +93,11 @@ export const mapRelations = (tables, naming) => {
|
|
|
84
93
|
}
|
|
85
94
|
}
|
|
86
95
|
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
for (const table of
|
|
90
|
-
const fkCols = fkIndex.get(table.name) || [];
|
|
91
|
-
for (const fk of fkCols) {
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const table of normalizedTables) {
|
|
99
|
+
const fkCols = fkIndex.get(table.name) || [];
|
|
100
|
+
for (const fk of fkCols) {
|
|
92
101
|
const targetTable = fk.references.table;
|
|
93
102
|
const targetKey = normalizeName(targetTable);
|
|
94
103
|
const belongsKey = relationKeys.get(table.name);
|
|
@@ -130,39 +139,44 @@ export const mapRelations = (tables, naming) => {
|
|
|
130
139
|
return relationMap;
|
|
131
140
|
};
|
|
132
141
|
|
|
133
|
-
export const buildSchemaMetadata = (schema, naming) => {
|
|
134
|
-
const tables = schema
|
|
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
|
-
|
|
142
|
+
export const buildSchemaMetadata = (schema, naming) => {
|
|
143
|
+
const tables = normalizeTables(schema?.tables)
|
|
144
|
+
.filter(t => t && typeof t.name === 'string' && t.name.trim().length > 0)
|
|
145
|
+
.map(t => {
|
|
146
|
+
const columns = normalizeTableColumns(t.columns).map(col => ({ ...col }));
|
|
147
|
+
const indexes = Array.isArray(t.indexes) ? t.indexes.map(idx => ({ ...idx })) : [];
|
|
148
|
+
const uniqueSingleColumns = new Set(
|
|
149
|
+
indexes
|
|
150
|
+
.filter(idx => idx?.unique && !idx?.where && Array.isArray(idx.columns) && idx.columns.length === 1)
|
|
151
|
+
.map(idx => idx.columns[0]?.column)
|
|
152
|
+
.filter(Boolean)
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
name: t.name,
|
|
157
|
+
schema: t.schema,
|
|
158
|
+
columns: columns.map(col => {
|
|
159
|
+
const unique = col.unique !== undefined ? col.unique : uniqueSingleColumns.has(col.name) ? true : undefined;
|
|
160
|
+
return { ...col, unique };
|
|
161
|
+
}),
|
|
162
|
+
primaryKey: t.primaryKey || [],
|
|
163
|
+
indexes,
|
|
164
|
+
isView: false
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const views = normalizeTables(schema?.views)
|
|
169
|
+
.filter(v => v && typeof v.name === 'string' && v.name.trim().length > 0)
|
|
170
|
+
.map(v => ({
|
|
171
|
+
name: v.name,
|
|
172
|
+
schema: v.schema,
|
|
173
|
+
columns: normalizeTableColumns(v.columns).map(col => ({ ...col })),
|
|
174
|
+
primaryKey: [],
|
|
175
|
+
indexes: [],
|
|
176
|
+
isView: true,
|
|
177
|
+
definition: v.definition,
|
|
178
|
+
comment: v.comment
|
|
179
|
+
}));
|
|
166
180
|
|
|
167
181
|
const allEntities = [...tables, ...views];
|
|
168
182
|
|
|
@@ -16,38 +16,40 @@
|
|
|
16
16
|
* @param {Object} naming - Naming strategy instance
|
|
17
17
|
* @returns {Object|null} Tree configuration if detected, null otherwise
|
|
18
18
|
*/
|
|
19
|
-
export const detectTreeTable = (table, naming) => {
|
|
20
|
-
const columns = table.columns
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
19
|
+
export const detectTreeTable = (table, naming) => {
|
|
20
|
+
const columns = Array.isArray(table?.columns) ? table.columns : [];
|
|
21
|
+
const getColumnName = column =>
|
|
22
|
+
column && typeof column.name === 'string' && column.name.trim().length > 0 ? column.name : undefined;
|
|
23
|
+
|
|
24
|
+
// Common patterns for tree columns
|
|
25
|
+
const parentPatterns = ['parentId', 'parent_id', 'parent'];
|
|
26
|
+
const leftPatterns = ['lft', 'left', 'leftValue', 'left_value'];
|
|
27
|
+
const rightPatterns = ['rght', 'right', 'rightValue', 'right_value'];
|
|
28
|
+
const depthPatterns = ['depth', 'level', 'treeLevel', 'tree_level', 'cod_nivel'];
|
|
29
|
+
|
|
30
|
+
const matchesTreePattern = (column, patterns, term) => {
|
|
31
|
+
const name = getColumnName(column);
|
|
32
|
+
if (!name) return false;
|
|
33
|
+
return patterns.includes(name) || name.toLowerCase().includes(term);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Find matching columns
|
|
37
|
+
const parentCol = columns.find(c => matchesTreePattern(c, parentPatterns, 'parent'));
|
|
38
|
+
|
|
39
|
+
const leftCol = columns.find(c => matchesTreePattern(c, leftPatterns, 'left'));
|
|
40
|
+
|
|
41
|
+
const rightCol = columns.find(c => matchesTreePattern(c, rightPatterns, 'right'));
|
|
42
|
+
|
|
43
|
+
const depthCol = columns.find(c => {
|
|
44
|
+
const name = getColumnName(c);
|
|
45
|
+
if (!name) return false;
|
|
46
|
+
return (
|
|
47
|
+
depthPatterns.includes(name) ||
|
|
48
|
+
name.toLowerCase().includes('depth') ||
|
|
49
|
+
name.toLowerCase().includes('level') ||
|
|
50
|
+
name.toLowerCase().includes('nivel')
|
|
51
|
+
);
|
|
52
|
+
});
|
|
51
53
|
|
|
52
54
|
// Validate: must have parent, left, and right columns
|
|
53
55
|
if (!parentCol || !leftCol || !rightCol) {
|
|
@@ -86,25 +88,29 @@ export const detectTreeTable = (table, naming) => {
|
|
|
86
88
|
config.depthKey = depthCol.name;
|
|
87
89
|
}
|
|
88
90
|
|
|
89
|
-
// Detect scope columns (columns that might be used for multi-tree scoping)
|
|
90
|
-
const scopeColumns = columns.filter(c => {
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
// Skip if it
|
|
101
|
-
if (
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
91
|
+
// Detect scope columns (columns that might be used for multi-tree scoping)
|
|
92
|
+
const scopeColumns = columns.filter(c => {
|
|
93
|
+
const name = getColumnName(c);
|
|
94
|
+
if (!name) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
// Skip tree columns and primary key
|
|
98
|
+
if (name === config.parentKey || name === config.leftKey ||
|
|
99
|
+
name === config.rightKey || name === config.depthKey) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
// Skip if it's the primary key
|
|
103
|
+
if (table.primaryKey?.includes(name)) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
// Skip if it has a foreign key to another table
|
|
107
|
+
if (c.references && c.references.table !== table.name) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
// Look for columns that might be scope columns (e.g., tenantId, organizationId)
|
|
111
|
+
const scopePatterns = ['tenant', 'organization', 'company', 'site', 'workspace'];
|
|
112
|
+
return scopePatterns.some(pattern => name.toLowerCase().includes(pattern));
|
|
113
|
+
}).map(c => getColumnName(c)).filter(Boolean);
|
|
108
114
|
|
|
109
115
|
if (scopeColumns.length > 0) {
|
|
110
116
|
config.scope = scopeColumns;
|
|
@@ -119,15 +125,15 @@ export const detectTreeTable = (table, naming) => {
|
|
|
119
125
|
* @param {Object} naming - Naming strategy instance
|
|
120
126
|
* @returns {Map} Map of table name to tree configuration
|
|
121
127
|
*/
|
|
122
|
-
export const mapTreeTables = (tables, naming) => {
|
|
123
|
-
const treeMap = new Map();
|
|
124
|
-
|
|
125
|
-
for (const table of tables) {
|
|
126
|
-
const treeConfig = detectTreeTable(table, naming);
|
|
127
|
-
if (treeConfig) {
|
|
128
|
-
treeMap.set(table.name, treeConfig);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
128
|
+
export const mapTreeTables = (tables, naming) => {
|
|
129
|
+
const treeMap = new Map();
|
|
130
|
+
|
|
131
|
+
for (const table of Array.isArray(tables) ? tables : []) {
|
|
132
|
+
const treeConfig = detectTreeTable(table, naming);
|
|
133
|
+
if (treeConfig && table && typeof table.name === 'string' && table.name.trim().length > 0) {
|
|
134
|
+
treeMap.set(table.name, treeConfig);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
131
137
|
|
|
132
138
|
return treeMap;
|
|
133
139
|
};
|
|
@@ -150,4 +156,4 @@ export const getTreeConfig = (tableName, treeMap) => {
|
|
|
150
156
|
*/
|
|
151
157
|
export const isTreeTable = (tableName, treeMap) => {
|
|
152
158
|
return treeMap.has(tableName);
|
|
153
|
-
};
|
|
159
|
+
};
|
|
@@ -8,9 +8,7 @@ import type { ValueOperandInput } from '../core/ast/expression.js';
|
|
|
8
8
|
import type { ExpressionNode } from '../core/ast/expression-nodes.js';
|
|
9
9
|
import { BulkBaseExecutor, type BulkExecutorOptions } from './bulk-executor.base.js';
|
|
10
10
|
import { createBulkExecutionContext, executeCompiled } from './bulk-context.js';
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
const DEFAULT_CHUNK_SIZE = 1000;
|
|
11
|
+
import { maybeTransaction } from './bulk-utils.js';
|
|
14
12
|
|
|
15
13
|
interface DeleteExecutorOptions extends BulkExecutorOptions {
|
|
16
14
|
by?: string;
|
|
@@ -30,7 +28,7 @@ export class BulkDeleteExecutor extends BulkBaseExecutor<DeleteExecutorOptions>
|
|
|
30
28
|
this.byColumnName = options.by ?? findPrimaryKey(table);
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
protected async executeChunk(chunk: ValueOperandInput[],
|
|
31
|
+
protected async executeChunk(chunk: ValueOperandInput[], _chunkIndex: number): Promise<ChunkOutcome> {
|
|
34
32
|
const byColumn = this.table.columns[this.byColumnName];
|
|
35
33
|
if (!byColumn) {
|
|
36
34
|
throw new Error(
|
|
@@ -39,7 +37,7 @@ export class BulkDeleteExecutor extends BulkBaseExecutor<DeleteExecutorOptions>
|
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
const extraWhere = this.options.where;
|
|
42
|
-
const inExpr = inList(byColumn, chunk as
|
|
40
|
+
const inExpr = inList(byColumn, chunk as unknown as Parameters<typeof inList>[1]);
|
|
43
41
|
const finalWhere = extraWhere ? and(inExpr, extraWhere) : inExpr;
|
|
44
42
|
|
|
45
43
|
const builder = new DeleteQueryBuilder(this.table).where(finalWhere as ExpressionNode);
|
|
@@ -4,6 +4,7 @@ import type { OrmSession } from '../orm/orm-session.js';
|
|
|
4
4
|
import type { BulkInsertOptions, ChunkOutcome, InsertRow } from './bulk-types.js';
|
|
5
5
|
import { BulkBaseExecutor, type BulkExecutorOptions } from './bulk-executor.base.js';
|
|
6
6
|
import { resolveReturningColumns, flattenQueryResults, executeCompiled } from './bulk-context.js';
|
|
7
|
+
import type { ValueOperandInput } from '../core/ast/expression.js';
|
|
7
8
|
|
|
8
9
|
interface InsertExecutorOptions extends BulkExecutorOptions {
|
|
9
10
|
returning?: boolean | import('../schema/column-types.js').ColumnDef[];
|
|
@@ -20,25 +21,24 @@ export class BulkInsertExecutor extends BulkBaseExecutor<InsertExecutorOptions>
|
|
|
20
21
|
super(session, table, rows, options);
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
protected async executeChunk(chunk: InsertRow[],
|
|
24
|
+
protected async executeChunk(chunk: InsertRow[], _chunkIndex: number): Promise<ChunkOutcome> {
|
|
24
25
|
const returningColumns = resolveReturningColumns(this.ctx, this.table, this.options.returning);
|
|
25
26
|
|
|
26
|
-
let builder: InsertQueryBuilder<unknown> | ConflictBuilder<unknown> =
|
|
27
|
-
new InsertQueryBuilder(this.table).values(chunk);
|
|
27
|
+
let builder: InsertQueryBuilder<unknown> | ConflictBuilder<unknown> = new InsertQueryBuilder(this.table).values(chunk);
|
|
28
28
|
|
|
29
29
|
if (this.options.onConflict) {
|
|
30
30
|
const conflictColumns = this.options.onConflict.target?.columns ?? [];
|
|
31
|
-
|
|
31
|
+
const conflictBuilder = (builder as InsertQueryBuilder<unknown>).onConflict(conflictColumns);
|
|
32
32
|
|
|
33
33
|
if (this.options.onConflict.action.type === 'DoNothing') {
|
|
34
|
-
builder =
|
|
34
|
+
builder = conflictBuilder.doNothing();
|
|
35
35
|
} else if (this.options.onConflict.action.type === 'DoUpdate' && this.options.onConflict.action.set) {
|
|
36
|
-
const setMap: Record<string,
|
|
36
|
+
const setMap: Record<string, ValueOperandInput> = {};
|
|
37
37
|
for (const assignment of this.options.onConflict.action.set) {
|
|
38
38
|
const colName = typeof assignment.column === 'object' ? assignment.column.name : assignment.column;
|
|
39
39
|
setMap[colName] = assignment.value;
|
|
40
40
|
}
|
|
41
|
-
builder =
|
|
41
|
+
builder = conflictBuilder.doUpdate(setMap);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -34,7 +34,7 @@ export class BulkUpdateExecutor extends BulkBaseExecutor<UpdateExecutorOptions>
|
|
|
34
34
|
this.byColumns = resolveByColumns(table, options.by);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
protected async executeChunk(chunk: UpdateRow[],
|
|
37
|
+
protected async executeChunk(chunk: UpdateRow[], _chunkIndex: number): Promise<ChunkOutcome> {
|
|
38
38
|
const allReturning: Record<string, unknown>[] = [];
|
|
39
39
|
const returningColumns = resolveReturningColumns(this.ctx, this.table, this.options.returning);
|
|
40
40
|
const extraWhere = this.options.where;
|
|
@@ -152,7 +152,7 @@ export async function bulkUpdateWhere<TTable extends TableDef>(
|
|
|
152
152
|
const buildTask = (chunk: ValueOperandInput[], chunkIndex: number) => async (): Promise<ChunkOutcome> => {
|
|
153
153
|
return runChunk(
|
|
154
154
|
async () => {
|
|
155
|
-
const inExpr = inList(byColumn, chunk as
|
|
155
|
+
const inExpr = inList(byColumn, chunk as unknown as Parameters<typeof inList>[1]);
|
|
156
156
|
const finalWhere = extraWhere ? and(inExpr, extraWhere) : inExpr;
|
|
157
157
|
|
|
158
158
|
let builder = new UpdateQueryBuilder(table).set(set as Record<string, unknown>).where(finalWhere);
|
|
@@ -3,12 +3,10 @@ import { findPrimaryKey } from '../query-builder/hydration-planner.js';
|
|
|
3
3
|
import type { TableDef } from '../schema/table.js';
|
|
4
4
|
import type { OrmSession } from '../orm/orm-session.js';
|
|
5
5
|
import type { BulkUpsertOptions, ChunkOutcome, InsertRow } from './bulk-types.js';
|
|
6
|
-
import type { UpsertClause } from '../core/ast/query.js';
|
|
7
6
|
import type { ValueOperandInput } from '../core/ast/expression.js';
|
|
8
7
|
import type { ColumnNode } from '../core/ast/expression.js';
|
|
9
8
|
import { BulkBaseExecutor, type BulkExecutorOptions } from './bulk-executor.base.js';
|
|
10
|
-
import { resolveReturningColumns, flattenQueryResults, executeCompiled
|
|
11
|
-
import { splitIntoChunks, runWithConcurrency, runChunk, maybeTransaction, aggregateOutcomes, aggregateOutcomesWithTimings } from './bulk-utils.js';
|
|
9
|
+
import { resolveReturningColumns, flattenQueryResults, executeCompiled } from './bulk-context.js';
|
|
12
10
|
|
|
13
11
|
interface UpsertExecutorOptions extends BulkExecutorOptions {
|
|
14
12
|
conflictColumns?: string[];
|
|
@@ -44,22 +42,22 @@ export class BulkUpsertExecutor extends BulkBaseExecutor<UpsertExecutorOptions>
|
|
|
44
42
|
Object.keys(rows[0] ?? {}).filter(col => !conflictSet.has(col) && col in table.columns);
|
|
45
43
|
}
|
|
46
44
|
|
|
47
|
-
protected async executeChunk(chunk: InsertRow[],
|
|
45
|
+
protected async executeChunk(chunk: InsertRow[], _chunkIndex: number): Promise<ChunkOutcome> {
|
|
48
46
|
const returningColumns = resolveReturningColumns(this.ctx, this.table, this.options.returning);
|
|
49
47
|
|
|
50
48
|
const set: Record<string, ValueOperandInput> = {};
|
|
51
49
|
for (const col of this.updateColumns) {
|
|
52
|
-
set[col] = { type: 'ExcludedColumn', name: col } as
|
|
50
|
+
set[col] = { type: 'ExcludedColumn', name: col } as unknown as ValueOperandInput;
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
let builder: InsertQueryBuilder<unknown>;
|
|
56
54
|
|
|
57
55
|
if (this.updateColumns.length === 0) {
|
|
58
|
-
builder = new InsertQueryBuilder(this.table).values(chunk).onConflict(this.conflictTargetNodes
|
|
56
|
+
builder = new InsertQueryBuilder(this.table).values(chunk).onConflict(this.conflictTargetNodes).doNothing();
|
|
59
57
|
} else {
|
|
60
58
|
builder = new InsertQueryBuilder(this.table)
|
|
61
59
|
.values(chunk)
|
|
62
|
-
.onConflict(this.conflictTargetNodes
|
|
60
|
+
.onConflict(this.conflictTargetNodes)
|
|
63
61
|
.doUpdate(set);
|
|
64
62
|
}
|
|
65
63
|
|