@dockstat/sqlite-wrapper 1.2.6 → 1.2.8
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/LICENSE +373 -373
- package/README.md +99 -66
- package/index.ts +858 -840
- package/package.json +54 -54
- package/query-builder/base.ts +221 -221
- package/query-builder/delete.ts +352 -352
- package/query-builder/index.ts +431 -431
- package/query-builder/insert.ts +249 -249
- package/query-builder/select.ts +358 -358
- package/query-builder/update.ts +278 -278
- package/query-builder/where.ts +307 -307
- package/types.ts +623 -623
package/query-builder/insert.ts
CHANGED
|
@@ -1,249 +1,249 @@
|
|
|
1
|
-
import type { SQLQueryBindings } from "bun:sqlite";
|
|
2
|
-
import type { InsertResult, InsertOptions } from "../types";
|
|
3
|
-
import { WhereQueryBuilder } from "./where";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Mixin class that adds INSERT functionality to the QueryBuilder.
|
|
7
|
-
* Handles single and bulk insert operations with conflict resolution.
|
|
8
|
-
*/
|
|
9
|
-
export class InsertQueryBuilder<
|
|
10
|
-
T extends Record<string, unknown>,
|
|
11
|
-
> extends WhereQueryBuilder<T> {
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Insert a single row or multiple rows into the table.
|
|
15
|
-
*
|
|
16
|
-
* @param data - Single object or array of objects to insert
|
|
17
|
-
* @param options - Insert options (OR IGNORE, OR REPLACE, etc.)
|
|
18
|
-
* @returns Insert result with insertId and changes count
|
|
19
|
-
*/
|
|
20
|
-
insert(
|
|
21
|
-
data: Partial<T> | Partial<T>[],
|
|
22
|
-
options?: InsertOptions,
|
|
23
|
-
): InsertResult {
|
|
24
|
-
this.getLogger().debug(`Building Data Array: ${data}`)
|
|
25
|
-
const rows = Array.isArray(data) ? data : [data];
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// Transform rows to handle JSON serialization
|
|
30
|
-
const transformedRows = rows.map((row) => this.transformRowToDb(row));
|
|
31
|
-
|
|
32
|
-
this.getLogger().debug(`Transformed row: ${JSON.stringify(transformedRows)}`)
|
|
33
|
-
|
|
34
|
-
if (transformedRows.length === 0) {
|
|
35
|
-
throw new Error("insert: data cannot be empty");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Get all unique columns from all rows
|
|
39
|
-
const allColumns = new Set<string>();
|
|
40
|
-
for (const row of transformedRows) {
|
|
41
|
-
for (const col of Object.keys(row)) {
|
|
42
|
-
allColumns.add(col);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const columns = Array.from(allColumns);
|
|
47
|
-
if (columns.length === 0) {
|
|
48
|
-
throw new Error("insert: no columns to insert");
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Build INSERT statement with conflict resolution
|
|
52
|
-
let insertType = "INSERT";
|
|
53
|
-
if (options?.orIgnore) insertType = "INSERT OR IGNORE";
|
|
54
|
-
else if (options?.orReplace) insertType = "INSERT OR REPLACE";
|
|
55
|
-
else if (options?.orAbort) insertType = "INSERT OR ABORT";
|
|
56
|
-
else if (options?.orFail) insertType = "INSERT OR FAIL";
|
|
57
|
-
else if (options?.orRollback) insertType = "INSERT OR ROLLBACK";
|
|
58
|
-
|
|
59
|
-
const quotedColumns = columns
|
|
60
|
-
.map((col) => this.quoteIdentifier(col))
|
|
61
|
-
.join(", ");
|
|
62
|
-
const placeholders = columns.map(() => "?").join(", ");
|
|
63
|
-
|
|
64
|
-
const query = `${insertType} INTO ${this.quoteIdentifier(this.getTableName())} (${quotedColumns}) VALUES (${placeholders})`;
|
|
65
|
-
const stmt = this.getDb().prepare(query);
|
|
66
|
-
|
|
67
|
-
let totalChanges = 0;
|
|
68
|
-
let lastInsertId = 0;
|
|
69
|
-
|
|
70
|
-
// Execute for each row
|
|
71
|
-
for (const row of transformedRows) {
|
|
72
|
-
const values = columns.map(
|
|
73
|
-
(col) => row[col as keyof typeof row] ?? null,
|
|
74
|
-
) as SQLQueryBindings[];
|
|
75
|
-
const result = stmt.run(...values);
|
|
76
|
-
totalChanges += result.changes;
|
|
77
|
-
if (result.lastInsertRowid) {
|
|
78
|
-
lastInsertId = Number(result.lastInsertRowid);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const result = {
|
|
83
|
-
insertId: lastInsertId,
|
|
84
|
-
changes: totalChanges,
|
|
85
|
-
};
|
|
86
|
-
this.reset();
|
|
87
|
-
return result;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Insert with OR IGNORE conflict resolution.
|
|
92
|
-
* Convenience method equivalent to insert(data, { orIgnore: true })
|
|
93
|
-
*
|
|
94
|
-
* @param data - Single object or array of objects to insert
|
|
95
|
-
* @returns Insert result with insertId and changes count
|
|
96
|
-
*/
|
|
97
|
-
insertOrIgnore(data: Partial<T> | Partial<T>[]): InsertResult {
|
|
98
|
-
return this.insert(data, { orIgnore: true });
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Insert with OR REPLACE conflict resolution.
|
|
103
|
-
* Convenience method equivalent to insert(data, { orReplace: true })
|
|
104
|
-
*
|
|
105
|
-
* @param data - Single object or array of objects to insert
|
|
106
|
-
* @returns Insert result with insertId and changes count
|
|
107
|
-
*/
|
|
108
|
-
insertOrReplace(data: Partial<T> | Partial<T>[]): InsertResult {
|
|
109
|
-
return this.insert(data, { orReplace: true });
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Insert with OR ABORT conflict resolution.
|
|
114
|
-
* This is the default behavior but provided for explicit usage.
|
|
115
|
-
*
|
|
116
|
-
* @param data - Single object or array of objects to insert
|
|
117
|
-
* @returns Insert result with insertId and changes count
|
|
118
|
-
*/
|
|
119
|
-
insertOrAbort(data: Partial<T> | Partial<T>[]): InsertResult {
|
|
120
|
-
return this.insert(data, { orAbort: true });
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Insert with OR FAIL conflict resolution.
|
|
125
|
-
*
|
|
126
|
-
* @param data - Single object or array of objects to insert
|
|
127
|
-
* @returns Insert result with insertId and changes count
|
|
128
|
-
*/
|
|
129
|
-
insertOrFail(data: Partial<T> | Partial<T>[]): InsertResult {
|
|
130
|
-
return this.insert(data, { orFail: true });
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Insert with OR ROLLBACK conflict resolution.
|
|
135
|
-
*
|
|
136
|
-
* @param data - Single object or array of objects to insert
|
|
137
|
-
* @returns Insert result with insertId and changes count
|
|
138
|
-
*/
|
|
139
|
-
insertOrRollback(data: Partial<T> | Partial<T>[]): InsertResult {
|
|
140
|
-
return this.insert(data, { orRollback: true });
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Insert and get the inserted row back.
|
|
145
|
-
* This is useful when you want to see the row with auto-generated fields.
|
|
146
|
-
*
|
|
147
|
-
* @param data - Single object to insert (bulk not supported for this method)
|
|
148
|
-
* @param options - Insert options
|
|
149
|
-
* @returns The inserted row with all fields, or null if insertion failed
|
|
150
|
-
*/
|
|
151
|
-
insertAndGet(data: Partial<T>, options?: InsertOptions): T | null {
|
|
152
|
-
const result = this.insert(data, options);
|
|
153
|
-
|
|
154
|
-
if (result.changes === 0) {
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// If we have an insertId, try to fetch the inserted row
|
|
159
|
-
if (result.insertId > 0) {
|
|
160
|
-
try {
|
|
161
|
-
const row = this.getDb()
|
|
162
|
-
.prepare(
|
|
163
|
-
`SELECT * FROM ${this.quoteIdentifier(this.getTableName())} WHERE rowid = ?`,
|
|
164
|
-
)
|
|
165
|
-
.get(result.insertId) as T | null;
|
|
166
|
-
return row ? this.transformRowFromDb(row) : null;
|
|
167
|
-
} catch {
|
|
168
|
-
// If fetching by rowid fails, return null
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Batch insert with transaction support.
|
|
178
|
-
* This method wraps multiple inserts in a transaction for better performance
|
|
179
|
-
* and atomicity when inserting large amounts of data.
|
|
180
|
-
*
|
|
181
|
-
* @param rows - Array of objects to insert
|
|
182
|
-
* @param options - Insert options
|
|
183
|
-
* @returns Insert result with total changes
|
|
184
|
-
*/
|
|
185
|
-
insertBatch(rows: Partial<T>[], options?: InsertOptions): InsertResult {
|
|
186
|
-
if (!Array.isArray(rows) || rows.length === 0) {
|
|
187
|
-
throw new Error("insertBatch: rows must be a non-empty array");
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const db = this.getDb();
|
|
191
|
-
|
|
192
|
-
// Use a transaction for batch operations
|
|
193
|
-
const transaction = db.transaction((rowsToInsert: Partial<T>[]) => {
|
|
194
|
-
let totalChanges = 0;
|
|
195
|
-
let lastInsertId = 0;
|
|
196
|
-
|
|
197
|
-
// Transform rows to handle JSON serialization
|
|
198
|
-
const transformedRows = rowsToInsert.map((row) =>
|
|
199
|
-
this.transformRowToDb(row),
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
// Get all unique columns from all rows
|
|
203
|
-
const allColumns = new Set<string>();
|
|
204
|
-
for (const row of transformedRows) {
|
|
205
|
-
for (const col of Object.keys(row)) {
|
|
206
|
-
allColumns.add(col);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const columns = Array.from(allColumns);
|
|
211
|
-
if (columns.length === 0) {
|
|
212
|
-
throw new Error("insertBatch: no columns to insert");
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Build INSERT statement with conflict resolution
|
|
216
|
-
let insertType = "INSERT";
|
|
217
|
-
if (options?.orIgnore) insertType = "INSERT OR IGNORE";
|
|
218
|
-
else if (options?.orReplace) insertType = "INSERT OR REPLACE";
|
|
219
|
-
else if (options?.orAbort) insertType = "INSERT OR ABORT";
|
|
220
|
-
else if (options?.orFail) insertType = "INSERT OR FAIL";
|
|
221
|
-
else if (options?.orRollback) insertType = "INSERT OR ROLLBACK";
|
|
222
|
-
|
|
223
|
-
const quotedColumns = columns
|
|
224
|
-
.map((col) => this.quoteIdentifier(col))
|
|
225
|
-
.join(", ");
|
|
226
|
-
const placeholders = columns.map(() => "?").join(", ");
|
|
227
|
-
|
|
228
|
-
const query = `${insertType} INTO ${this.quoteIdentifier(this.getTableName())} (${quotedColumns}) VALUES (${placeholders})`;
|
|
229
|
-
const stmt = db.prepare(query);
|
|
230
|
-
|
|
231
|
-
for (const row of transformedRows) {
|
|
232
|
-
const values = columns.map(
|
|
233
|
-
(col) => row[col as keyof typeof row] ?? null,
|
|
234
|
-
) as SQLQueryBindings[];
|
|
235
|
-
const result = stmt.run(...values);
|
|
236
|
-
totalChanges += result.changes;
|
|
237
|
-
if (result.lastInsertRowid) {
|
|
238
|
-
lastInsertId = Number(result.lastInsertRowid);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return { insertId: lastInsertId, changes: totalChanges };
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
const result = transaction(rows);
|
|
246
|
-
this.reset();
|
|
247
|
-
return result;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
1
|
+
import type { SQLQueryBindings } from "bun:sqlite";
|
|
2
|
+
import type { InsertResult, InsertOptions } from "../types";
|
|
3
|
+
import { WhereQueryBuilder } from "./where";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Mixin class that adds INSERT functionality to the QueryBuilder.
|
|
7
|
+
* Handles single and bulk insert operations with conflict resolution.
|
|
8
|
+
*/
|
|
9
|
+
export class InsertQueryBuilder<
|
|
10
|
+
T extends Record<string, unknown>,
|
|
11
|
+
> extends WhereQueryBuilder<T> {
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Insert a single row or multiple rows into the table.
|
|
15
|
+
*
|
|
16
|
+
* @param data - Single object or array of objects to insert
|
|
17
|
+
* @param options - Insert options (OR IGNORE, OR REPLACE, etc.)
|
|
18
|
+
* @returns Insert result with insertId and changes count
|
|
19
|
+
*/
|
|
20
|
+
insert(
|
|
21
|
+
data: Partial<T> | Partial<T>[],
|
|
22
|
+
options?: InsertOptions,
|
|
23
|
+
): InsertResult {
|
|
24
|
+
this.getLogger().debug(`Building Data Array: ${data}`)
|
|
25
|
+
const rows = Array.isArray(data) ? data : [data];
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
// Transform rows to handle JSON serialization
|
|
30
|
+
const transformedRows = rows.map((row) => this.transformRowToDb(row));
|
|
31
|
+
|
|
32
|
+
this.getLogger().debug(`Transformed row: ${JSON.stringify(transformedRows)}`)
|
|
33
|
+
|
|
34
|
+
if (transformedRows.length === 0) {
|
|
35
|
+
throw new Error("insert: data cannot be empty");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Get all unique columns from all rows
|
|
39
|
+
const allColumns = new Set<string>();
|
|
40
|
+
for (const row of transformedRows) {
|
|
41
|
+
for (const col of Object.keys(row)) {
|
|
42
|
+
allColumns.add(col);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const columns = Array.from(allColumns);
|
|
47
|
+
if (columns.length === 0) {
|
|
48
|
+
throw new Error("insert: no columns to insert");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Build INSERT statement with conflict resolution
|
|
52
|
+
let insertType = "INSERT";
|
|
53
|
+
if (options?.orIgnore) insertType = "INSERT OR IGNORE";
|
|
54
|
+
else if (options?.orReplace) insertType = "INSERT OR REPLACE";
|
|
55
|
+
else if (options?.orAbort) insertType = "INSERT OR ABORT";
|
|
56
|
+
else if (options?.orFail) insertType = "INSERT OR FAIL";
|
|
57
|
+
else if (options?.orRollback) insertType = "INSERT OR ROLLBACK";
|
|
58
|
+
|
|
59
|
+
const quotedColumns = columns
|
|
60
|
+
.map((col) => this.quoteIdentifier(col))
|
|
61
|
+
.join(", ");
|
|
62
|
+
const placeholders = columns.map(() => "?").join(", ");
|
|
63
|
+
|
|
64
|
+
const query = `${insertType} INTO ${this.quoteIdentifier(this.getTableName())} (${quotedColumns}) VALUES (${placeholders})`;
|
|
65
|
+
const stmt = this.getDb().prepare(query);
|
|
66
|
+
|
|
67
|
+
let totalChanges = 0;
|
|
68
|
+
let lastInsertId = 0;
|
|
69
|
+
|
|
70
|
+
// Execute for each row
|
|
71
|
+
for (const row of transformedRows) {
|
|
72
|
+
const values = columns.map(
|
|
73
|
+
(col) => row[col as keyof typeof row] ?? null,
|
|
74
|
+
) as SQLQueryBindings[];
|
|
75
|
+
const result = stmt.run(...values);
|
|
76
|
+
totalChanges += result.changes;
|
|
77
|
+
if (result.lastInsertRowid) {
|
|
78
|
+
lastInsertId = Number(result.lastInsertRowid);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const result = {
|
|
83
|
+
insertId: lastInsertId,
|
|
84
|
+
changes: totalChanges,
|
|
85
|
+
};
|
|
86
|
+
this.reset();
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Insert with OR IGNORE conflict resolution.
|
|
92
|
+
* Convenience method equivalent to insert(data, { orIgnore: true })
|
|
93
|
+
*
|
|
94
|
+
* @param data - Single object or array of objects to insert
|
|
95
|
+
* @returns Insert result with insertId and changes count
|
|
96
|
+
*/
|
|
97
|
+
insertOrIgnore(data: Partial<T> | Partial<T>[]): InsertResult {
|
|
98
|
+
return this.insert(data, { orIgnore: true });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Insert with OR REPLACE conflict resolution.
|
|
103
|
+
* Convenience method equivalent to insert(data, { orReplace: true })
|
|
104
|
+
*
|
|
105
|
+
* @param data - Single object or array of objects to insert
|
|
106
|
+
* @returns Insert result with insertId and changes count
|
|
107
|
+
*/
|
|
108
|
+
insertOrReplace(data: Partial<T> | Partial<T>[]): InsertResult {
|
|
109
|
+
return this.insert(data, { orReplace: true });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Insert with OR ABORT conflict resolution.
|
|
114
|
+
* This is the default behavior but provided for explicit usage.
|
|
115
|
+
*
|
|
116
|
+
* @param data - Single object or array of objects to insert
|
|
117
|
+
* @returns Insert result with insertId and changes count
|
|
118
|
+
*/
|
|
119
|
+
insertOrAbort(data: Partial<T> | Partial<T>[]): InsertResult {
|
|
120
|
+
return this.insert(data, { orAbort: true });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Insert with OR FAIL conflict resolution.
|
|
125
|
+
*
|
|
126
|
+
* @param data - Single object or array of objects to insert
|
|
127
|
+
* @returns Insert result with insertId and changes count
|
|
128
|
+
*/
|
|
129
|
+
insertOrFail(data: Partial<T> | Partial<T>[]): InsertResult {
|
|
130
|
+
return this.insert(data, { orFail: true });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Insert with OR ROLLBACK conflict resolution.
|
|
135
|
+
*
|
|
136
|
+
* @param data - Single object or array of objects to insert
|
|
137
|
+
* @returns Insert result with insertId and changes count
|
|
138
|
+
*/
|
|
139
|
+
insertOrRollback(data: Partial<T> | Partial<T>[]): InsertResult {
|
|
140
|
+
return this.insert(data, { orRollback: true });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Insert and get the inserted row back.
|
|
145
|
+
* This is useful when you want to see the row with auto-generated fields.
|
|
146
|
+
*
|
|
147
|
+
* @param data - Single object to insert (bulk not supported for this method)
|
|
148
|
+
* @param options - Insert options
|
|
149
|
+
* @returns The inserted row with all fields, or null if insertion failed
|
|
150
|
+
*/
|
|
151
|
+
insertAndGet(data: Partial<T>, options?: InsertOptions): T | null {
|
|
152
|
+
const result = this.insert(data, options);
|
|
153
|
+
|
|
154
|
+
if (result.changes === 0) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// If we have an insertId, try to fetch the inserted row
|
|
159
|
+
if (result.insertId > 0) {
|
|
160
|
+
try {
|
|
161
|
+
const row = this.getDb()
|
|
162
|
+
.prepare(
|
|
163
|
+
`SELECT * FROM ${this.quoteIdentifier(this.getTableName())} WHERE rowid = ?`,
|
|
164
|
+
)
|
|
165
|
+
.get(result.insertId) as T | null;
|
|
166
|
+
return row ? this.transformRowFromDb(row) : null;
|
|
167
|
+
} catch {
|
|
168
|
+
// If fetching by rowid fails, return null
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Batch insert with transaction support.
|
|
178
|
+
* This method wraps multiple inserts in a transaction for better performance
|
|
179
|
+
* and atomicity when inserting large amounts of data.
|
|
180
|
+
*
|
|
181
|
+
* @param rows - Array of objects to insert
|
|
182
|
+
* @param options - Insert options
|
|
183
|
+
* @returns Insert result with total changes
|
|
184
|
+
*/
|
|
185
|
+
insertBatch(rows: Partial<T>[], options?: InsertOptions): InsertResult {
|
|
186
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
187
|
+
throw new Error("insertBatch: rows must be a non-empty array");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const db = this.getDb();
|
|
191
|
+
|
|
192
|
+
// Use a transaction for batch operations
|
|
193
|
+
const transaction = db.transaction((rowsToInsert: Partial<T>[]) => {
|
|
194
|
+
let totalChanges = 0;
|
|
195
|
+
let lastInsertId = 0;
|
|
196
|
+
|
|
197
|
+
// Transform rows to handle JSON serialization
|
|
198
|
+
const transformedRows = rowsToInsert.map((row) =>
|
|
199
|
+
this.transformRowToDb(row),
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
// Get all unique columns from all rows
|
|
203
|
+
const allColumns = new Set<string>();
|
|
204
|
+
for (const row of transformedRows) {
|
|
205
|
+
for (const col of Object.keys(row)) {
|
|
206
|
+
allColumns.add(col);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const columns = Array.from(allColumns);
|
|
211
|
+
if (columns.length === 0) {
|
|
212
|
+
throw new Error("insertBatch: no columns to insert");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Build INSERT statement with conflict resolution
|
|
216
|
+
let insertType = "INSERT";
|
|
217
|
+
if (options?.orIgnore) insertType = "INSERT OR IGNORE";
|
|
218
|
+
else if (options?.orReplace) insertType = "INSERT OR REPLACE";
|
|
219
|
+
else if (options?.orAbort) insertType = "INSERT OR ABORT";
|
|
220
|
+
else if (options?.orFail) insertType = "INSERT OR FAIL";
|
|
221
|
+
else if (options?.orRollback) insertType = "INSERT OR ROLLBACK";
|
|
222
|
+
|
|
223
|
+
const quotedColumns = columns
|
|
224
|
+
.map((col) => this.quoteIdentifier(col))
|
|
225
|
+
.join(", ");
|
|
226
|
+
const placeholders = columns.map(() => "?").join(", ");
|
|
227
|
+
|
|
228
|
+
const query = `${insertType} INTO ${this.quoteIdentifier(this.getTableName())} (${quotedColumns}) VALUES (${placeholders})`;
|
|
229
|
+
const stmt = db.prepare(query);
|
|
230
|
+
|
|
231
|
+
for (const row of transformedRows) {
|
|
232
|
+
const values = columns.map(
|
|
233
|
+
(col) => row[col as keyof typeof row] ?? null,
|
|
234
|
+
) as SQLQueryBindings[];
|
|
235
|
+
const result = stmt.run(...values);
|
|
236
|
+
totalChanges += result.changes;
|
|
237
|
+
if (result.lastInsertRowid) {
|
|
238
|
+
lastInsertId = Number(result.lastInsertRowid);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return { insertId: lastInsertId, changes: totalChanges };
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const result = transaction(rows);
|
|
246
|
+
this.reset();
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
}
|