metal-orm 1.0.64 → 1.0.65
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 +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -59
- package/dist/index.d.ts +61 -59
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/ast/aggregate-functions.ts +1 -1
- package/src/core/ast/window-functions.ts +6 -6
- package/src/core/functions/array.ts +4 -4
- package/src/core/functions/control-flow.ts +6 -6
- package/src/core/functions/json.ts +6 -6
- package/src/index.ts +2 -1
- package/src/orm/entity-context.ts +9 -7
- package/src/orm/entity-relations.ts +207 -207
- package/src/orm/entity.ts +124 -124
- package/src/orm/execute.ts +166 -166
- package/src/orm/identity-map.ts +1 -1
- package/src/orm/lazy-batch/shared.ts +1 -1
- package/src/orm/orm-session.ts +54 -54
- package/src/orm/relation-change-processor.ts +3 -3
- package/src/orm/relations/has-many.ts +1 -1
- package/src/orm/runtime-types.ts +5 -5
- package/src/orm/save-graph.ts +161 -161
- package/src/orm/unit-of-work.ts +58 -56
- package/src/query-builder/insert-query-state.ts +156 -155
- package/src/query-builder/insert.ts +5 -2
- package/src/query-builder/select.ts +112 -111
- package/src/schema/column-types.ts +14 -14
- package/src/schema/table.ts +39 -31
- package/src/schema/types.ts +54 -54
package/src/orm/unit-of-work.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ColumnNode, eq } from '../core/ast/expression.js';
|
|
1
|
+
import { ColumnNode, eq } from '../core/ast/expression.js';
|
|
2
|
+
import type { ValueOperandInput } from '../core/ast/expression.js';
|
|
2
3
|
import type { Dialect, CompiledQuery } from '../core/dialect/abstract.js';
|
|
3
4
|
import { InsertQueryBuilder } from '../query-builder/insert.js';
|
|
4
5
|
import { UpdateQueryBuilder } from '../query-builder/update.js';
|
|
@@ -13,8 +14,8 @@ import type { TrackedEntity } from './runtime-types.js';
|
|
|
13
14
|
/**
|
|
14
15
|
* Unit of Work pattern implementation for tracking entity changes.
|
|
15
16
|
*/
|
|
16
|
-
export class UnitOfWork {
|
|
17
|
-
private readonly trackedEntities = new Map<
|
|
17
|
+
export class UnitOfWork {
|
|
18
|
+
private readonly trackedEntities = new Map<object, TrackedEntity>();
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Creates a new UnitOfWork instance.
|
|
@@ -51,9 +52,9 @@ export class UnitOfWork {
|
|
|
51
52
|
* @param pk - The primary key value
|
|
52
53
|
* @returns The entity or undefined if not found
|
|
53
54
|
*/
|
|
54
|
-
getEntity(table: TableDef, pk: string | number):
|
|
55
|
-
return this.identityMap.getEntity(table, pk);
|
|
56
|
-
}
|
|
55
|
+
getEntity(table: TableDef, pk: string | number): object | undefined {
|
|
56
|
+
return this.identityMap.getEntity(table, pk);
|
|
57
|
+
}
|
|
57
58
|
|
|
58
59
|
/**
|
|
59
60
|
* Gets all tracked entities for a specific table.
|
|
@@ -69,9 +70,9 @@ export class UnitOfWork {
|
|
|
69
70
|
* @param entity - The entity to find
|
|
70
71
|
* @returns The tracked entity or undefined if not found
|
|
71
72
|
*/
|
|
72
|
-
findTracked(entity:
|
|
73
|
-
return this.trackedEntities.get(entity);
|
|
74
|
-
}
|
|
73
|
+
findTracked(entity: object): TrackedEntity | undefined {
|
|
74
|
+
return this.trackedEntities.get(entity);
|
|
75
|
+
}
|
|
75
76
|
|
|
76
77
|
/**
|
|
77
78
|
* Sets an entity in the identity map.
|
|
@@ -79,9 +80,9 @@ export class UnitOfWork {
|
|
|
79
80
|
* @param pk - The primary key value
|
|
80
81
|
* @param entity - The entity instance
|
|
81
82
|
*/
|
|
82
|
-
setEntity(table: TableDef, pk: string | number, entity:
|
|
83
|
-
if (pk === null || pk === undefined) return;
|
|
84
|
-
let tracked = this.trackedEntities.get(entity);
|
|
83
|
+
setEntity(table: TableDef, pk: string | number, entity: object): void {
|
|
84
|
+
if (pk === null || pk === undefined) return;
|
|
85
|
+
let tracked = this.trackedEntities.get(entity);
|
|
85
86
|
if (!tracked) {
|
|
86
87
|
tracked = {
|
|
87
88
|
table,
|
|
@@ -104,7 +105,7 @@ export class UnitOfWork {
|
|
|
104
105
|
* @param entity - The entity instance
|
|
105
106
|
* @param pk - Optional primary key value
|
|
106
107
|
*/
|
|
107
|
-
trackNew(table: TableDef, entity:
|
|
108
|
+
trackNew(table: TableDef, entity: object, pk?: string | number): void {
|
|
108
109
|
const tracked: TrackedEntity = {
|
|
109
110
|
table,
|
|
110
111
|
entity,
|
|
@@ -124,7 +125,7 @@ export class UnitOfWork {
|
|
|
124
125
|
* @param pk - The primary key value
|
|
125
126
|
* @param entity - The entity instance
|
|
126
127
|
*/
|
|
127
|
-
trackManaged(table: TableDef, pk: string | number, entity:
|
|
128
|
+
trackManaged(table: TableDef, pk: string | number, entity: object): void {
|
|
128
129
|
const tracked: TrackedEntity = {
|
|
129
130
|
table,
|
|
130
131
|
entity,
|
|
@@ -140,22 +141,22 @@ export class UnitOfWork {
|
|
|
140
141
|
* Marks an entity as dirty (modified).
|
|
141
142
|
* @param entity - The entity to mark as dirty
|
|
142
143
|
*/
|
|
143
|
-
markDirty(entity:
|
|
144
|
-
const tracked = this.trackedEntities.get(entity);
|
|
145
|
-
if (!tracked) return;
|
|
146
|
-
if (tracked.status === EntityStatus.New || tracked.status === EntityStatus.Removed) return;
|
|
147
|
-
tracked.status = EntityStatus.Dirty;
|
|
148
|
-
}
|
|
144
|
+
markDirty(entity: object): void {
|
|
145
|
+
const tracked = this.trackedEntities.get(entity);
|
|
146
|
+
if (!tracked) return;
|
|
147
|
+
if (tracked.status === EntityStatus.New || tracked.status === EntityStatus.Removed) return;
|
|
148
|
+
tracked.status = EntityStatus.Dirty;
|
|
149
|
+
}
|
|
149
150
|
|
|
150
151
|
/**
|
|
151
152
|
* Marks an entity as removed.
|
|
152
153
|
* @param entity - The entity to mark as removed
|
|
153
154
|
*/
|
|
154
|
-
markRemoved(entity:
|
|
155
|
-
const tracked = this.trackedEntities.get(entity);
|
|
156
|
-
if (!tracked) return;
|
|
157
|
-
tracked.status = EntityStatus.Removed;
|
|
158
|
-
}
|
|
155
|
+
markRemoved(entity: object): void {
|
|
156
|
+
const tracked = this.trackedEntities.get(entity);
|
|
157
|
+
if (!tracked) return;
|
|
158
|
+
tracked.status = EntityStatus.Removed;
|
|
159
|
+
}
|
|
159
160
|
|
|
160
161
|
/**
|
|
161
162
|
* Flushes pending changes to the database.
|
|
@@ -194,8 +195,8 @@ export class UnitOfWork {
|
|
|
194
195
|
private async flushInsert(tracked: TrackedEntity): Promise<void> {
|
|
195
196
|
await this.runHook(tracked.table.hooks?.beforeInsert, tracked);
|
|
196
197
|
|
|
197
|
-
const payload = this.extractColumns(tracked.table, tracked.entity as Record<string, unknown>);
|
|
198
|
-
let builder = new InsertQueryBuilder(tracked.table).values(payload);
|
|
198
|
+
const payload = this.extractColumns(tracked.table, tracked.entity as Record<string, unknown>);
|
|
199
|
+
let builder = new InsertQueryBuilder(tracked.table).values(payload as Record<string, ValueOperandInput>);
|
|
199
200
|
if (this.dialect.supportsReturning()) {
|
|
200
201
|
builder = builder.returning(...this.getReturningColumns(tracked.table));
|
|
201
202
|
}
|
|
@@ -287,16 +288,16 @@ export class UnitOfWork {
|
|
|
287
288
|
* @param tracked - The tracked entity
|
|
288
289
|
* @returns Object with changed column values
|
|
289
290
|
*/
|
|
290
|
-
private computeChanges(tracked: TrackedEntity): Record<string, unknown> {
|
|
291
|
-
const snapshot = tracked.original ?? {};
|
|
292
|
-
const changes: Record<string, unknown> = {};
|
|
293
|
-
for (const column of Object.keys(tracked.table.columns)) {
|
|
294
|
-
const current = tracked.entity[column];
|
|
295
|
-
if (snapshot[column] !== current) {
|
|
296
|
-
changes[column] = current;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return changes;
|
|
291
|
+
private computeChanges(tracked: TrackedEntity): Record<string, unknown> {
|
|
292
|
+
const snapshot = tracked.original ?? {};
|
|
293
|
+
const changes: Record<string, unknown> = {};
|
|
294
|
+
for (const column of Object.keys(tracked.table.columns)) {
|
|
295
|
+
const current = (tracked.entity as Record<string, unknown>)[column];
|
|
296
|
+
if (snapshot[column] !== current) {
|
|
297
|
+
changes[column] = current;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return changes;
|
|
300
301
|
}
|
|
301
302
|
|
|
302
303
|
/**
|
|
@@ -342,18 +343,18 @@ export class UnitOfWork {
|
|
|
342
343
|
* @param tracked - The tracked entity
|
|
343
344
|
* @param results - Query results
|
|
344
345
|
*/
|
|
345
|
-
private applyReturningResults(tracked: TrackedEntity, results: QueryResult[]): void {
|
|
346
|
-
if (!this.dialect.supportsReturning()) return;
|
|
347
|
-
const first = results[0];
|
|
348
|
-
if (!first || first.values.length === 0) return;
|
|
349
|
-
|
|
350
|
-
const row = first.values[0];
|
|
351
|
-
for (let i = 0; i < first.columns.length; i++) {
|
|
352
|
-
const columnName = this.normalizeColumnName(first.columns[i]);
|
|
353
|
-
if (!(columnName in tracked.table.columns)) continue;
|
|
354
|
-
tracked.entity[columnName] = row[i];
|
|
355
|
-
}
|
|
356
|
-
}
|
|
346
|
+
private applyReturningResults(tracked: TrackedEntity, results: QueryResult[]): void {
|
|
347
|
+
if (!this.dialect.supportsReturning()) return;
|
|
348
|
+
const first = results[0];
|
|
349
|
+
if (!first || first.values.length === 0) return;
|
|
350
|
+
|
|
351
|
+
const row = first.values[0];
|
|
352
|
+
for (let i = 0; i < first.columns.length; i++) {
|
|
353
|
+
const columnName = this.normalizeColumnName(first.columns[i]);
|
|
354
|
+
if (!(columnName in tracked.table.columns)) continue;
|
|
355
|
+
(tracked.entity as Record<string, unknown>)[columnName] = row[i];
|
|
356
|
+
}
|
|
357
|
+
}
|
|
357
358
|
|
|
358
359
|
/**
|
|
359
360
|
* Normalizes a column name by removing quotes and table prefixes.
|
|
@@ -394,10 +395,11 @@ export class UnitOfWork {
|
|
|
394
395
|
* @param tracked - The tracked entity
|
|
395
396
|
* @returns Primary key value or null
|
|
396
397
|
*/
|
|
397
|
-
private getPrimaryKeyValue(tracked: TrackedEntity): string | number | null {
|
|
398
|
-
const key = findPrimaryKey(tracked.table);
|
|
399
|
-
const val = tracked.entity[key];
|
|
400
|
-
if (val === undefined || val === null) return null;
|
|
401
|
-
return
|
|
402
|
-
|
|
403
|
-
}
|
|
398
|
+
private getPrimaryKeyValue(tracked: TrackedEntity): string | number | null {
|
|
399
|
+
const key = findPrimaryKey(tracked.table);
|
|
400
|
+
const val = (tracked.entity as Record<string, unknown>)[key];
|
|
401
|
+
if (val === undefined || val === null) return null;
|
|
402
|
+
if (typeof val !== 'string' && typeof val !== 'number') return null;
|
|
403
|
+
return val;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
@@ -1,161 +1,162 @@
|
|
|
1
|
-
import { TableDef } from '../schema/table.js';
|
|
2
|
-
import { InsertQueryNode, SelectQueryNode } from '../core/ast/query.js';
|
|
1
|
+
import { TableDef } from '../schema/table.js';
|
|
2
|
+
import { InsertQueryNode, SelectQueryNode } from '../core/ast/query.js';
|
|
3
3
|
import {
|
|
4
4
|
ColumnNode,
|
|
5
5
|
OperandNode,
|
|
6
6
|
isValueOperandInput,
|
|
7
7
|
valueToOperand
|
|
8
8
|
} from '../core/ast/expression.js';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
public readonly
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
*
|
|
26
|
-
* @param
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
*
|
|
66
|
-
* @
|
|
67
|
-
* @
|
|
68
|
-
* @throws Error if
|
|
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
|
-
return this
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
*
|
|
119
|
-
* @param
|
|
120
|
-
* @
|
|
121
|
-
* @
|
|
122
|
-
* @throws Error if
|
|
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
|
-
}
|
|
9
|
+
import type { ValueOperandInput } from '../core/ast/expression.js';
|
|
10
|
+
import {
|
|
11
|
+
buildColumnNodes,
|
|
12
|
+
createTableNode
|
|
13
|
+
} from '../core/ast/builders.js';
|
|
14
|
+
|
|
15
|
+
type InsertRows = Record<string, ValueOperandInput>[];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Maintains immutable state for building INSERT queries
|
|
19
|
+
*/
|
|
20
|
+
export class InsertQueryState {
|
|
21
|
+
public readonly table: TableDef;
|
|
22
|
+
public readonly ast: InsertQueryNode;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Creates a new InsertQueryState instance
|
|
26
|
+
* @param table - The table definition for the INSERT query
|
|
27
|
+
* @param ast - Optional initial AST node, defaults to a basic INSERT query
|
|
28
|
+
*/
|
|
29
|
+
constructor(table: TableDef, ast?: InsertQueryNode) {
|
|
30
|
+
this.table = table;
|
|
31
|
+
this.ast = ast ?? {
|
|
32
|
+
type: 'InsertQuery',
|
|
33
|
+
into: createTableNode(table),
|
|
34
|
+
columns: [],
|
|
35
|
+
source: {
|
|
36
|
+
type: 'InsertValues',
|
|
37
|
+
rows: []
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private clone(nextAst: InsertQueryNode): InsertQueryState {
|
|
43
|
+
return new InsertQueryState(this.table, nextAst);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private ensureColumnsFromRow(rows: InsertRows): ColumnNode[] {
|
|
47
|
+
if (this.ast.columns.length) return this.ast.columns;
|
|
48
|
+
return buildColumnNodes(this.table, Object.keys(rows[0]));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private appendValues(rows: OperandNode[][]): OperandNode[][] {
|
|
52
|
+
if (this.ast.source.type === 'InsertValues') {
|
|
53
|
+
return [...this.ast.source.rows, ...rows];
|
|
54
|
+
}
|
|
55
|
+
return rows;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private getTableColumns(): ColumnNode[] {
|
|
59
|
+
const names = Object.keys(this.table.columns);
|
|
60
|
+
if (!names.length) return [];
|
|
61
|
+
return buildColumnNodes(this.table, names);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Adds VALUES clause to the INSERT query
|
|
66
|
+
* @param rows - Array of row objects to insert
|
|
67
|
+
* @returns A new InsertQueryState with the VALUES clause added
|
|
68
|
+
* @throws Error if mixing VALUES with SELECT source
|
|
69
|
+
* @throws Error if invalid values are provided
|
|
70
|
+
*/
|
|
71
|
+
withValues(rows: InsertRows): InsertQueryState {
|
|
72
|
+
if (!rows.length) return this;
|
|
73
|
+
|
|
74
|
+
if (this.ast.source.type === 'InsertSelect') {
|
|
75
|
+
throw new Error('Cannot mix INSERT ... VALUES with INSERT ... SELECT source.');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const definedColumns = this.ensureColumnsFromRow(rows);
|
|
79
|
+
|
|
80
|
+
const newRows: OperandNode[][] = rows.map((row, rowIndex) =>
|
|
81
|
+
definedColumns.map(column => {
|
|
82
|
+
const rawValue = row[column.name];
|
|
83
|
+
|
|
84
|
+
if (!isValueOperandInput(rawValue)) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Invalid insert value for column "${column.name}" in row ${rowIndex}: only primitives, null, or OperandNodes are allowed`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return valueToOperand(rawValue);
|
|
91
|
+
})
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
return this.clone({
|
|
95
|
+
...this.ast,
|
|
96
|
+
columns: definedColumns,
|
|
97
|
+
source: {
|
|
98
|
+
type: 'InsertValues',
|
|
99
|
+
rows: this.appendValues(newRows)
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Sets the columns for the INSERT query
|
|
106
|
+
* @param columns - Column nodes to insert into
|
|
107
|
+
* @returns A new InsertQueryState with the specified columns
|
|
108
|
+
*/
|
|
109
|
+
withColumns(columns: ColumnNode[]): InsertQueryState {
|
|
110
|
+
if (!columns.length) return this;
|
|
111
|
+
return this.clone({
|
|
112
|
+
...this.ast,
|
|
113
|
+
columns: [...columns]
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Adds SELECT source to the INSERT query
|
|
119
|
+
* @param query - The SELECT query to use as source
|
|
120
|
+
* @param columns - Target columns for the INSERT
|
|
121
|
+
* @returns A new InsertQueryState with the SELECT source
|
|
122
|
+
* @throws Error if mixing SELECT with VALUES source
|
|
123
|
+
* @throws Error if no destination columns specified
|
|
124
|
+
*/
|
|
125
|
+
withSelect(query: SelectQueryNode, columns: ColumnNode[]): InsertQueryState {
|
|
126
|
+
const targetColumns =
|
|
127
|
+
columns.length
|
|
128
|
+
? columns
|
|
129
|
+
: this.ast.columns.length
|
|
130
|
+
? this.ast.columns
|
|
131
|
+
: this.getTableColumns();
|
|
132
|
+
|
|
133
|
+
if (!targetColumns.length) {
|
|
134
|
+
throw new Error('INSERT ... SELECT requires specifying destination columns.');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (this.ast.source.type === 'InsertValues' && this.ast.source.rows.length) {
|
|
138
|
+
throw new Error('Cannot mix INSERT ... SELECT with INSERT ... VALUES source.');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return this.clone({
|
|
142
|
+
...this.ast,
|
|
143
|
+
columns: [...targetColumns],
|
|
144
|
+
source: {
|
|
145
|
+
type: 'InsertSelect',
|
|
146
|
+
query
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Adds a RETURNING clause to the INSERT query
|
|
153
|
+
* @param columns - Columns to return after insertion
|
|
154
|
+
* @returns A new InsertQueryState with the RETURNING clause added
|
|
155
|
+
*/
|
|
156
|
+
withReturning(columns: ColumnNode[]): InsertQueryState {
|
|
157
|
+
return this.clone({
|
|
158
|
+
...this.ast,
|
|
159
|
+
returning: [...columns]
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { SelectQueryBuilder } from './select.js';
|
|
2
2
|
import { TableDef } from '../schema/table.js';
|
|
3
3
|
import { ColumnDef } from '../schema/column-types.js';
|
|
4
|
-
import { ColumnNode } from '../core/ast/expression.js';
|
|
4
|
+
import { ColumnNode } from '../core/ast/expression.js';
|
|
5
|
+
import type { ValueOperandInput } from '../core/ast/expression.js';
|
|
5
6
|
import { CompiledQuery, InsertCompiler, Dialect } from '../core/dialect/abstract.js';
|
|
6
7
|
import { DialectKey, resolveDialectInput } from '../core/dialect/dialect-factory.js';
|
|
7
8
|
import { InsertQueryNode, SelectQueryNode } from '../core/ast/query.js';
|
|
@@ -36,7 +37,9 @@ export class InsertQueryBuilder<T> {
|
|
|
36
37
|
* @param rowOrRows - Single row object or array of row objects to insert
|
|
37
38
|
* @returns A new InsertQueryBuilder with the VALUES clause added
|
|
38
39
|
*/
|
|
39
|
-
values(
|
|
40
|
+
values(
|
|
41
|
+
rowOrRows: Record<string, ValueOperandInput> | Record<string, ValueOperandInput>[]
|
|
42
|
+
): InsertQueryBuilder<T> {
|
|
40
43
|
const rows = Array.isArray(rowOrRows) ? rowOrRows : [rowOrRows];
|
|
41
44
|
if (!rows.length) return this;
|
|
42
45
|
return this.clone(this.state.withValues(rows));
|