@dockstat/sqlite-wrapper 1.2.7 → 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 -99
- 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/update.ts
CHANGED
|
@@ -1,278 +1,278 @@
|
|
|
1
|
-
import type { SQLQueryBindings } from "bun:sqlite";
|
|
2
|
-
import type { UpdateResult } from "../types";
|
|
3
|
-
import { SelectQueryBuilder } from "./select";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Mixin class that adds UPDATE functionality to the QueryBuilder.
|
|
7
|
-
* Handles safe update operations with mandatory WHERE conditions.
|
|
8
|
-
*/
|
|
9
|
-
export class UpdateQueryBuilder<
|
|
10
|
-
T extends Record<string, unknown>,
|
|
11
|
-
> extends SelectQueryBuilder<T> {
|
|
12
|
-
/**
|
|
13
|
-
* Update rows matching the WHERE conditions with the provided data.
|
|
14
|
-
* Requires at least one WHERE condition to prevent accidental full table updates.
|
|
15
|
-
*
|
|
16
|
-
* @param data - Object with columns to update and their new values
|
|
17
|
-
* @returns Update result with changes count
|
|
18
|
-
*/
|
|
19
|
-
update(data: Partial<T>): UpdateResult {
|
|
20
|
-
this.requireWhereClause("UPDATE");
|
|
21
|
-
|
|
22
|
-
// Transform data to handle JSON serialization
|
|
23
|
-
const transformedData = this.transformRowToDb(data);
|
|
24
|
-
const updateColumns = Object.keys(transformedData);
|
|
25
|
-
if (updateColumns.length === 0) {
|
|
26
|
-
this.reset()
|
|
27
|
-
throw new Error("update: no columns to update");
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Handle regex conditions by first fetching matching rows
|
|
31
|
-
if (this.hasRegexConditions()) {
|
|
32
|
-
this.reset()
|
|
33
|
-
return this.updateWithRegexConditions(transformedData);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Build UPDATE statement
|
|
37
|
-
const setClause = updateColumns
|
|
38
|
-
.map((col) => `${this.quoteIdentifier(col)} = ?`)
|
|
39
|
-
.join(", ");
|
|
40
|
-
|
|
41
|
-
const [whereClause, whereParams] = this.buildWhereClause();
|
|
42
|
-
const query = `UPDATE ${this.quoteIdentifier(this.getTableName())} SET ${setClause}${whereClause}`;
|
|
43
|
-
|
|
44
|
-
const updateValues = updateColumns.map((col) => transformedData[col]);
|
|
45
|
-
const allParams = [...updateValues, ...whereParams];
|
|
46
|
-
|
|
47
|
-
const result = this.getDb()
|
|
48
|
-
.prepare(query)
|
|
49
|
-
.run(...allParams);
|
|
50
|
-
|
|
51
|
-
const out = {
|
|
52
|
-
changes: result.changes,
|
|
53
|
-
};
|
|
54
|
-
this.reset();
|
|
55
|
-
return out;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Handle UPDATE operations when regex conditions are present.
|
|
60
|
-
* This requires client-side filtering and individual row updates.
|
|
61
|
-
*/
|
|
62
|
-
private updateWithRegexConditions(
|
|
63
|
-
transformedData: Record<string, SQLQueryBindings>,
|
|
64
|
-
): UpdateResult {
|
|
65
|
-
// First, get all rows matching SQL conditions (without regex)
|
|
66
|
-
const [selectQuery, selectParams] = this.buildWhereClause();
|
|
67
|
-
const candidateRows = this.getDb()
|
|
68
|
-
.prepare(
|
|
69
|
-
`SELECT rowid, * FROM ${this.quoteIdentifier(this.getTableName())}${selectQuery}`,
|
|
70
|
-
)
|
|
71
|
-
.all(...selectParams) as (T & { rowid: number })[];
|
|
72
|
-
|
|
73
|
-
// Apply regex filtering
|
|
74
|
-
const matchingRows = this.applyRegexFiltering(candidateRows);
|
|
75
|
-
|
|
76
|
-
if (matchingRows.length === 0) {
|
|
77
|
-
this.reset()
|
|
78
|
-
return { changes: 0 };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Update each matching row by rowid
|
|
82
|
-
const updateColumns = Object.keys(transformedData);
|
|
83
|
-
const setClause = updateColumns
|
|
84
|
-
.map((col) => `${this.quoteIdentifier(col)} = ?`)
|
|
85
|
-
.join(", ");
|
|
86
|
-
|
|
87
|
-
const updateQuery = `UPDATE ${this.quoteIdentifier(this.getTableName())} SET ${setClause} WHERE rowid = ?`;
|
|
88
|
-
const stmt = this.getDb().prepare(updateQuery);
|
|
89
|
-
|
|
90
|
-
let totalChanges = 0;
|
|
91
|
-
const updateValues = updateColumns.map((col) => transformedData[col]);
|
|
92
|
-
|
|
93
|
-
for (const row of matchingRows) {
|
|
94
|
-
const result = stmt.run(...updateValues, row.rowid as SQLQueryBindings);
|
|
95
|
-
totalChanges += result.changes;
|
|
96
|
-
}
|
|
97
|
-
this.reset()
|
|
98
|
-
return { changes: totalChanges };
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Update or insert (upsert) functionality using INSERT OR REPLACE.
|
|
103
|
-
* This method attempts to update existing rows, and inserts new ones if they don't exist.
|
|
104
|
-
* Requires a unique constraint or primary key to work properly.
|
|
105
|
-
*
|
|
106
|
-
* @param data - Object with columns to upsert
|
|
107
|
-
* @returns Update result with changes count
|
|
108
|
-
*/
|
|
109
|
-
upsert(data: Partial<T>): UpdateResult {
|
|
110
|
-
// Transform data to handle JSON serialization
|
|
111
|
-
const transformedData = this.transformRowToDb(data);
|
|
112
|
-
const columns = Object.keys(transformedData);
|
|
113
|
-
if (columns.length === 0) {
|
|
114
|
-
this.reset()
|
|
115
|
-
throw new Error("upsert: no columns to upsert");
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const quotedColumns = columns
|
|
119
|
-
.map((col) => this.quoteIdentifier(col))
|
|
120
|
-
.join(", ");
|
|
121
|
-
const placeholders = columns.map(() => "?").join(", ");
|
|
122
|
-
|
|
123
|
-
const query = `INSERT OR REPLACE INTO ${this.quoteIdentifier(this.getTableName())} (${quotedColumns}) VALUES (${placeholders})`;
|
|
124
|
-
|
|
125
|
-
const values = columns.map((col) => transformedData[col] ?? null);
|
|
126
|
-
const result = this.getDb()
|
|
127
|
-
.prepare(query)
|
|
128
|
-
.run(...values);
|
|
129
|
-
|
|
130
|
-
const out = {
|
|
131
|
-
changes: result.changes,
|
|
132
|
-
};
|
|
133
|
-
this.reset();
|
|
134
|
-
return out;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Increment a numeric column by a specified amount.
|
|
139
|
-
* This is more efficient than fetching, calculating, and updating.
|
|
140
|
-
*
|
|
141
|
-
* @param column - Column name to increment
|
|
142
|
-
* @param amount - Amount to increment by (defaults to 1)
|
|
143
|
-
* @returns Update result with changes count
|
|
144
|
-
*/
|
|
145
|
-
increment(column: keyof T, amount = 1): UpdateResult {
|
|
146
|
-
this.requireWhereClause("INCREMENT");
|
|
147
|
-
|
|
148
|
-
const [whereClause, whereParams] = this.buildWhereClause();
|
|
149
|
-
const query = `UPDATE ${this.quoteIdentifier(this.getTableName())} SET ${this.quoteIdentifier(String(column))} = ${this.quoteIdentifier(String(column))} + ?${whereClause}`;
|
|
150
|
-
|
|
151
|
-
const result = this.getDb()
|
|
152
|
-
.prepare(query)
|
|
153
|
-
.run(amount, ...whereParams);
|
|
154
|
-
|
|
155
|
-
const out = {
|
|
156
|
-
changes: result.changes,
|
|
157
|
-
};
|
|
158
|
-
this.reset();
|
|
159
|
-
return out;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Decrement a numeric column by a specified amount.
|
|
164
|
-
* This is more efficient than fetching, calculating, and updating.
|
|
165
|
-
*
|
|
166
|
-
* @param column - Column name to decrement
|
|
167
|
-
* @param amount - Amount to decrement by (defaults to 1)
|
|
168
|
-
* @returns Update result with changes count
|
|
169
|
-
*/
|
|
170
|
-
decrement(column: keyof T, amount = 1): UpdateResult {
|
|
171
|
-
const out = this.increment(column, -amount);
|
|
172
|
-
this.reset();
|
|
173
|
-
return out;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Update and get the updated rows back.
|
|
178
|
-
* This is useful when you want to see the rows after the update.
|
|
179
|
-
*
|
|
180
|
-
* @param data - Object with columns to update and their new values
|
|
181
|
-
* @returns Array of updated rows
|
|
182
|
-
*/
|
|
183
|
-
updateAndGet(data: Partial<T>): T[] {
|
|
184
|
-
// First, get the rows that will be updated
|
|
185
|
-
const rowsToUpdate = this.all();
|
|
186
|
-
|
|
187
|
-
// Perform the update
|
|
188
|
-
const updateResult = this.update(data);
|
|
189
|
-
|
|
190
|
-
if (updateResult.changes === 0) {
|
|
191
|
-
this.reset()
|
|
192
|
-
return [];
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// For simplicity, return the originally matched rows
|
|
196
|
-
// In a real implementation, you might want to re-fetch the updated rows
|
|
197
|
-
const out = rowsToUpdate;
|
|
198
|
-
this.reset();
|
|
199
|
-
return out;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Batch update multiple rows with different values.
|
|
204
|
-
* This is more efficient than individual updates when updating many rows.
|
|
205
|
-
*
|
|
206
|
-
* @param updates - Array of objects, each containing update data and conditions
|
|
207
|
-
* @returns Update result with total changes count
|
|
208
|
-
*/
|
|
209
|
-
updateBatch(
|
|
210
|
-
updates: Array<{ where: Partial<T>; data: Partial<T> }>,
|
|
211
|
-
): UpdateResult {
|
|
212
|
-
if (!Array.isArray(updates) || updates.length === 0) {
|
|
213
|
-
this.reset()
|
|
214
|
-
throw new Error("updateBatch: updates must be a non-empty array");
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const db = this.getDb();
|
|
218
|
-
|
|
219
|
-
// Use a transaction for batch operations
|
|
220
|
-
const transaction = db.transaction(
|
|
221
|
-
(updatesToProcess: Array<{ where: Partial<T>; data: Partial<T> }>) => {
|
|
222
|
-
let totalChanges = 0;
|
|
223
|
-
|
|
224
|
-
for (const { where: whereData, data } of updatesToProcess) {
|
|
225
|
-
// Transform data to handle JSON serialization
|
|
226
|
-
const transformedUpdateData = this.transformRowToDb(data);
|
|
227
|
-
const updateColumns = Object.keys(transformedUpdateData);
|
|
228
|
-
if (updateColumns.length === 0) {
|
|
229
|
-
continue; // Skip empty updates
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Build WHERE conditions for this update
|
|
233
|
-
const whereConditions: string[] = [];
|
|
234
|
-
const whereParams: SQLQueryBindings[] = [];
|
|
235
|
-
|
|
236
|
-
for (const [column, value] of Object.entries(whereData)) {
|
|
237
|
-
if (value === null || value === undefined) {
|
|
238
|
-
whereConditions.push(`${this.quoteIdentifier(column)} IS NULL`);
|
|
239
|
-
} else {
|
|
240
|
-
whereConditions.push(`${this.quoteIdentifier(column)} = ?`);
|
|
241
|
-
whereParams.push(value);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (whereConditions.length === 0) {
|
|
246
|
-
this.reset()
|
|
247
|
-
throw new Error(
|
|
248
|
-
"updateBatch: each update must have WHERE conditions",
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Build UPDATE statement
|
|
253
|
-
const setClause = updateColumns
|
|
254
|
-
.map((col) => `${this.quoteIdentifier(col)} = ?`)
|
|
255
|
-
.join(", ");
|
|
256
|
-
|
|
257
|
-
const whereClause = ` WHERE ${whereConditions.join(" AND ")}`;
|
|
258
|
-
const query = `UPDATE ${this.quoteIdentifier(this.getTableName())} SET ${setClause}${whereClause}`;
|
|
259
|
-
|
|
260
|
-
const updateValues = updateColumns.map(
|
|
261
|
-
(col) => transformedUpdateData[col] ?? null,
|
|
262
|
-
);
|
|
263
|
-
const allParams = [...updateValues, ...whereParams];
|
|
264
|
-
|
|
265
|
-
const result = db.prepare(query).run(...allParams);
|
|
266
|
-
totalChanges += result.changes;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
this.reset()
|
|
270
|
-
return { changes: totalChanges };
|
|
271
|
-
},
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
const out = transaction(updates);
|
|
275
|
-
this.reset();
|
|
276
|
-
return out;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
1
|
+
import type { SQLQueryBindings } from "bun:sqlite";
|
|
2
|
+
import type { UpdateResult } from "../types";
|
|
3
|
+
import { SelectQueryBuilder } from "./select";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Mixin class that adds UPDATE functionality to the QueryBuilder.
|
|
7
|
+
* Handles safe update operations with mandatory WHERE conditions.
|
|
8
|
+
*/
|
|
9
|
+
export class UpdateQueryBuilder<
|
|
10
|
+
T extends Record<string, unknown>,
|
|
11
|
+
> extends SelectQueryBuilder<T> {
|
|
12
|
+
/**
|
|
13
|
+
* Update rows matching the WHERE conditions with the provided data.
|
|
14
|
+
* Requires at least one WHERE condition to prevent accidental full table updates.
|
|
15
|
+
*
|
|
16
|
+
* @param data - Object with columns to update and their new values
|
|
17
|
+
* @returns Update result with changes count
|
|
18
|
+
*/
|
|
19
|
+
update(data: Partial<T>): UpdateResult {
|
|
20
|
+
this.requireWhereClause("UPDATE");
|
|
21
|
+
|
|
22
|
+
// Transform data to handle JSON serialization
|
|
23
|
+
const transformedData = this.transformRowToDb(data);
|
|
24
|
+
const updateColumns = Object.keys(transformedData);
|
|
25
|
+
if (updateColumns.length === 0) {
|
|
26
|
+
this.reset()
|
|
27
|
+
throw new Error("update: no columns to update");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Handle regex conditions by first fetching matching rows
|
|
31
|
+
if (this.hasRegexConditions()) {
|
|
32
|
+
this.reset()
|
|
33
|
+
return this.updateWithRegexConditions(transformedData);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Build UPDATE statement
|
|
37
|
+
const setClause = updateColumns
|
|
38
|
+
.map((col) => `${this.quoteIdentifier(col)} = ?`)
|
|
39
|
+
.join(", ");
|
|
40
|
+
|
|
41
|
+
const [whereClause, whereParams] = this.buildWhereClause();
|
|
42
|
+
const query = `UPDATE ${this.quoteIdentifier(this.getTableName())} SET ${setClause}${whereClause}`;
|
|
43
|
+
|
|
44
|
+
const updateValues = updateColumns.map((col) => transformedData[col]);
|
|
45
|
+
const allParams = [...updateValues, ...whereParams];
|
|
46
|
+
|
|
47
|
+
const result = this.getDb()
|
|
48
|
+
.prepare(query)
|
|
49
|
+
.run(...allParams);
|
|
50
|
+
|
|
51
|
+
const out = {
|
|
52
|
+
changes: result.changes,
|
|
53
|
+
};
|
|
54
|
+
this.reset();
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Handle UPDATE operations when regex conditions are present.
|
|
60
|
+
* This requires client-side filtering and individual row updates.
|
|
61
|
+
*/
|
|
62
|
+
private updateWithRegexConditions(
|
|
63
|
+
transformedData: Record<string, SQLQueryBindings>,
|
|
64
|
+
): UpdateResult {
|
|
65
|
+
// First, get all rows matching SQL conditions (without regex)
|
|
66
|
+
const [selectQuery, selectParams] = this.buildWhereClause();
|
|
67
|
+
const candidateRows = this.getDb()
|
|
68
|
+
.prepare(
|
|
69
|
+
`SELECT rowid, * FROM ${this.quoteIdentifier(this.getTableName())}${selectQuery}`,
|
|
70
|
+
)
|
|
71
|
+
.all(...selectParams) as (T & { rowid: number })[];
|
|
72
|
+
|
|
73
|
+
// Apply regex filtering
|
|
74
|
+
const matchingRows = this.applyRegexFiltering(candidateRows);
|
|
75
|
+
|
|
76
|
+
if (matchingRows.length === 0) {
|
|
77
|
+
this.reset()
|
|
78
|
+
return { changes: 0 };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Update each matching row by rowid
|
|
82
|
+
const updateColumns = Object.keys(transformedData);
|
|
83
|
+
const setClause = updateColumns
|
|
84
|
+
.map((col) => `${this.quoteIdentifier(col)} = ?`)
|
|
85
|
+
.join(", ");
|
|
86
|
+
|
|
87
|
+
const updateQuery = `UPDATE ${this.quoteIdentifier(this.getTableName())} SET ${setClause} WHERE rowid = ?`;
|
|
88
|
+
const stmt = this.getDb().prepare(updateQuery);
|
|
89
|
+
|
|
90
|
+
let totalChanges = 0;
|
|
91
|
+
const updateValues = updateColumns.map((col) => transformedData[col]);
|
|
92
|
+
|
|
93
|
+
for (const row of matchingRows) {
|
|
94
|
+
const result = stmt.run(...updateValues, row.rowid as SQLQueryBindings);
|
|
95
|
+
totalChanges += result.changes;
|
|
96
|
+
}
|
|
97
|
+
this.reset()
|
|
98
|
+
return { changes: totalChanges };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Update or insert (upsert) functionality using INSERT OR REPLACE.
|
|
103
|
+
* This method attempts to update existing rows, and inserts new ones if they don't exist.
|
|
104
|
+
* Requires a unique constraint or primary key to work properly.
|
|
105
|
+
*
|
|
106
|
+
* @param data - Object with columns to upsert
|
|
107
|
+
* @returns Update result with changes count
|
|
108
|
+
*/
|
|
109
|
+
upsert(data: Partial<T>): UpdateResult {
|
|
110
|
+
// Transform data to handle JSON serialization
|
|
111
|
+
const transformedData = this.transformRowToDb(data);
|
|
112
|
+
const columns = Object.keys(transformedData);
|
|
113
|
+
if (columns.length === 0) {
|
|
114
|
+
this.reset()
|
|
115
|
+
throw new Error("upsert: no columns to upsert");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const quotedColumns = columns
|
|
119
|
+
.map((col) => this.quoteIdentifier(col))
|
|
120
|
+
.join(", ");
|
|
121
|
+
const placeholders = columns.map(() => "?").join(", ");
|
|
122
|
+
|
|
123
|
+
const query = `INSERT OR REPLACE INTO ${this.quoteIdentifier(this.getTableName())} (${quotedColumns}) VALUES (${placeholders})`;
|
|
124
|
+
|
|
125
|
+
const values = columns.map((col) => transformedData[col] ?? null);
|
|
126
|
+
const result = this.getDb()
|
|
127
|
+
.prepare(query)
|
|
128
|
+
.run(...values);
|
|
129
|
+
|
|
130
|
+
const out = {
|
|
131
|
+
changes: result.changes,
|
|
132
|
+
};
|
|
133
|
+
this.reset();
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Increment a numeric column by a specified amount.
|
|
139
|
+
* This is more efficient than fetching, calculating, and updating.
|
|
140
|
+
*
|
|
141
|
+
* @param column - Column name to increment
|
|
142
|
+
* @param amount - Amount to increment by (defaults to 1)
|
|
143
|
+
* @returns Update result with changes count
|
|
144
|
+
*/
|
|
145
|
+
increment(column: keyof T, amount = 1): UpdateResult {
|
|
146
|
+
this.requireWhereClause("INCREMENT");
|
|
147
|
+
|
|
148
|
+
const [whereClause, whereParams] = this.buildWhereClause();
|
|
149
|
+
const query = `UPDATE ${this.quoteIdentifier(this.getTableName())} SET ${this.quoteIdentifier(String(column))} = ${this.quoteIdentifier(String(column))} + ?${whereClause}`;
|
|
150
|
+
|
|
151
|
+
const result = this.getDb()
|
|
152
|
+
.prepare(query)
|
|
153
|
+
.run(amount, ...whereParams);
|
|
154
|
+
|
|
155
|
+
const out = {
|
|
156
|
+
changes: result.changes,
|
|
157
|
+
};
|
|
158
|
+
this.reset();
|
|
159
|
+
return out;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Decrement a numeric column by a specified amount.
|
|
164
|
+
* This is more efficient than fetching, calculating, and updating.
|
|
165
|
+
*
|
|
166
|
+
* @param column - Column name to decrement
|
|
167
|
+
* @param amount - Amount to decrement by (defaults to 1)
|
|
168
|
+
* @returns Update result with changes count
|
|
169
|
+
*/
|
|
170
|
+
decrement(column: keyof T, amount = 1): UpdateResult {
|
|
171
|
+
const out = this.increment(column, -amount);
|
|
172
|
+
this.reset();
|
|
173
|
+
return out;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Update and get the updated rows back.
|
|
178
|
+
* This is useful when you want to see the rows after the update.
|
|
179
|
+
*
|
|
180
|
+
* @param data - Object with columns to update and their new values
|
|
181
|
+
* @returns Array of updated rows
|
|
182
|
+
*/
|
|
183
|
+
updateAndGet(data: Partial<T>): T[] {
|
|
184
|
+
// First, get the rows that will be updated
|
|
185
|
+
const rowsToUpdate = this.all();
|
|
186
|
+
|
|
187
|
+
// Perform the update
|
|
188
|
+
const updateResult = this.update(data);
|
|
189
|
+
|
|
190
|
+
if (updateResult.changes === 0) {
|
|
191
|
+
this.reset()
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// For simplicity, return the originally matched rows
|
|
196
|
+
// In a real implementation, you might want to re-fetch the updated rows
|
|
197
|
+
const out = rowsToUpdate;
|
|
198
|
+
this.reset();
|
|
199
|
+
return out;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Batch update multiple rows with different values.
|
|
204
|
+
* This is more efficient than individual updates when updating many rows.
|
|
205
|
+
*
|
|
206
|
+
* @param updates - Array of objects, each containing update data and conditions
|
|
207
|
+
* @returns Update result with total changes count
|
|
208
|
+
*/
|
|
209
|
+
updateBatch(
|
|
210
|
+
updates: Array<{ where: Partial<T>; data: Partial<T> }>,
|
|
211
|
+
): UpdateResult {
|
|
212
|
+
if (!Array.isArray(updates) || updates.length === 0) {
|
|
213
|
+
this.reset()
|
|
214
|
+
throw new Error("updateBatch: updates must be a non-empty array");
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const db = this.getDb();
|
|
218
|
+
|
|
219
|
+
// Use a transaction for batch operations
|
|
220
|
+
const transaction = db.transaction(
|
|
221
|
+
(updatesToProcess: Array<{ where: Partial<T>; data: Partial<T> }>) => {
|
|
222
|
+
let totalChanges = 0;
|
|
223
|
+
|
|
224
|
+
for (const { where: whereData, data } of updatesToProcess) {
|
|
225
|
+
// Transform data to handle JSON serialization
|
|
226
|
+
const transformedUpdateData = this.transformRowToDb(data);
|
|
227
|
+
const updateColumns = Object.keys(transformedUpdateData);
|
|
228
|
+
if (updateColumns.length === 0) {
|
|
229
|
+
continue; // Skip empty updates
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Build WHERE conditions for this update
|
|
233
|
+
const whereConditions: string[] = [];
|
|
234
|
+
const whereParams: SQLQueryBindings[] = [];
|
|
235
|
+
|
|
236
|
+
for (const [column, value] of Object.entries(whereData)) {
|
|
237
|
+
if (value === null || value === undefined) {
|
|
238
|
+
whereConditions.push(`${this.quoteIdentifier(column)} IS NULL`);
|
|
239
|
+
} else {
|
|
240
|
+
whereConditions.push(`${this.quoteIdentifier(column)} = ?`);
|
|
241
|
+
whereParams.push(value);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (whereConditions.length === 0) {
|
|
246
|
+
this.reset()
|
|
247
|
+
throw new Error(
|
|
248
|
+
"updateBatch: each update must have WHERE conditions",
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Build UPDATE statement
|
|
253
|
+
const setClause = updateColumns
|
|
254
|
+
.map((col) => `${this.quoteIdentifier(col)} = ?`)
|
|
255
|
+
.join(", ");
|
|
256
|
+
|
|
257
|
+
const whereClause = ` WHERE ${whereConditions.join(" AND ")}`;
|
|
258
|
+
const query = `UPDATE ${this.quoteIdentifier(this.getTableName())} SET ${setClause}${whereClause}`;
|
|
259
|
+
|
|
260
|
+
const updateValues = updateColumns.map(
|
|
261
|
+
(col) => transformedUpdateData[col] ?? null,
|
|
262
|
+
);
|
|
263
|
+
const allParams = [...updateValues, ...whereParams];
|
|
264
|
+
|
|
265
|
+
const result = db.prepare(query).run(...allParams);
|
|
266
|
+
totalChanges += result.changes;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
this.reset()
|
|
270
|
+
return { changes: totalChanges };
|
|
271
|
+
},
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
const out = transaction(updates);
|
|
275
|
+
this.reset();
|
|
276
|
+
return out;
|
|
277
|
+
}
|
|
278
|
+
}
|