sqlite-hub-client 0.6.0 → 0.7.0
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 +60 -0
- package/dist/database.d.ts +46 -0
- package/dist/database.js +56 -0
- package/dist/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,6 +73,26 @@ const active = await db.count("users", { active: 1 });
|
|
|
73
73
|
// Exists check
|
|
74
74
|
const exists = await db.exists("users", { email: "alice@example.com" });
|
|
75
75
|
|
|
76
|
+
// Upsert — INSERT OR REPLACE (relies on PRIMARY KEY / UNIQUE constraints)
|
|
77
|
+
await db.upsert("users", { id: 1, email: "alice@example.com", name: "Alice" });
|
|
78
|
+
|
|
79
|
+
// Upsert — explicit conflict target
|
|
80
|
+
await db.upsert(
|
|
81
|
+
"users",
|
|
82
|
+
{ email: "alice@example.com", name: "Alice" },
|
|
83
|
+
{ conflictColumns: ["email"] }
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Bulk upsert
|
|
87
|
+
await db.upsertMany(
|
|
88
|
+
"users",
|
|
89
|
+
[
|
|
90
|
+
{ email: "alice@example.com", name: "Alice" },
|
|
91
|
+
{ email: "bob@example.com", name: "Bob" },
|
|
92
|
+
],
|
|
93
|
+
{ conflictColumns: ["email"] }
|
|
94
|
+
);
|
|
95
|
+
|
|
76
96
|
// Update
|
|
77
97
|
await db.update("users", { name: "Alice Smith" }, { id: 1 });
|
|
78
98
|
|
|
@@ -168,6 +188,46 @@ await db.insertMany("posts", [
|
|
|
168
188
|
]);
|
|
169
189
|
```
|
|
170
190
|
|
|
191
|
+
#### `db.upsert(table, data, options?)` → `ExecResult`
|
|
192
|
+
|
|
193
|
+
Insert a row or update it on conflict.
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
// INSERT OR REPLACE — relies on PRIMARY KEY / UNIQUE constraints
|
|
197
|
+
await db.upsert("users", { id: 1, email: "alice@example.com", name: "Alice" });
|
|
198
|
+
|
|
199
|
+
// INSERT … ON CONFLICT(email) DO UPDATE SET name = excluded.name
|
|
200
|
+
await db.upsert(
|
|
201
|
+
"users",
|
|
202
|
+
{ email: "alice@example.com", name: "Alice" },
|
|
203
|
+
{ conflictColumns: ["email"] }
|
|
204
|
+
);
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### `db.upsertMany(table, rows, options?)` → `ExecResult`
|
|
208
|
+
|
|
209
|
+
Bulk version of `upsert`.
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
await db.upsertMany(
|
|
213
|
+
"users",
|
|
214
|
+
[
|
|
215
|
+
{ email: "alice@example.com", name: "Alice" },
|
|
216
|
+
{ email: "bob@example.com", name: "Bob" },
|
|
217
|
+
],
|
|
218
|
+
{ conflictColumns: ["email"] }
|
|
219
|
+
);
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**`UpsertOptions` fields:**
|
|
223
|
+
|
|
224
|
+
| Field | Type | Description |
|
|
225
|
+
| ------------------ | ---------- | --------------------------------------------------------------------------------------------------------------- |
|
|
226
|
+
| `conflictColumns` | `string[]` | Conflict-target columns. Omit to use `INSERT OR REPLACE` (relies on PRIMARY KEY / UNIQUE). |
|
|
227
|
+
| `updateColumns` | `string[]` | Columns to overwrite on conflict. Defaults to all columns **not** in `conflictColumns`. Pass `[]` for DO NOTHING. |
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
171
231
|
#### `db.update(table, data, where)` → `ExecResult`
|
|
172
232
|
|
|
173
233
|
```ts
|
package/dist/database.d.ts
CHANGED
|
@@ -25,6 +25,21 @@ export interface CreateIndexOptions {
|
|
|
25
25
|
unique?: boolean;
|
|
26
26
|
ifNotExists?: boolean;
|
|
27
27
|
}
|
|
28
|
+
export interface UpsertOptions {
|
|
29
|
+
/**
|
|
30
|
+
* Column(s) that form the conflict target.
|
|
31
|
+
* When provided the statement becomes:
|
|
32
|
+
* INSERT INTO … ON CONFLICT(col1, col2) DO UPDATE SET …
|
|
33
|
+
* When omitted `INSERT OR REPLACE` is used instead (relies on the
|
|
34
|
+
* table's PRIMARY KEY / UNIQUE constraints).
|
|
35
|
+
*/
|
|
36
|
+
conflictColumns?: string[];
|
|
37
|
+
/**
|
|
38
|
+
* Columns to update on conflict.
|
|
39
|
+
* Defaults to all columns that are NOT in `conflictColumns`.
|
|
40
|
+
*/
|
|
41
|
+
updateColumns?: string[];
|
|
42
|
+
}
|
|
28
43
|
/**
|
|
29
44
|
* High-level database client.
|
|
30
45
|
* Accepts any `IAdapter` — currently `HttpAdapter`, extensible to direct
|
|
@@ -76,6 +91,37 @@ export declare class Database {
|
|
|
76
91
|
* ]);
|
|
77
92
|
*/
|
|
78
93
|
insertMany(table: string, rows: Record<string, unknown>[]): Promise<ExecResult>;
|
|
94
|
+
/**
|
|
95
|
+
* Upsert a single row — insert or update on conflict.
|
|
96
|
+
*
|
|
97
|
+
* Without `conflictColumns` the statement uses `INSERT OR REPLACE` which
|
|
98
|
+
* relies on the table's PRIMARY KEY / UNIQUE constraints.
|
|
99
|
+
*
|
|
100
|
+
* With `conflictColumns` it uses the more precise:
|
|
101
|
+
* `INSERT … ON CONFLICT(cols) DO UPDATE SET …`
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* // relies on PRIMARY KEY / UNIQUE constraints
|
|
105
|
+
* await db.upsert("users", { id: 1, email: "a@b.com", name: "Alice" });
|
|
106
|
+
*
|
|
107
|
+
* // explicit conflict target
|
|
108
|
+
* await db.upsert("users", { email: "a@b.com", name: "Alice" }, { conflictColumns: ["email"] });
|
|
109
|
+
*/
|
|
110
|
+
upsert(table: string, data: Record<string, unknown>, options?: UpsertOptions): Promise<ExecResult>;
|
|
111
|
+
/**
|
|
112
|
+
* Upsert multiple rows — insert or update each row on conflict.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* await db.upsertMany(
|
|
116
|
+
* "users",
|
|
117
|
+
* [
|
|
118
|
+
* { email: "a@b.com", name: "Alice" },
|
|
119
|
+
* { email: "b@c.com", name: "Bob" },
|
|
120
|
+
* ],
|
|
121
|
+
* { conflictColumns: ["email"] }
|
|
122
|
+
* );
|
|
123
|
+
*/
|
|
124
|
+
upsertMany(table: string, rows: Record<string, unknown>[], options?: UpsertOptions): Promise<ExecResult>;
|
|
79
125
|
/**
|
|
80
126
|
* Update rows matching `where`.
|
|
81
127
|
*
|
package/dist/database.js
CHANGED
|
@@ -114,6 +114,62 @@ export class Database {
|
|
|
114
114
|
const bindings = rows.flatMap((row) => keys.map((k) => row[k]));
|
|
115
115
|
return this._write(`INSERT INTO "${table}" (${cols}) VALUES ${rowPlaceholders}`, bindings);
|
|
116
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Upsert a single row — insert or update on conflict.
|
|
119
|
+
*
|
|
120
|
+
* Without `conflictColumns` the statement uses `INSERT OR REPLACE` which
|
|
121
|
+
* relies on the table's PRIMARY KEY / UNIQUE constraints.
|
|
122
|
+
*
|
|
123
|
+
* With `conflictColumns` it uses the more precise:
|
|
124
|
+
* `INSERT … ON CONFLICT(cols) DO UPDATE SET …`
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* // relies on PRIMARY KEY / UNIQUE constraints
|
|
128
|
+
* await db.upsert("users", { id: 1, email: "a@b.com", name: "Alice" });
|
|
129
|
+
*
|
|
130
|
+
* // explicit conflict target
|
|
131
|
+
* await db.upsert("users", { email: "a@b.com", name: "Alice" }, { conflictColumns: ["email"] });
|
|
132
|
+
*/
|
|
133
|
+
async upsert(table, data, options = {}) {
|
|
134
|
+
return this.upsertMany(table, [data], options);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Upsert multiple rows — insert or update each row on conflict.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* await db.upsertMany(
|
|
141
|
+
* "users",
|
|
142
|
+
* [
|
|
143
|
+
* { email: "a@b.com", name: "Alice" },
|
|
144
|
+
* { email: "b@c.com", name: "Bob" },
|
|
145
|
+
* ],
|
|
146
|
+
* { conflictColumns: ["email"] }
|
|
147
|
+
* );
|
|
148
|
+
*/
|
|
149
|
+
async upsertMany(table, rows, options = {}) {
|
|
150
|
+
if (rows.length === 0)
|
|
151
|
+
return { rowsAffected: 0, lastInsertRowid: null };
|
|
152
|
+
const keys = Object.keys(rows[0]);
|
|
153
|
+
const cols = keys.map((k) => `"${k}"`).join(", ");
|
|
154
|
+
const rowPlaceholders = rows
|
|
155
|
+
.map(() => `(${keys.map(() => "?").join(", ")})`)
|
|
156
|
+
.join(", ");
|
|
157
|
+
const bindings = rows.flatMap((row) => keys.map((k) => row[k]));
|
|
158
|
+
if (options.conflictColumns && options.conflictColumns.length > 0) {
|
|
159
|
+
const conflictCols = options.conflictColumns
|
|
160
|
+
.map((c) => `"${c}"`)
|
|
161
|
+
.join(", ");
|
|
162
|
+
const updateCols = options.updateColumns ??
|
|
163
|
+
keys.filter((k) => !options.conflictColumns.includes(k));
|
|
164
|
+
const conflictClause = updateCols.length === 0
|
|
165
|
+
? `ON CONFLICT(${conflictCols}) DO NOTHING`
|
|
166
|
+
: `ON CONFLICT(${conflictCols}) DO UPDATE SET ${updateCols
|
|
167
|
+
.map((k) => `"${k}" = excluded."${k}"`)
|
|
168
|
+
.join(", ")}`;
|
|
169
|
+
return this._write(`INSERT INTO "${table}" (${cols}) VALUES ${rowPlaceholders} ${conflictClause}`, bindings);
|
|
170
|
+
}
|
|
171
|
+
return this._write(`INSERT OR REPLACE INTO "${table}" (${cols}) VALUES ${rowPlaceholders}`, bindings);
|
|
172
|
+
}
|
|
117
173
|
/**
|
|
118
174
|
* Update rows matching `where`.
|
|
119
175
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { Database } from "./database.js";
|
|
2
|
-
export type { ColumnDef, CreateIndexOptions, CreateTableOptions, FindOptions, OrderDirection, WhereClause } from "./database.js";
|
|
2
|
+
export type { ColumnDef, CreateIndexOptions, CreateTableOptions, FindOptions, OrderDirection, UpsertOptions, WhereClause } from "./database.js";
|
|
3
3
|
export { HttpAdapter } from "./adapters/http.js";
|
|
4
4
|
export type { HttpAdapterOptions } from "./adapters/http.js";
|
|
5
5
|
export type { ColumnHeader, ExecResult, IAdapter, QueryResult, RawResult } from "./adapters/types.js";
|