@dockstat/sqlite-wrapper 1.0.0 → 1.1.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 +680 -389
- package/dist/index.d.ts +649 -122
- package/dist/index.js +20 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,138 +1,212 @@
|
|
|
1
|
-
# @dockstat/
|
|
1
|
+
# @dockstat/sqlite-wrapper
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A comprehensive TypeScript wrapper around Bun's `bun:sqlite` with type-safe table creation, advanced query building, and full SQLite feature support.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
* **`QueryBuilder<T>`** — expressive, chainable query builder with:
|
|
5
|
+
## Features
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
**INSERT operations:**
|
|
17
|
-
* single/bulk inserts `.insert(...)`
|
|
18
|
-
* conflict resolution `.insertOrIgnore(...)`, `.insertOrReplace(...)`
|
|
19
|
-
|
|
20
|
-
**UPDATE operations:**
|
|
21
|
-
* safe updates `.update(...)` (requires WHERE conditions)
|
|
22
|
-
|
|
23
|
-
**DELETE operations:**
|
|
24
|
-
* safe deletes `.delete()` (requires WHERE conditions)
|
|
25
|
-
|
|
26
|
-
> **Important** — Bun's `bun:sqlite` currently doesn't provide a JS `registerFunction` to add custom SQL functions. Regex conditions added with `whereRgx()` are collected and applied **in JavaScript** after fetching rows that match the non-regex SQL conditions. See the **Performance** section below for implications.
|
|
7
|
+
* **Type-safe table creation** with column helpers and comprehensive constraint support
|
|
8
|
+
* **Expressive QueryBuilder** with chainable operations for SELECT, INSERT, UPDATE, DELETE
|
|
9
|
+
* **Advanced WHERE conditions** including regex filtering, raw SQL, operators, and more
|
|
10
|
+
* **Conflict resolution** for INSERT operations (OR IGNORE, OR REPLACE, etc.)
|
|
11
|
+
* **Safety features** requiring WHERE conditions for UPDATE/DELETE operations
|
|
12
|
+
* **Full SQLite support** including JSON columns, generated columns, foreign keys, and more
|
|
13
|
+
* **Performance optimizations** with proper parameter binding and transaction support
|
|
27
14
|
|
|
28
15
|
---
|
|
29
16
|
|
|
30
|
-
## Quick
|
|
17
|
+
## Quick Start
|
|
31
18
|
|
|
32
19
|
```ts
|
|
33
|
-
import DB,
|
|
20
|
+
import { DB, column, sql } from "@dockstat/sqlite-wrapper";
|
|
34
21
|
|
|
35
22
|
interface User {
|
|
36
|
-
id
|
|
23
|
+
id?: number;
|
|
37
24
|
name: string;
|
|
38
25
|
email: string;
|
|
39
26
|
type: string;
|
|
40
|
-
|
|
27
|
+
active?: boolean;
|
|
28
|
+
metadata?: object;
|
|
29
|
+
created_at?: number;
|
|
30
|
+
updated_at?: number;
|
|
41
31
|
}
|
|
42
32
|
|
|
43
|
-
// Create
|
|
33
|
+
// Create database with optimized settings
|
|
44
34
|
const db = new DB("app.db", {
|
|
45
35
|
pragmas: [
|
|
46
36
|
["journal_mode", "WAL"],
|
|
47
37
|
["synchronous", "NORMAL"],
|
|
48
|
-
["foreign_keys", "ON"]
|
|
49
|
-
|
|
50
|
-
loadExtensions: [
|
|
51
|
-
"/absolute/path/to/my_extension" // optional compiled SQLite extension
|
|
38
|
+
["foreign_keys", "ON"],
|
|
39
|
+
["cache_size", -64000]
|
|
52
40
|
]
|
|
53
41
|
});
|
|
54
42
|
|
|
55
|
-
//
|
|
56
|
-
db.createTable(
|
|
57
|
-
|
|
58
|
-
{
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
);
|
|
43
|
+
// Create table with type-safe column definitions
|
|
44
|
+
db.createTable("users", {
|
|
45
|
+
id: column.id(), // INTEGER PRIMARY KEY AUTOINCREMENT
|
|
46
|
+
name: column.text({ notNull: true }),
|
|
47
|
+
email: column.text({ unique: true, notNull: true }),
|
|
48
|
+
type: column.enum(["admin", "user", "guest"], { default: "user" }),
|
|
49
|
+
active: column.boolean({ default: true }),
|
|
50
|
+
metadata: column.json({ validateJson: true }),
|
|
51
|
+
created_at: column.createdAt(), // Auto-timestamp
|
|
52
|
+
updated_at: column.updatedAt()
|
|
53
|
+
});
|
|
67
54
|
|
|
68
|
-
//
|
|
69
|
-
const
|
|
70
|
-
.table<User>("users")
|
|
55
|
+
// Query with the powerful QueryBuilder
|
|
56
|
+
const users = db.table<User>("users")
|
|
71
57
|
.select(["id", "name", "email"])
|
|
72
|
-
.where({
|
|
73
|
-
.
|
|
58
|
+
.where({ active: true })
|
|
59
|
+
.whereRgx({ email: /@company\.com$/i })
|
|
60
|
+
.orderBy("created_at")
|
|
74
61
|
.desc()
|
|
75
62
|
.limit(10)
|
|
76
63
|
.all();
|
|
77
64
|
|
|
78
|
-
console.log(
|
|
65
|
+
console.log(users);
|
|
66
|
+
```
|
|
79
67
|
|
|
80
|
-
|
|
81
|
-
const gmailUsers = db
|
|
82
|
-
.table<User>("users")
|
|
83
|
-
.select(["id", "email"])
|
|
84
|
-
.where({ type: "container" }) // SQL
|
|
85
|
-
.whereRgx({ email: /@gmail\.com$/i }) // client-side regex
|
|
86
|
-
.all();
|
|
68
|
+
---
|
|
87
69
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const insertResult = db
|
|
92
|
-
.table<User>("users")
|
|
93
|
-
.insert({
|
|
94
|
-
name: "John Doe",
|
|
95
|
-
email: "john@example.com",
|
|
96
|
-
type: "container"
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
console.log(`Inserted user with ID: ${insertResult.insertId}`);
|
|
100
|
-
|
|
101
|
-
// INSERT - multiple rows with conflict resolution
|
|
102
|
-
const bulkResult = db
|
|
103
|
-
.table<User>("users")
|
|
104
|
-
.insertOrIgnore([
|
|
105
|
-
{ name: "Alice", email: "alice@example.com", type: "container" },
|
|
106
|
-
{ name: "Bob", email: "bob@example.com", type: "container" }
|
|
107
|
-
]);
|
|
108
|
-
|
|
109
|
-
console.log(`Inserted ${bulkResult.changes} users`);
|
|
110
|
-
|
|
111
|
-
// UPDATE - with WHERE conditions
|
|
112
|
-
const updateResult = db
|
|
113
|
-
.table<User>("users")
|
|
114
|
-
.where({ type: "container" })
|
|
115
|
-
.whereRgx({ email: /@gmail\.com$/i })
|
|
116
|
-
.update({ type: "gmail_user" });
|
|
117
|
-
|
|
118
|
-
console.log(`Updated ${updateResult.changes} users`);
|
|
119
|
-
|
|
120
|
-
// DELETE - with WHERE conditions
|
|
121
|
-
const deleteResult = db
|
|
122
|
-
.table<User>("users")
|
|
123
|
-
.where({ type: "gmail_user" })
|
|
124
|
-
.delete();
|
|
70
|
+
## Table Creation
|
|
71
|
+
|
|
72
|
+
### Modern Column Helpers (Recommended)
|
|
125
73
|
|
|
126
|
-
|
|
74
|
+
Create tables with type-safe column definitions and comprehensive SQLite feature support:
|
|
127
75
|
|
|
128
|
-
|
|
76
|
+
```ts
|
|
77
|
+
import { DB, column, sql } from "@dockstat/sqlite-wrapper";
|
|
78
|
+
|
|
79
|
+
db.createTable("products", {
|
|
80
|
+
// Primary keys and auto-increment
|
|
81
|
+
id: column.id(), // INTEGER PRIMARY KEY AUTOINCREMENT
|
|
82
|
+
uuid: column.uuid({ generateDefault: true }),
|
|
83
|
+
|
|
84
|
+
// Text columns with constraints
|
|
85
|
+
name: column.text({ notNull: true }),
|
|
86
|
+
description: column.text({ length: 1000 }),
|
|
87
|
+
sku: column.varchar(50, { unique: true }),
|
|
88
|
+
|
|
89
|
+
// Numeric columns
|
|
90
|
+
price: column.numeric({ precision: 10, scale: 2, check: "price > 0" }),
|
|
91
|
+
quantity: column.integer({ default: 0, check: "quantity >= 0" }),
|
|
92
|
+
weight: column.real(),
|
|
93
|
+
|
|
94
|
+
// Boolean with validation
|
|
95
|
+
active: column.boolean({ default: true }),
|
|
96
|
+
|
|
97
|
+
// Enums with validation
|
|
98
|
+
category: column.enum(["electronics", "clothing", "books"], {
|
|
99
|
+
default: "electronics"
|
|
100
|
+
}),
|
|
101
|
+
|
|
102
|
+
// JSON data
|
|
103
|
+
specifications: column.json({ validateJson: true }),
|
|
104
|
+
tags: column.json(),
|
|
105
|
+
|
|
106
|
+
// Dates and timestamps
|
|
107
|
+
manufactured_date: column.date(),
|
|
108
|
+
created_at: column.createdAt(), // Auto-managed
|
|
109
|
+
updated_at: column.updatedAt(),
|
|
110
|
+
|
|
111
|
+
// Foreign keys
|
|
112
|
+
supplier_id: column.foreignKey("suppliers", "id", {
|
|
113
|
+
onDelete: "SET NULL",
|
|
114
|
+
onUpdate: "CASCADE"
|
|
115
|
+
}),
|
|
116
|
+
|
|
117
|
+
// Generated columns
|
|
118
|
+
display_price: {
|
|
119
|
+
type: "TEXT",
|
|
120
|
+
generated: {
|
|
121
|
+
expression: "printf('$%.2f', price)",
|
|
122
|
+
stored: false // VIRTUAL column
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}, {
|
|
126
|
+
constraints: {
|
|
127
|
+
// Table-level constraints
|
|
128
|
+
unique: [["sku", "supplier_id"]], // Composite unique
|
|
129
|
+
check: ["price > 0 OR category = 'free'"],
|
|
130
|
+
foreignKeys: [{
|
|
131
|
+
columns: ["supplier_id"],
|
|
132
|
+
references: {
|
|
133
|
+
table: "suppliers",
|
|
134
|
+
columns: ["id"],
|
|
135
|
+
onDelete: "CASCADE"
|
|
136
|
+
}
|
|
137
|
+
}]
|
|
138
|
+
},
|
|
139
|
+
comment: "Product catalog with full specifications"
|
|
140
|
+
});
|
|
129
141
|
```
|
|
130
142
|
|
|
131
|
-
|
|
143
|
+
### Available Column Types
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
// Integer types
|
|
147
|
+
column.integer()
|
|
148
|
+
column.integer({ size: "BIGINT" })
|
|
149
|
+
column.id() // INTEGER PRIMARY KEY AUTOINCREMENT
|
|
150
|
+
|
|
151
|
+
// Text types
|
|
152
|
+
column.text()
|
|
153
|
+
column.varchar(255)
|
|
154
|
+
column.char(10)
|
|
155
|
+
column.uuid()
|
|
156
|
+
|
|
157
|
+
// Numeric types
|
|
158
|
+
column.real()
|
|
159
|
+
column.numeric({ precision: 10, scale: 2 })
|
|
160
|
+
column.numeric({ variant: "DECIMAL" })
|
|
161
|
+
|
|
162
|
+
// Date/Time types
|
|
163
|
+
column.date()
|
|
164
|
+
column.time()
|
|
165
|
+
column.datetime()
|
|
166
|
+
column.timestamp()
|
|
167
|
+
column.createdAt() // Auto-managed creation timestamp
|
|
168
|
+
column.updatedAt() // Auto-managed update timestamp
|
|
169
|
+
|
|
170
|
+
// Special types
|
|
171
|
+
column.boolean() // INTEGER with CHECK constraint
|
|
172
|
+
column.json() // TEXT with optional validation
|
|
173
|
+
column.blob()
|
|
174
|
+
column.enum(["value1", "value2"])
|
|
175
|
+
|
|
176
|
+
// Foreign keys
|
|
177
|
+
column.foreignKey("other_table", "other_column", {
|
|
178
|
+
onDelete: "CASCADE",
|
|
179
|
+
onUpdate: "RESTRICT"
|
|
180
|
+
})
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Column Constraints
|
|
184
|
+
|
|
185
|
+
All column helpers support comprehensive constraints:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
column.text({
|
|
189
|
+
notNull: true,
|
|
190
|
+
unique: true,
|
|
191
|
+
default: "default_value",
|
|
192
|
+
check: "length(column_name) > 0",
|
|
193
|
+
collate: "NOCASE",
|
|
194
|
+
comment: "User description"
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
// Default value expressions
|
|
198
|
+
column.timestamp({
|
|
199
|
+
default: sql.currentTimestamp()
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
column.text({
|
|
203
|
+
default: sql.raw("upper(hex(randomblob(16)))")
|
|
204
|
+
})
|
|
205
|
+
```
|
|
132
206
|
|
|
133
|
-
|
|
207
|
+
### Legacy Table Creation
|
|
134
208
|
|
|
135
|
-
|
|
209
|
+
Object-style (still supported):
|
|
136
210
|
|
|
137
211
|
```ts
|
|
138
212
|
db.createTable("posts", {
|
|
@@ -144,435 +218,652 @@ db.createTable("posts", {
|
|
|
144
218
|
}, { ifNotExists: true });
|
|
145
219
|
```
|
|
146
220
|
|
|
147
|
-
|
|
221
|
+
String-style (still supported):
|
|
148
222
|
|
|
149
223
|
```ts
|
|
150
|
-
db.createTable(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
);
|
|
224
|
+
db.createTable("events", `
|
|
225
|
+
id INTEGER PRIMARY KEY,
|
|
226
|
+
name TEXT NOT NULL,
|
|
227
|
+
occurred_at INTEGER NOT NULL
|
|
228
|
+
`);
|
|
156
229
|
```
|
|
157
230
|
|
|
158
231
|
---
|
|
159
232
|
|
|
160
|
-
##
|
|
233
|
+
## Query Building
|
|
161
234
|
|
|
162
|
-
|
|
235
|
+
### SELECT Operations
|
|
163
236
|
|
|
164
237
|
```ts
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
238
|
+
// Basic selection
|
|
239
|
+
const users = db.table<User>("users")
|
|
240
|
+
.select(["id", "name", "email"])
|
|
241
|
+
.where({ active: true })
|
|
242
|
+
.all();
|
|
243
|
+
|
|
244
|
+
// Complex conditions
|
|
245
|
+
const results = db.table<User>("users")
|
|
246
|
+
.where({ type: "admin" })
|
|
247
|
+
.whereIn("id", [1, 2, 3, 4, 5])
|
|
248
|
+
.whereOp("created_at", ">", Date.now() - 86400000)
|
|
249
|
+
.whereRaw("name LIKE ?", ["%admin%"])
|
|
250
|
+
.whereRgx({ email: /@company\.com$/i }) // Client-side regex
|
|
251
|
+
.orderBy("created_at")
|
|
252
|
+
.desc()
|
|
253
|
+
.limit(50)
|
|
254
|
+
.offset(100)
|
|
255
|
+
.all();
|
|
256
|
+
|
|
257
|
+
// Result methods
|
|
258
|
+
const allUsers = db.table<User>("users").all();
|
|
259
|
+
const firstUser = db.table<User>("users").first();
|
|
260
|
+
const singleUser = db.table<User>("users").where({ id: 1 }).get();
|
|
261
|
+
const userCount = db.table<User>("users").where({ active: true }).count();
|
|
262
|
+
const userExists = db.table<User>("users").where({ email: "test@example.com" }).exists();
|
|
263
|
+
|
|
264
|
+
// Single values
|
|
265
|
+
const userName = db.table<User>("users").where({ id: 1 }).value("name");
|
|
266
|
+
const allNames = db.table<User>("users").pluck("name");
|
|
171
267
|
```
|
|
172
268
|
|
|
173
|
-
|
|
269
|
+
### INSERT Operations
|
|
174
270
|
|
|
175
271
|
```ts
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
272
|
+
// Single insert
|
|
273
|
+
const result = db.table<User>("users").insert({
|
|
274
|
+
name: "John Doe",
|
|
275
|
+
email: "john@example.com",
|
|
276
|
+
type: "user"
|
|
277
|
+
});
|
|
278
|
+
console.log(`Inserted user with ID: ${result.insertId}`);
|
|
180
279
|
|
|
181
|
-
|
|
280
|
+
// Bulk insert
|
|
281
|
+
const users = [
|
|
282
|
+
{ name: "Alice", email: "alice@example.com" },
|
|
283
|
+
{ name: "Bob", email: "bob@example.com" }
|
|
284
|
+
];
|
|
285
|
+
const bulkResult = db.table<User>("users").insert(users);
|
|
182
286
|
|
|
183
|
-
|
|
287
|
+
// Conflict resolution
|
|
288
|
+
db.table<User>("users").insertOrIgnore(userData);
|
|
289
|
+
db.table<User>("users").insertOrReplace(userData);
|
|
184
290
|
|
|
185
|
-
|
|
291
|
+
// Custom conflict resolution
|
|
292
|
+
db.table<User>("users").insert(userData, {
|
|
293
|
+
orIgnore: true,
|
|
294
|
+
orReplace: false,
|
|
295
|
+
orAbort: false,
|
|
296
|
+
orFail: false,
|
|
297
|
+
orRollback: false
|
|
298
|
+
});
|
|
186
299
|
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
]
|
|
300
|
+
// Insert and get back the row
|
|
301
|
+
const newUser = db.table<User>("users").insertAndGet({
|
|
302
|
+
name: "Jane",
|
|
303
|
+
email: "jane@example.com"
|
|
192
304
|
});
|
|
305
|
+
|
|
306
|
+
// Batch insert with transaction
|
|
307
|
+
db.table<User>("users").insertBatch(largeUserArray);
|
|
193
308
|
```
|
|
194
309
|
|
|
195
|
-
|
|
310
|
+
### UPDATE Operations
|
|
196
311
|
|
|
197
|
-
|
|
198
|
-
db.loadExtension("/absolute/path/to/my_extension");
|
|
199
|
-
```
|
|
312
|
+
All UPDATE operations require WHERE conditions for safety:
|
|
200
313
|
|
|
201
|
-
|
|
314
|
+
```ts
|
|
315
|
+
// Basic update
|
|
316
|
+
const result = db.table<User>("users")
|
|
317
|
+
.where({ id: 1 })
|
|
318
|
+
.update({ name: "Updated Name" });
|
|
202
319
|
|
|
203
|
-
|
|
320
|
+
// Complex conditions
|
|
321
|
+
db.table<User>("users")
|
|
322
|
+
.where({ type: "user" })
|
|
323
|
+
.whereRgx({ email: /@oldcompany\.com$/i })
|
|
324
|
+
.update({ type: "migrated_user" });
|
|
204
325
|
|
|
205
|
-
|
|
326
|
+
// Increment/decrement
|
|
327
|
+
db.table<User>("users")
|
|
328
|
+
.where({ id: 1 })
|
|
329
|
+
.increment("login_count", 1);
|
|
206
330
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
```
|
|
331
|
+
// Upsert (insert or update)
|
|
332
|
+
db.table<User>("users").upsert({
|
|
333
|
+
id: 1,
|
|
334
|
+
name: "John",
|
|
335
|
+
email: "john@example.com"
|
|
336
|
+
});
|
|
214
337
|
|
|
215
|
-
|
|
338
|
+
// Update and get affected rows
|
|
339
|
+
const updatedUsers = db.table<User>("users")
|
|
340
|
+
.where({ active: false })
|
|
341
|
+
.updateAndGet({ active: true });
|
|
216
342
|
|
|
217
|
-
|
|
218
|
-
|
|
343
|
+
// Batch update
|
|
344
|
+
db.table<User>("users").updateBatch([
|
|
345
|
+
{ where: { id: 1 }, data: { name: "User 1" } },
|
|
346
|
+
{ where: { id: 2 }, data: { name: "User 2" } }
|
|
347
|
+
]);
|
|
219
348
|
```
|
|
220
349
|
|
|
221
|
-
|
|
350
|
+
### DELETE Operations
|
|
351
|
+
|
|
352
|
+
All DELETE operations require WHERE conditions for safety:
|
|
222
353
|
|
|
223
354
|
```ts
|
|
224
|
-
|
|
225
|
-
|
|
355
|
+
// Basic delete
|
|
356
|
+
const result = db.table<User>("users")
|
|
357
|
+
.where({ active: false })
|
|
358
|
+
.delete();
|
|
226
359
|
|
|
227
|
-
|
|
360
|
+
// Complex conditions
|
|
361
|
+
db.table<User>("users")
|
|
362
|
+
.where({ type: "temporary" })
|
|
363
|
+
.whereOp("created_at", "<", Date.now() - 604800000)
|
|
364
|
+
.delete();
|
|
228
365
|
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
366
|
+
// Delete and get deleted rows
|
|
367
|
+
const deletedUsers = db.table<User>("users")
|
|
368
|
+
.where({ active: false })
|
|
369
|
+
.deleteAndGet();
|
|
232
370
|
|
|
233
|
-
|
|
371
|
+
// Soft delete
|
|
372
|
+
db.table<User>("users")
|
|
373
|
+
.where({ id: 1 })
|
|
374
|
+
.softDelete("deleted_at", Date.now());
|
|
234
375
|
|
|
235
|
-
|
|
236
|
-
|
|
376
|
+
// Restore soft deleted
|
|
377
|
+
db.table<User>("users")
|
|
378
|
+
.where({ id: 1 })
|
|
379
|
+
.restore("deleted_at");
|
|
380
|
+
|
|
381
|
+
// Utility methods
|
|
382
|
+
db.table<User>("users").deleteOlderThan("created_at", Date.now() - 2592000000);
|
|
383
|
+
db.table<User>("users").deleteDuplicates(["email"]);
|
|
384
|
+
db.table<User>("users").truncate(); // Delete all rows
|
|
385
|
+
|
|
386
|
+
// Batch delete
|
|
387
|
+
db.table<User>("users").deleteBatch([
|
|
388
|
+
{ type: "temp" },
|
|
389
|
+
{ active: false }
|
|
390
|
+
]);
|
|
237
391
|
```
|
|
238
392
|
|
|
239
393
|
---
|
|
240
394
|
|
|
241
|
-
##
|
|
395
|
+
## Advanced Features
|
|
242
396
|
|
|
243
|
-
|
|
397
|
+
### JSON Column Support
|
|
244
398
|
|
|
245
399
|
```ts
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
.all();
|
|
255
|
-
```
|
|
400
|
+
interface UserWithMetadata {
|
|
401
|
+
id: number;
|
|
402
|
+
name: string;
|
|
403
|
+
metadata: {
|
|
404
|
+
preferences: object;
|
|
405
|
+
settings: object;
|
|
406
|
+
};
|
|
407
|
+
}
|
|
256
408
|
|
|
257
|
-
|
|
409
|
+
// Configure JSON columns for automatic serialization/deserialization
|
|
410
|
+
const users = db.table<UserWithMetadata>("users", {
|
|
411
|
+
jsonColumns: ["metadata"]
|
|
412
|
+
});
|
|
258
413
|
|
|
259
|
-
|
|
414
|
+
// Insert with automatic JSON serialization
|
|
415
|
+
users.insert({
|
|
416
|
+
name: "John",
|
|
417
|
+
metadata: {
|
|
418
|
+
preferences: { theme: "dark" },
|
|
419
|
+
settings: { notifications: true }
|
|
420
|
+
}
|
|
421
|
+
});
|
|
260
422
|
|
|
261
|
-
|
|
423
|
+
// Query returns properly deserialized objects
|
|
424
|
+
const user = users.where({ id: 1 }).first();
|
|
425
|
+
console.log(user.metadata.preferences.theme); // "dark"
|
|
426
|
+
```
|
|
262
427
|
|
|
263
|
-
|
|
428
|
+
### Regex Filtering
|
|
264
429
|
|
|
265
430
|
```ts
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
.
|
|
431
|
+
// Client-side regex filtering
|
|
432
|
+
const gmailUsers = db.table<User>("users")
|
|
433
|
+
.where({ active: true }) // SQL filter first
|
|
434
|
+
.whereRgx({ email: /@gmail\.com$/i }) // Then regex filter
|
|
269
435
|
.all();
|
|
270
|
-
```
|
|
271
436
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
437
|
+
// Multiple regex conditions
|
|
438
|
+
const results = db.table<User>("users")
|
|
439
|
+
.whereRgx({
|
|
440
|
+
email: /@(gmail|yahoo)\.com$/i,
|
|
441
|
+
name: /^[A-Z]/
|
|
442
|
+
})
|
|
443
|
+
.all();
|
|
276
444
|
```
|
|
277
445
|
|
|
278
|
-
|
|
446
|
+
### Raw SQL and Advanced Conditions
|
|
279
447
|
|
|
280
448
|
```ts
|
|
449
|
+
// Raw WHERE expressions
|
|
281
450
|
db.table<User>("users")
|
|
282
|
-
.
|
|
283
|
-
|
|
451
|
+
.whereRaw("created_at > ? AND (type = ? OR type = ?)", [
|
|
452
|
+
Date.now() - 86400000,
|
|
453
|
+
"admin",
|
|
454
|
+
"moderator"
|
|
455
|
+
])
|
|
284
456
|
.all();
|
|
285
|
-
```
|
|
286
457
|
|
|
287
|
-
|
|
458
|
+
// Operator helpers
|
|
459
|
+
db.table<User>("users")
|
|
460
|
+
.whereOp("created_at", ">=", startDate)
|
|
461
|
+
.whereOp("created_at", "<=", endDate)
|
|
462
|
+
.whereOp("name", "LIKE", "%john%")
|
|
463
|
+
.all();
|
|
288
464
|
|
|
289
|
-
|
|
465
|
+
// BETWEEN clauses
|
|
466
|
+
db.table<User>("users")
|
|
467
|
+
.whereBetween("created_at", startDate, endDate)
|
|
468
|
+
.whereNotBetween("age", 0, 18)
|
|
469
|
+
.all();
|
|
290
470
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
471
|
+
// NULL checks
|
|
472
|
+
db.table<User>("users")
|
|
473
|
+
.whereNull("deleted_at")
|
|
474
|
+
.whereNotNull("email")
|
|
475
|
+
.all();
|
|
294
476
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
.
|
|
298
|
-
.
|
|
299
|
-
.desc()
|
|
300
|
-
.offset((page - 1) * pageSize)
|
|
301
|
-
.limit(pageSize)
|
|
477
|
+
// IN clauses
|
|
478
|
+
db.table<User>("users")
|
|
479
|
+
.whereIn("type", ["admin", "moderator"])
|
|
480
|
+
.whereNotIn("status", ["banned", "suspended"])
|
|
302
481
|
.all();
|
|
303
482
|
```
|
|
304
483
|
|
|
305
|
-
|
|
484
|
+
### Database Configuration
|
|
306
485
|
|
|
307
|
-
|
|
486
|
+
```ts
|
|
487
|
+
// Constructor options
|
|
488
|
+
const db = new DB("app.db", {
|
|
489
|
+
pragmas: [
|
|
490
|
+
["journal_mode", "WAL"],
|
|
491
|
+
["synchronous", "NORMAL"],
|
|
492
|
+
["foreign_keys", "ON"],
|
|
493
|
+
["cache_size", -64000],
|
|
494
|
+
["temp_store", "memory"]
|
|
495
|
+
],
|
|
496
|
+
loadExtensions: [
|
|
497
|
+
"/path/to/regexp_extension.so"
|
|
498
|
+
]
|
|
499
|
+
});
|
|
308
500
|
|
|
309
|
-
|
|
501
|
+
// Runtime PRAGMA management
|
|
502
|
+
db.pragma("cache_size", -32000);
|
|
503
|
+
const journalMode = db.pragma("journal_mode");
|
|
310
504
|
|
|
311
|
-
|
|
505
|
+
// Extension loading
|
|
506
|
+
db.loadExtension("/path/to/custom_extension.so");
|
|
312
507
|
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
name: "John Doe",
|
|
318
|
-
email: "john@example.com",
|
|
319
|
-
type: "container"
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
console.log(result.insertId, result.changes);
|
|
508
|
+
// Database info
|
|
509
|
+
const tableInfo = db.getTableInfo("users");
|
|
510
|
+
const foreignKeys = db.getForeignKeys("users");
|
|
511
|
+
const indexes = db.getIndexes("users");
|
|
323
512
|
```
|
|
324
513
|
|
|
325
|
-
|
|
514
|
+
---
|
|
326
515
|
|
|
327
|
-
|
|
328
|
-
const users = [
|
|
329
|
-
{ name: "Alice", email: "alice@example.com", type: "container" },
|
|
330
|
-
{ name: "Bob", email: "bob@example.com", type: "container" }
|
|
331
|
-
];
|
|
516
|
+
## Performance Considerations
|
|
332
517
|
|
|
333
|
-
|
|
334
|
-
console.log(`Inserted ${result.changes} users`);
|
|
335
|
-
```
|
|
518
|
+
### Query Optimization
|
|
336
519
|
|
|
337
|
-
**
|
|
520
|
+
1. **Use SQL filters before regex**: Always use `.where()` conditions to reduce the dataset before applying `.whereRgx()` filters.
|
|
338
521
|
|
|
339
522
|
```ts
|
|
340
|
-
//
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
email:
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
// INSERT OR REPLACE - replace duplicates
|
|
347
|
-
const result2 = db.table<User>("users").insertOrReplace({
|
|
348
|
-
name: "Updated User",
|
|
349
|
-
email: "existing@example.com"
|
|
350
|
-
});
|
|
523
|
+
// Good: SQL filter first, then regex
|
|
524
|
+
db.table<User>("users")
|
|
525
|
+
.where({ active: true, type: "user" }) // Reduces dataset
|
|
526
|
+
.whereRgx({ email: /@company\.com$/i }) // Then regex filter
|
|
527
|
+
.all();
|
|
351
528
|
|
|
352
|
-
//
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
});
|
|
529
|
+
// Avoid: Regex on entire table
|
|
530
|
+
db.table<User>("users")
|
|
531
|
+
.whereRgx({ email: /@company\.com$/i })
|
|
532
|
+
.all();
|
|
357
533
|
```
|
|
358
534
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
## 9) UPDATE operations
|
|
362
|
-
|
|
363
|
-
**Basic update (requires WHERE conditions):**
|
|
535
|
+
2. **Use appropriate indexes**: Create indexes on frequently queried columns.
|
|
364
536
|
|
|
365
537
|
```ts
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
.update({ name: "Updated Name", type: "admin" });
|
|
370
|
-
|
|
371
|
-
console.log(`Updated ${result.changes} rows`);
|
|
538
|
+
db.createIndex("idx_users_email", "users", "email", { unique: true });
|
|
539
|
+
db.createIndex("idx_users_created_at", "users", "created_at");
|
|
540
|
+
db.createIndex("idx_users_type_active", "users", ["type", "active"]);
|
|
372
541
|
```
|
|
373
542
|
|
|
374
|
-
**
|
|
543
|
+
3. **Batch operations**: Use bulk operations for better performance.
|
|
375
544
|
|
|
376
545
|
```ts
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
546
|
+
// Good: Bulk insert
|
|
547
|
+
db.table<User>("users").insertBatch(largeArray);
|
|
548
|
+
|
|
549
|
+
// Avoid: Individual inserts
|
|
550
|
+
largeArray.forEach(user => db.table<User>("users").insert(user));
|
|
382
551
|
```
|
|
383
552
|
|
|
384
|
-
|
|
553
|
+
### Transaction Management
|
|
385
554
|
|
|
386
555
|
```ts
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
.
|
|
556
|
+
// Manual transactions
|
|
557
|
+
db.begin();
|
|
558
|
+
try {
|
|
559
|
+
db.table<User>("users").insert(userData1);
|
|
560
|
+
db.table<User>("users").insert(userData2);
|
|
561
|
+
db.commit();
|
|
562
|
+
} catch (error) {
|
|
563
|
+
db.rollback();
|
|
564
|
+
throw error;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Automatic transaction wrapper
|
|
568
|
+
const result = db.transaction(() => {
|
|
569
|
+
const user = db.table<User>("users").insert(userData);
|
|
570
|
+
db.table("audit_log").insert({ action: "user_created", user_id: user.insertId });
|
|
571
|
+
return user;
|
|
572
|
+
});
|
|
391
573
|
```
|
|
392
574
|
|
|
393
|
-
|
|
575
|
+
### Memory and Performance Tips
|
|
576
|
+
|
|
577
|
+
- Use `PRAGMA cache_size` to tune memory usage
|
|
578
|
+
- Enable WAL mode for better concurrency
|
|
579
|
+
- Use `PRAGMA optimize` periodically for query optimization
|
|
580
|
+
- Consider `WITHOUT ROWID` tables for specific use cases
|
|
581
|
+
- Use `PRAGMA vacuum` to reclaim space after large deletions
|
|
394
582
|
|
|
395
583
|
---
|
|
396
584
|
|
|
397
|
-
##
|
|
585
|
+
## Error Handling and Safety
|
|
586
|
+
|
|
587
|
+
### Required WHERE Conditions
|
|
398
588
|
|
|
399
|
-
|
|
589
|
+
UPDATE and DELETE operations require WHERE conditions to prevent accidental data modification:
|
|
400
590
|
|
|
401
591
|
```ts
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
.
|
|
405
|
-
|
|
592
|
+
// This will throw an error
|
|
593
|
+
try {
|
|
594
|
+
db.table<User>("users").update({ active: false });
|
|
595
|
+
} catch (error) {
|
|
596
|
+
console.error("UPDATE requires WHERE conditions");
|
|
597
|
+
}
|
|
406
598
|
|
|
407
|
-
|
|
599
|
+
// This is safe
|
|
600
|
+
db.table<User>("users")
|
|
601
|
+
.where({ type: "temporary" })
|
|
602
|
+
.update({ active: false });
|
|
408
603
|
```
|
|
409
604
|
|
|
410
|
-
|
|
605
|
+
### Type Safety
|
|
606
|
+
|
|
607
|
+
The library provides comprehensive TypeScript support:
|
|
411
608
|
|
|
412
609
|
```ts
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
```
|
|
610
|
+
interface StrictUser {
|
|
611
|
+
id: number;
|
|
612
|
+
name: string;
|
|
613
|
+
email: string;
|
|
614
|
+
}
|
|
419
615
|
|
|
420
|
-
|
|
616
|
+
const users = db.table<StrictUser>("users");
|
|
421
617
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
.whereRgx({ email: /^test.*@example\.com$/i })
|
|
426
|
-
.delete();
|
|
427
|
-
```
|
|
618
|
+
// TypeScript will enforce correct column names
|
|
619
|
+
users.select(["id", "name"]); // ✓ Valid
|
|
620
|
+
users.select(["invalid_column"]); // ✗ TypeScript error
|
|
428
621
|
|
|
429
|
-
|
|
622
|
+
// TypeScript will enforce correct data types
|
|
623
|
+
users.insert({ name: "John", email: "john@example.com" }); // ✓ Valid
|
|
624
|
+
users.insert({ name: 123 }); // ✗ TypeScript error
|
|
625
|
+
```
|
|
430
626
|
|
|
431
|
-
|
|
627
|
+
### Parameter Binding
|
|
432
628
|
|
|
433
|
-
|
|
629
|
+
All queries use proper parameter binding to prevent SQL injection:
|
|
434
630
|
|
|
435
631
|
```ts
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
.
|
|
439
|
-
.
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
.
|
|
444
|
-
.limit(100)
|
|
632
|
+
// Safe - uses parameter binding
|
|
633
|
+
db.table<User>("users")
|
|
634
|
+
.whereRaw("name = ? AND email = ?", [userName, userEmail])
|
|
635
|
+
.all();
|
|
636
|
+
|
|
637
|
+
// Parameters are automatically escaped
|
|
638
|
+
db.table<User>("users")
|
|
639
|
+
.where({ name: "O'Reilly" }) // Automatically handled
|
|
445
640
|
.all();
|
|
446
641
|
```
|
|
447
642
|
|
|
448
643
|
---
|
|
449
644
|
|
|
450
|
-
##
|
|
645
|
+
## API Reference
|
|
451
646
|
|
|
452
|
-
|
|
647
|
+
### DB Class
|
|
453
648
|
|
|
454
649
|
```ts
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
}
|
|
650
|
+
constructor(path: string, options?: {
|
|
651
|
+
pragmas?: Array<[string, any]>;
|
|
652
|
+
loadExtensions?: string[];
|
|
653
|
+
})
|
|
654
|
+
|
|
655
|
+
// Table operations
|
|
656
|
+
table<T>(tableName: string, jsonConfig?: JsonColumnConfig<T>): QueryBuilder<T>
|
|
657
|
+
createTable(tableName: string, columns: CreateTableColumns, options?: TableOptions): void
|
|
658
|
+
dropTable(tableName: string, options?: { ifExists?: boolean }): void
|
|
659
|
+
|
|
660
|
+
// Index operations
|
|
661
|
+
createIndex(name: string, table: string, columns: string | string[], options?: IndexOptions): void
|
|
662
|
+
dropIndex(name: string, options?: { ifExists?: boolean }): void
|
|
663
|
+
|
|
664
|
+
// Database operations
|
|
665
|
+
pragma(name: string, value?: any): any
|
|
666
|
+
loadExtension(path: string): void
|
|
667
|
+
exec(sql: string): void
|
|
668
|
+
prepare(sql: string): Statement
|
|
669
|
+
transaction<T>(fn: () => T): T
|
|
670
|
+
begin(): void
|
|
671
|
+
commit(): void
|
|
672
|
+
rollback(): void
|
|
673
|
+
close(): void
|
|
674
|
+
|
|
675
|
+
// Introspection
|
|
676
|
+
getTableInfo(tableName: string): TableInfo[]
|
|
677
|
+
getForeignKeys(tableName: string): ForeignKeyInfo[]
|
|
678
|
+
getIndexes(tableName: string): IndexInfo[]
|
|
679
|
+
getSchema(): SchemaInfo[]
|
|
459
680
|
```
|
|
460
681
|
|
|
461
|
-
|
|
682
|
+
### QueryBuilder Class
|
|
462
683
|
|
|
463
684
|
```ts
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
685
|
+
// WHERE conditions
|
|
686
|
+
where(conditions: WhereCondition<T>): this
|
|
687
|
+
whereRgx(conditions: RegexCondition<T>): this
|
|
688
|
+
whereRaw(expr: string, params?: any[]): this
|
|
689
|
+
whereExpr(expr: string, params?: any[]): this
|
|
690
|
+
whereIn(column: keyof T, values: any[]): this
|
|
691
|
+
whereNotIn(column: keyof T, values: any[]): this
|
|
692
|
+
whereOp(column: keyof T, op: string, value: any): this
|
|
693
|
+
whereBetween(column: keyof T, min: any, max: any): this
|
|
694
|
+
whereNotBetween(column: keyof T, min: any, max: any): this
|
|
695
|
+
whereNull(column: keyof T): this
|
|
696
|
+
whereNotNull(column: keyof T): this
|
|
697
|
+
|
|
698
|
+
// SELECT operations
|
|
699
|
+
select(columns: ColumnNames<T>): this
|
|
700
|
+
orderBy(column: keyof T): this
|
|
701
|
+
asc(): this
|
|
702
|
+
desc(): this
|
|
703
|
+
limit(amount: number): this
|
|
704
|
+
offset(start: number): this
|
|
705
|
+
|
|
706
|
+
// Result execution
|
|
707
|
+
all(): T[]
|
|
708
|
+
get(): T | null
|
|
709
|
+
first(): T | null
|
|
710
|
+
count(): number
|
|
711
|
+
exists(): boolean
|
|
712
|
+
value<K extends keyof T>(column: K): T[K] | null
|
|
713
|
+
pluck<K extends keyof T>(column: K): T[K][]
|
|
714
|
+
|
|
715
|
+
// INSERT operations
|
|
716
|
+
insert(data: Partial<T> | Partial<T>[], options?: InsertOptions): InsertResult
|
|
717
|
+
insertOrIgnore(data: Partial<T> | Partial<T>[]): InsertResult
|
|
718
|
+
insertOrReplace(data: Partial<T> | Partial<T>[]): InsertResult
|
|
719
|
+
insertAndGet(data: Partial<T>, options?: InsertOptions): T | null
|
|
720
|
+
insertBatch(rows: Partial<T>[], options?: InsertOptions): InsertResult
|
|
721
|
+
|
|
722
|
+
// UPDATE operations
|
|
723
|
+
update(data: Partial<T>): UpdateResult
|
|
724
|
+
upsert(data: Partial<T>): UpdateResult
|
|
725
|
+
increment(column: keyof T, amount?: number): UpdateResult
|
|
726
|
+
decrement(column: keyof T, amount?: number): UpdateResult
|
|
727
|
+
updateAndGet(data: Partial<T>): T[]
|
|
728
|
+
updateBatch(updates: Array<{ where: Partial<T>; data: Partial<T> }>): UpdateResult
|
|
729
|
+
|
|
730
|
+
// DELETE operations
|
|
731
|
+
delete(): DeleteResult
|
|
732
|
+
deleteAndGet(): T[]
|
|
733
|
+
softDelete(deletedColumn?: keyof T, deletedValue?: any): DeleteResult
|
|
734
|
+
restore(deletedColumn?: keyof T): DeleteResult
|
|
735
|
+
deleteBatch(conditions: Array<Partial<T>>): DeleteResult
|
|
736
|
+
truncate(): DeleteResult
|
|
737
|
+
deleteOlderThan(timestampColumn: keyof T, olderThan: number): DeleteResult
|
|
738
|
+
deleteDuplicates(columns: Array<keyof T>): DeleteResult
|
|
471
739
|
```
|
|
472
740
|
|
|
473
|
-
|
|
741
|
+
### Result Types
|
|
474
742
|
|
|
475
743
|
```ts
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
} catch (error) {
|
|
480
|
-
console.error("UPDATE operation requires WHERE conditions");
|
|
744
|
+
interface InsertResult {
|
|
745
|
+
insertId: number;
|
|
746
|
+
changes: number;
|
|
481
747
|
}
|
|
482
748
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
749
|
+
interface UpdateResult {
|
|
750
|
+
changes: number;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
interface DeleteResult {
|
|
754
|
+
changes: number;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
interface InsertOptions {
|
|
758
|
+
orIgnore?: boolean;
|
|
759
|
+
orReplace?: boolean;
|
|
760
|
+
orAbort?: boolean;
|
|
761
|
+
orFail?: boolean;
|
|
762
|
+
orRollback?: boolean;
|
|
488
763
|
}
|
|
489
764
|
```
|
|
490
765
|
|
|
491
766
|
---
|
|
492
767
|
|
|
493
|
-
##
|
|
768
|
+
## Examples
|
|
494
769
|
|
|
495
|
-
|
|
770
|
+
### E-commerce Product Catalog
|
|
496
771
|
|
|
497
772
|
```ts
|
|
498
|
-
|
|
499
|
-
const rows = db
|
|
500
|
-
.table<User>("users")
|
|
501
|
-
.whereExpr("email REGEXP ?", ["@example\\.com$"])
|
|
502
|
-
.all();
|
|
503
|
-
```
|
|
773
|
+
import { DB, column, sql } from "@dockstat/sqlite-wrapper";
|
|
504
774
|
|
|
505
|
-
|
|
775
|
+
interface Product {
|
|
776
|
+
id?: number;
|
|
777
|
+
name: string;
|
|
778
|
+
sku: string;
|
|
779
|
+
price: number;
|
|
780
|
+
category: string;
|
|
781
|
+
specifications?: object;
|
|
782
|
+
active?: boolean;
|
|
783
|
+
created_at?: number;
|
|
784
|
+
updated_at?: number;
|
|
785
|
+
}
|
|
506
786
|
|
|
507
|
-
|
|
787
|
+
const db = new DB("ecommerce.db");
|
|
788
|
+
|
|
789
|
+
// Create products table
|
|
790
|
+
db.createTable("products", {
|
|
791
|
+
id: column.id(),
|
|
792
|
+
name: column.text({ notNull: true }),
|
|
793
|
+
sku: column.varchar(50, { unique: true, notNull: true }),
|
|
794
|
+
price: column.numeric({ precision: 10, scale: 2, check: "price > 0" }),
|
|
795
|
+
category: column.enum(["electronics", "clothing", "books"], {
|
|
796
|
+
default: "electronics"
|
|
797
|
+
}),
|
|
798
|
+
specifications: column.json({ validateJson: true }),
|
|
799
|
+
active: column.boolean({ default: true }),
|
|
800
|
+
created_at: column.createdAt(),
|
|
801
|
+
updated_at: column.updatedAt()
|
|
802
|
+
});
|
|
508
803
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
804
|
+
// Query products
|
|
805
|
+
const activeProducts = db.table<Product>("products")
|
|
806
|
+
.where({ active: true })
|
|
807
|
+
.whereOp("price", "<=", 100)
|
|
808
|
+
.orderBy("created_at")
|
|
809
|
+
.desc()
|
|
810
|
+
.all();
|
|
513
811
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
812
|
+
// Update pricing
|
|
813
|
+
db.table<Product>("products")
|
|
814
|
+
.where({ category: "electronics" })
|
|
815
|
+
.whereOp("price", ">", 1000)
|
|
816
|
+
.increment("price", -50); // $50 discount
|
|
817
|
+
```
|
|
518
818
|
|
|
519
|
-
###
|
|
520
|
-
* When using `.whereRgx()`, the operation fetches candidate rows first, then applies regex filtering client-side.
|
|
521
|
-
* For better performance with regex conditions, consider using SQL `LIKE` patterns where possible.
|
|
819
|
+
### User Management System
|
|
522
820
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
821
|
+
```ts
|
|
822
|
+
interface User {
|
|
823
|
+
id?: number;
|
|
824
|
+
username: string;
|
|
825
|
+
email: string;
|
|
826
|
+
role: string;
|
|
827
|
+
profile?: object;
|
|
828
|
+
active?: boolean;
|
|
829
|
+
last_login?: number;
|
|
830
|
+
created_at?: number;
|
|
831
|
+
}
|
|
528
832
|
|
|
529
|
-
|
|
833
|
+
// Create users with comprehensive constraints
|
|
834
|
+
db.createTable("users", {
|
|
835
|
+
id: column.id(),
|
|
836
|
+
username: column.varchar(50, {
|
|
837
|
+
unique: true,
|
|
838
|
+
notNull: true,
|
|
839
|
+
check: "length(username) >= 3"
|
|
840
|
+
}),
|
|
841
|
+
email: column.text({
|
|
842
|
+
unique: true,
|
|
843
|
+
notNull: true,
|
|
844
|
+
check: "email LIKE '%@%.%'"
|
|
845
|
+
}),
|
|
846
|
+
role: column.enum(["admin", "moderator", "user"], { default: "user" }),
|
|
847
|
+
profile: column.json(),
|
|
848
|
+
active: column.boolean({ default: true }),
|
|
849
|
+
last_login: column.timestamp(),
|
|
850
|
+
created_at: column.createdAt()
|
|
851
|
+
}, {
|
|
852
|
+
constraints: {
|
|
853
|
+
unique: [["username", "email"]]
|
|
854
|
+
}
|
|
855
|
+
});
|
|
530
856
|
|
|
531
|
-
|
|
857
|
+
// Find inactive users
|
|
858
|
+
const inactiveUsers = db.table<User>("users")
|
|
859
|
+
.where({ active: true })
|
|
860
|
+
.whereOp("last_login", "<", Date.now() - 2592000000) // 30 days
|
|
861
|
+
.all();
|
|
862
|
+
|
|
863
|
+
// Bulk role assignment
|
|
864
|
+
db.table<User>("users")
|
|
865
|
+
.whereIn("id", [1, 2, 3, 4, 5])
|
|
866
|
+
.update({ role: "moderator" });
|
|
867
|
+
```
|
|
532
868
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
* `constructor(path: string, opts?: { pragmas?: [string, any][], loadExtensions?: string[] })`
|
|
536
|
-
* `table<T>(tableName: string): QueryBuilder<T>`
|
|
537
|
-
* `createTable(tableName: string, columns: string | Record<string,string>, options?: { ifNotExists?: boolean; withoutRowId?: boolean })`
|
|
538
|
-
* `pragma(name: string, value?: any): any`
|
|
539
|
-
* `loadExtension(path: string): void`
|
|
540
|
-
* `close(): void`
|
|
541
|
-
|
|
542
|
-
### `QueryBuilder<T>` (named export)
|
|
543
|
-
|
|
544
|
-
**WHERE methods (shared across operations):**
|
|
545
|
-
* `where(cond: Partial<Record<keyof T, string | number | boolean | null>>)`
|
|
546
|
-
* `whereRgx(cond: Partial<Record<keyof T, string | RegExp>>)`
|
|
547
|
-
* `whereExpr(expr: string, params?: any[])`
|
|
548
|
-
* `whereRaw(expr: string, params?: any[])`
|
|
549
|
-
* `whereIn(column: keyof T, values: any[])`
|
|
550
|
-
* `whereOp(column: keyof T, op: string, value: any)`
|
|
551
|
-
|
|
552
|
-
**SELECT methods:**
|
|
553
|
-
* `select(columns: Array<keyof T> | ["*"])`
|
|
554
|
-
* `orderBy(column: keyof T)`
|
|
555
|
-
* `asc()`, `desc()`
|
|
556
|
-
* `limit(n)`, `offset(n)`
|
|
557
|
-
* `all(): T[]`, `get(): T | null`, `first(): T | null`, `count(): number`
|
|
558
|
-
|
|
559
|
-
**INSERT methods:**
|
|
560
|
-
* `insert(data: Partial<T> | Partial<T>[], options?: InsertOptions): InsertResult`
|
|
561
|
-
* `insertOrIgnore(data: Partial<T> | Partial<T>[]): InsertResult`
|
|
562
|
-
* `insertOrReplace(data: Partial<T> | Partial<T>[]): InsertResult`
|
|
563
|
-
|
|
564
|
-
**UPDATE methods:**
|
|
565
|
-
* `update(data: Partial<T>): UpdateResult` *(requires WHERE conditions)*
|
|
566
|
-
|
|
567
|
-
**DELETE methods:**
|
|
568
|
-
* `delete(): DeleteResult` *(requires WHERE conditions)*
|
|
569
|
-
|
|
570
|
-
### Types (named exports)
|
|
571
|
-
|
|
572
|
-
* `InsertResult` — `{ insertId: number, changes: number }`
|
|
573
|
-
* `UpdateResult` — `{ changes: number }`
|
|
574
|
-
* `DeleteResult` — `{ changes: number }`
|
|
575
|
-
* `InsertOptions` — conflict resolution options
|
|
576
|
-
* `ColumnNames<T>` — column selection type
|
|
577
|
-
* `WhereCondition<T>` — WHERE condition type
|
|
578
|
-
* `RegexCondition<T>` — regex condition type
|
|
869
|
+
This comprehensive wrapper provides everything you need for robust SQLite operations in TypeScript applications with type safety, performance optimizations, and modern development practices.
|