@livestore/common 0.3.0-dev.29 → 0.3.0-dev.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/devtools/devtools-messages-client-session.d.ts +21 -21
- package/dist/devtools/devtools-messages-common.d.ts +6 -6
- package/dist/devtools/devtools-messages-leader.d.ts +24 -24
- package/dist/query-builder/api.d.ts +1 -1
- package/dist/query-builder/api.d.ts.map +1 -1
- package/dist/query-builder/impl.d.ts.map +1 -1
- package/dist/query-builder/impl.js +11 -5
- package/dist/query-builder/impl.js.map +1 -1
- package/dist/query-builder/impl.test.d.ts.map +1 -1
- package/dist/query-builder/impl.test.js +155 -130
- package/dist/query-builder/impl.test.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/src/query-builder/api.ts +1 -1
- package/src/query-builder/impl.test.ts +165 -143
- package/src/query-builder/impl.ts +11 -6
- package/src/version.ts +1 -1
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@livestore/common",
|
3
|
-
"version": "0.3.0-dev.
|
3
|
+
"version": "0.3.0-dev.30",
|
4
4
|
"type": "module",
|
5
5
|
"sideEffects": false,
|
6
6
|
"exports": {
|
@@ -54,7 +54,7 @@
|
|
54
54
|
"graphology": "0.26.0-alpha1",
|
55
55
|
"graphology-dag": "0.4.1",
|
56
56
|
"graphology-types": "0.24.8",
|
57
|
-
"@livestore/utils": "0.3.0-dev.
|
57
|
+
"@livestore/utils": "0.3.0-dev.30"
|
58
58
|
},
|
59
59
|
"devDependencies": {
|
60
60
|
"vitest": "^3.1.1"
|
package/src/query-builder/api.ts
CHANGED
@@ -19,7 +19,7 @@ export namespace QueryBuilderAst {
|
|
19
19
|
export interface SelectQuery {
|
20
20
|
readonly _tag: 'SelectQuery'
|
21
21
|
readonly columns: string[]
|
22
|
-
readonly pickFirst: false | { fallback: () => any }
|
22
|
+
readonly pickFirst: false | { fallback: () => any } | 'no-fallback'
|
23
23
|
readonly select: {
|
24
24
|
columns: ReadonlyArray<string>
|
25
25
|
}
|
@@ -2,6 +2,7 @@ import { Schema } from '@livestore/utils/effect'
|
|
2
2
|
import { describe, expect, it } from 'vitest'
|
3
3
|
|
4
4
|
import { State } from '../schema/mod.js'
|
5
|
+
import type { QueryBuilder } from './api.js'
|
5
6
|
import { getResultSchema } from './impl.js'
|
6
7
|
|
7
8
|
const todos = State.SQLite.table({
|
@@ -74,236 +75,241 @@ export const issue = State.SQLite.table({
|
|
74
75
|
|
75
76
|
const db = { todos, todosWithIntId, comments, issue, UiState, UiStateWithDefaultId }
|
76
77
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
})
|
78
|
+
const dump = (qb: QueryBuilder<any, any, any>) => ({
|
79
|
+
bindValues: qb.asSql().bindValues,
|
80
|
+
query: qb.asSql().query,
|
81
|
+
schema: getResultSchema(qb).toString(),
|
82
|
+
})
|
83
83
|
|
84
|
+
describe('query builder', () => {
|
84
85
|
describe('basic queries', () => {
|
85
86
|
it('should handle simple SELECT queries', () => {
|
86
|
-
expect(db.todos
|
87
|
+
expect(dump(db.todos)).toMatchInlineSnapshot(`
|
87
88
|
{
|
88
89
|
"bindValues": [],
|
89
90
|
"query": "SELECT * FROM 'todos'",
|
91
|
+
"schema": "ReadonlyArray<todos>",
|
90
92
|
}
|
91
93
|
`)
|
92
94
|
|
93
|
-
expect(db.todos.select('id')
|
95
|
+
expect(dump(db.todos.select('id'))).toMatchInlineSnapshot(`
|
94
96
|
{
|
95
97
|
"bindValues": [],
|
96
98
|
"query": "SELECT id FROM 'todos'",
|
99
|
+
"schema": "ReadonlyArray<({ readonly id: string } <-> string)>",
|
97
100
|
}
|
98
101
|
`)
|
99
102
|
|
100
|
-
expect(db.todos.select('id', 'text')
|
103
|
+
expect(dump(db.todos.select('id', 'text'))).toMatchInlineSnapshot(`
|
101
104
|
{
|
102
105
|
"bindValues": [],
|
103
106
|
"query": "SELECT id, text FROM 'todos'",
|
107
|
+
"schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
|
104
108
|
}
|
105
109
|
`)
|
106
110
|
})
|
107
111
|
|
108
112
|
it('should handle .first()', () => {
|
109
|
-
expect(db.todos.select('id', 'text').first()
|
113
|
+
expect(dump(db.todos.select('id', 'text').first())).toMatchInlineSnapshot(`
|
110
114
|
{
|
111
115
|
"bindValues": [
|
112
116
|
1,
|
113
117
|
],
|
114
118
|
"query": "SELECT id, text FROM 'todos' LIMIT ?",
|
119
|
+
"schema": "(ReadonlyArray<{ readonly id: string; readonly text: string }> <-> { readonly id: string; readonly text: string })",
|
115
120
|
}
|
116
121
|
`)
|
117
122
|
|
118
|
-
expect(
|
119
|
-
db.todos
|
120
|
-
.select('id', 'text')
|
121
|
-
.first({ fallback: () => undefined })
|
122
|
-
.asSql(),
|
123
|
-
).toMatchInlineSnapshot(`
|
123
|
+
expect(dump(db.todos.select('id', 'text').first({ fallback: () => undefined }))).toMatchInlineSnapshot(`
|
124
124
|
{
|
125
125
|
"bindValues": [
|
126
126
|
1,
|
127
127
|
],
|
128
128
|
"query": "SELECT id, text FROM 'todos' LIMIT ?",
|
129
|
+
"schema": "(ReadonlyArray<{ readonly id: string; readonly text: string }> | readonly [undefined] <-> { readonly id: string; readonly text: string } | undefined)",
|
129
130
|
}
|
130
131
|
`)
|
131
132
|
})
|
132
133
|
|
133
134
|
it('should handle WHERE clauses', () => {
|
134
|
-
expect(db.todos.select('id', 'text').where('completed', true)
|
135
|
+
expect(dump(db.todos.select('id', 'text').where('completed', true))).toMatchInlineSnapshot(`
|
135
136
|
{
|
136
137
|
"bindValues": [
|
137
138
|
1,
|
138
139
|
],
|
139
140
|
"query": "SELECT id, text FROM 'todos' WHERE completed = ?",
|
141
|
+
"schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
|
140
142
|
}
|
141
143
|
`)
|
142
|
-
expect(db.todos.select('id', 'text').where('completed', '!=', true)
|
144
|
+
expect(dump(db.todos.select('id', 'text').where('completed', '!=', true))).toMatchInlineSnapshot(`
|
143
145
|
{
|
144
146
|
"bindValues": [
|
145
147
|
1,
|
146
148
|
],
|
147
149
|
"query": "SELECT id, text FROM 'todos' WHERE completed != ?",
|
150
|
+
"schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
|
148
151
|
}
|
149
152
|
`)
|
150
|
-
expect(db.todos.select('id', 'text').where({ completed: true })
|
153
|
+
expect(dump(db.todos.select('id', 'text').where({ completed: true }))).toMatchInlineSnapshot(`
|
151
154
|
{
|
152
155
|
"bindValues": [
|
153
156
|
1,
|
154
157
|
],
|
155
158
|
"query": "SELECT id, text FROM 'todos' WHERE completed = ?",
|
159
|
+
"schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
|
156
160
|
}
|
157
161
|
`)
|
158
|
-
expect(db.todos.select('id', 'text').where({ completed: undefined })
|
162
|
+
expect(dump(db.todos.select('id', 'text').where({ completed: undefined }))).toMatchInlineSnapshot(`
|
159
163
|
{
|
160
164
|
"bindValues": [],
|
161
165
|
"query": "SELECT id, text FROM 'todos'",
|
166
|
+
"schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
|
162
167
|
}
|
163
168
|
`)
|
164
|
-
expect(
|
165
|
-
db.todos
|
166
|
-
.select('id', 'text')
|
167
|
-
.where({ deletedAt: { op: '<=', value: new Date('2024-01-01') } })
|
168
|
-
.asSql(),
|
169
|
-
).toMatchInlineSnapshot(`
|
170
|
-
{
|
171
|
-
"bindValues": [
|
172
|
-
"2024-01-01T00:00:00.000Z",
|
173
|
-
],
|
174
|
-
"query": "SELECT id, text FROM 'todos' WHERE deletedAt <= ?",
|
175
|
-
}
|
176
|
-
`)
|
177
|
-
expect(
|
178
|
-
db.todos
|
179
|
-
.select('id', 'text')
|
180
|
-
.where({ status: { op: 'IN', value: ['active'] } })
|
181
|
-
.asSql(),
|
182
|
-
).toMatchInlineSnapshot(`
|
183
|
-
{
|
184
|
-
"bindValues": [
|
185
|
-
"active",
|
186
|
-
],
|
187
|
-
"query": "SELECT id, text FROM 'todos' WHERE status IN (?)",
|
188
|
-
}
|
189
|
-
`)
|
190
|
-
expect(
|
191
|
-
db.todos
|
192
|
-
.select('id', 'text')
|
193
|
-
.where({ status: { op: 'NOT IN', value: ['active', 'completed'] } })
|
194
|
-
.asSql(),
|
195
|
-
).toMatchInlineSnapshot(`
|
196
|
-
{
|
197
|
-
"bindValues": [
|
198
|
-
"active",
|
199
|
-
"completed",
|
200
|
-
],
|
201
|
-
"query": "SELECT id, text FROM 'todos' WHERE status NOT IN (?, ?)",
|
202
|
-
}
|
203
|
-
`)
|
204
|
-
})
|
205
|
-
|
206
|
-
it('should handle OFFSET and LIMIT clauses', () => {
|
207
|
-
expect(db.todos.select('id', 'text').where('completed', true).offset(10).limit(10).asSql())
|
169
|
+
expect(dump(db.todos.select('id', 'text').where({ deletedAt: { op: '<=', value: new Date('2024-01-01') } })))
|
208
170
|
.toMatchInlineSnapshot(`
|
209
171
|
{
|
210
172
|
"bindValues": [
|
211
|
-
|
212
|
-
10,
|
213
|
-
10,
|
173
|
+
"2024-01-01T00:00:00.000Z",
|
214
174
|
],
|
215
|
-
"query": "SELECT id, text FROM 'todos' WHERE
|
175
|
+
"query": "SELECT id, text FROM 'todos' WHERE deletedAt <= ?",
|
176
|
+
"schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
|
216
177
|
}
|
217
178
|
`)
|
218
|
-
|
219
|
-
|
220
|
-
it('should handle OFFSET and LIMIT clauses correctly', () => {
|
221
|
-
// Test with both offset and limit
|
222
|
-
expect(db.todos.select('id', 'text').where('completed', true).offset(5).limit(10).asSql()).toMatchInlineSnapshot(`
|
179
|
+
expect(dump(db.todos.select('id', 'text').where({ status: { op: 'IN', value: ['active'] } })))
|
180
|
+
.toMatchInlineSnapshot(`
|
223
181
|
{
|
224
182
|
"bindValues": [
|
225
|
-
|
226
|
-
5,
|
227
|
-
10,
|
183
|
+
"active",
|
228
184
|
],
|
229
|
-
"query": "SELECT id, text FROM 'todos' WHERE
|
185
|
+
"query": "SELECT id, text FROM 'todos' WHERE status IN (?)",
|
186
|
+
"schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
|
230
187
|
}
|
231
188
|
`)
|
232
|
-
|
233
|
-
|
234
|
-
expect(db.todos.select('id', 'text').where('completed', true).offset(5).asSql()).toMatchInlineSnapshot(`
|
189
|
+
expect(dump(db.todos.select('id', 'text').where({ status: { op: 'NOT IN', value: ['active', 'completed'] } })))
|
190
|
+
.toMatchInlineSnapshot(`
|
235
191
|
{
|
236
192
|
"bindValues": [
|
237
|
-
|
238
|
-
|
193
|
+
"active",
|
194
|
+
"completed",
|
239
195
|
],
|
240
|
-
"query": "SELECT id, text FROM 'todos' WHERE
|
196
|
+
"query": "SELECT id, text FROM 'todos' WHERE status NOT IN (?, ?)",
|
197
|
+
"schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
|
241
198
|
}
|
242
199
|
`)
|
200
|
+
})
|
201
|
+
|
202
|
+
it('should handle OFFSET and LIMIT clauses', () => {
|
203
|
+
expect(dump(db.todos.select('id', 'text').where('completed', true).offset(10).limit(10))).toMatchInlineSnapshot(`
|
204
|
+
{
|
205
|
+
"bindValues": [
|
206
|
+
1,
|
207
|
+
10,
|
208
|
+
10,
|
209
|
+
],
|
210
|
+
"query": "SELECT id, text FROM 'todos' WHERE completed = ? OFFSET ? LIMIT ?",
|
211
|
+
"schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
|
212
|
+
}
|
213
|
+
`)
|
214
|
+
})
|
215
|
+
|
216
|
+
it('should handle OFFSET and LIMIT clauses correctly', () => {
|
217
|
+
// Test with both offset and limit
|
218
|
+
expect(dump(db.todos.select('id', 'text').where('completed', true).offset(5).limit(10))).toMatchInlineSnapshot(`
|
219
|
+
{
|
220
|
+
"bindValues": [
|
221
|
+
1,
|
222
|
+
5,
|
223
|
+
10,
|
224
|
+
],
|
225
|
+
"query": "SELECT id, text FROM 'todos' WHERE completed = ? OFFSET ? LIMIT ?",
|
226
|
+
"schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
|
227
|
+
}
|
228
|
+
`)
|
229
|
+
|
230
|
+
// Test with only offset
|
231
|
+
expect(dump(db.todos.select('id', 'text').where('completed', true).offset(5))).toMatchInlineSnapshot(`
|
232
|
+
{
|
233
|
+
"bindValues": [
|
234
|
+
1,
|
235
|
+
5,
|
236
|
+
],
|
237
|
+
"query": "SELECT id, text FROM 'todos' WHERE completed = ? OFFSET ?",
|
238
|
+
"schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
|
239
|
+
}
|
240
|
+
`)
|
243
241
|
|
244
242
|
// Test with only limit
|
245
|
-
expect(db.todos.select('id', 'text').where('completed', true).limit(10)
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
}
|
253
|
-
|
243
|
+
expect(dump(db.todos.select('id', 'text').where('completed', true).limit(10))).toMatchInlineSnapshot(`
|
244
|
+
{
|
245
|
+
"bindValues": [
|
246
|
+
1,
|
247
|
+
10,
|
248
|
+
],
|
249
|
+
"query": "SELECT id, text FROM 'todos' WHERE completed = ? LIMIT ?",
|
250
|
+
"schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
|
251
|
+
}
|
252
|
+
`)
|
254
253
|
})
|
255
254
|
|
256
255
|
it('should handle COUNT queries', () => {
|
257
|
-
expect(db.todos.count()
|
256
|
+
expect(dump(db.todos.count())).toMatchInlineSnapshot(`
|
258
257
|
{
|
259
258
|
"bindValues": [],
|
260
259
|
"query": "SELECT COUNT(*) as count FROM 'todos'",
|
260
|
+
"schema": "(ReadonlyArray<({ readonly count: number } <-> number)> <-> number)",
|
261
261
|
}
|
262
262
|
`)
|
263
|
-
expect(db.todos.count().where('completed', true)
|
263
|
+
expect(dump(db.todos.count().where('completed', true))).toMatchInlineSnapshot(`
|
264
264
|
{
|
265
265
|
"bindValues": [
|
266
266
|
1,
|
267
267
|
],
|
268
268
|
"query": "SELECT COUNT(*) as count FROM 'todos' WHERE completed = ?",
|
269
|
+
"schema": "(ReadonlyArray<({ readonly count: number } <-> number)> <-> number)",
|
269
270
|
}
|
270
271
|
`)
|
271
272
|
})
|
272
273
|
|
273
274
|
it('should handle NULL comparisons', () => {
|
274
|
-
expect(db.todos.select('id', 'text').where('deletedAt', '=', null)
|
275
|
+
expect(dump(db.todos.select('id', 'text').where('deletedAt', '=', null))).toMatchInlineSnapshot(`
|
275
276
|
{
|
276
277
|
"bindValues": [],
|
277
278
|
"query": "SELECT id, text FROM 'todos' WHERE deletedAt IS NULL",
|
279
|
+
"schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
|
278
280
|
}
|
279
281
|
`)
|
280
|
-
expect(db.todos.select('id', 'text').where('deletedAt', '!=', null)
|
282
|
+
expect(dump(db.todos.select('id', 'text').where('deletedAt', '!=', null))).toMatchInlineSnapshot(`
|
281
283
|
{
|
282
284
|
"bindValues": [],
|
283
285
|
"query": "SELECT id, text FROM 'todos' WHERE deletedAt IS NOT NULL",
|
286
|
+
"schema": "ReadonlyArray<{ readonly id: string; readonly text: string }>",
|
284
287
|
}
|
285
288
|
`)
|
286
289
|
})
|
287
290
|
|
288
291
|
it('should handle orderBy', () => {
|
289
|
-
expect(db.todos.orderBy('completed', 'desc')
|
292
|
+
expect(dump(db.todos.orderBy('completed', 'desc'))).toMatchInlineSnapshot(`
|
290
293
|
{
|
291
294
|
"bindValues": [],
|
292
295
|
"query": "SELECT * FROM 'todos' ORDER BY completed desc",
|
296
|
+
"schema": "ReadonlyArray<todos>",
|
293
297
|
}
|
294
298
|
`)
|
295
299
|
|
296
|
-
expect(db.todos.orderBy([{ col: 'completed', direction: 'desc' }])
|
300
|
+
expect(dump(db.todos.orderBy([{ col: 'completed', direction: 'desc' }]))).toMatchInlineSnapshot(`
|
297
301
|
{
|
298
302
|
"bindValues": [],
|
299
303
|
"query": "SELECT * FROM 'todos' ORDER BY completed desc",
|
304
|
+
"schema": "ReadonlyArray<todos>",
|
300
305
|
}
|
301
306
|
`)
|
302
307
|
|
303
|
-
expect(db.todos.orderBy([])
|
308
|
+
expect(dump(db.todos.orderBy([]))).toMatchInlineSnapshot(`
|
304
309
|
{
|
305
310
|
"bindValues": [],
|
306
311
|
"query": "SELECT * FROM 'todos'",
|
312
|
+
"schema": "ReadonlyArray<todos>",
|
307
313
|
}
|
308
314
|
`)
|
309
315
|
})
|
@@ -311,31 +317,34 @@ describe('query builder', () => {
|
|
311
317
|
|
312
318
|
// describe('getOrCreate queries', () => {
|
313
319
|
// it('should handle getOrCreate queries', () => {
|
314
|
-
// expect(db.UiState.getOrCreate('sessionid-1')
|
320
|
+
// expect(dump(db.UiState.getOrCreate('sessionid-1'))).toMatchInlineSnapshot(`
|
315
321
|
// {
|
316
322
|
// "bindValues": [
|
317
323
|
// "sessionid-1",
|
318
324
|
// ],
|
319
325
|
// "query": "SELECT * FROM 'UiState' WHERE id = ?",
|
326
|
+
// "schema": "...", // TODO determine schema
|
320
327
|
// }
|
321
328
|
// `)
|
322
329
|
// })
|
323
330
|
|
324
331
|
// it('should handle getOrCreate queries with default id', () => {
|
325
|
-
// expect(db.UiStateWithDefaultId.getOrCreate()
|
332
|
+
// expect(dump(db.UiStateWithDefaultId.getOrCreate())).toMatchInlineSnapshot(`
|
326
333
|
// {
|
327
334
|
// "bindValues": [],
|
328
335
|
// "query": "SELECT * FROM 'UiState' WHERE id = ?",
|
336
|
+
// "schema": "...", // TODO determine schema
|
329
337
|
// }
|
330
338
|
// `)
|
331
339
|
// })
|
332
340
|
// // it('should handle row queries with numbers', () => {
|
333
|
-
// // expect(db.todosWithIntId.getOrCreate(123, { insertValues: { status: 'active' } })
|
341
|
+
// // expect(dump(db.todosWithIntId.getOrCreate(123, { insertValues: { status: 'active' } }))).toMatchInlineSnapshot(`
|
334
342
|
// // {
|
335
343
|
// // "bindValues": [
|
336
344
|
// // 123,
|
337
345
|
// // ],
|
338
346
|
// // "query": "SELECT * FROM 'todos_with_int_id' WHERE id = ?",
|
347
|
+
// // "schema": "...", // TODO determine schema
|
339
348
|
// // }
|
340
349
|
// // `)
|
341
350
|
// // })
|
@@ -343,7 +352,7 @@ describe('query builder', () => {
|
|
343
352
|
|
344
353
|
describe('write operations', () => {
|
345
354
|
it('should handle INSERT queries', () => {
|
346
|
-
expect(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' })
|
355
|
+
expect(dump(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }))).toMatchInlineSnapshot(`
|
347
356
|
{
|
348
357
|
"bindValues": [
|
349
358
|
"123",
|
@@ -351,12 +360,13 @@ describe('query builder', () => {
|
|
351
360
|
"active",
|
352
361
|
],
|
353
362
|
"query": "INSERT INTO 'todos' (id, text, status) VALUES (?, ?, ?)",
|
363
|
+
"schema": "number",
|
354
364
|
}
|
355
365
|
`)
|
356
366
|
})
|
357
367
|
|
358
368
|
it('should handle INSERT queries with undefined values', () => {
|
359
|
-
expect(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active', completed: undefined })
|
369
|
+
expect(dump(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active', completed: undefined })))
|
360
370
|
.toMatchInlineSnapshot(`
|
361
371
|
{
|
362
372
|
"bindValues": [
|
@@ -365,6 +375,7 @@ describe('query builder', () => {
|
|
365
375
|
"active",
|
366
376
|
],
|
367
377
|
"query": "INSERT INTO 'todos' (id, text, status) VALUES (?, ?, ?)",
|
378
|
+
"schema": "number",
|
368
379
|
}
|
369
380
|
`)
|
370
381
|
})
|
@@ -372,8 +383,8 @@ describe('query builder', () => {
|
|
372
383
|
// Test helped to catch a bindValues ordering bug
|
373
384
|
it('should handle INSERT queries (issue)', () => {
|
374
385
|
expect(
|
375
|
-
|
376
|
-
.insert({
|
386
|
+
dump(
|
387
|
+
db.issue.insert({
|
377
388
|
id: 1,
|
378
389
|
title: 'Revert the user profile page',
|
379
390
|
priority: 2,
|
@@ -381,8 +392,8 @@ describe('query builder', () => {
|
|
381
392
|
modified: new Date('2024-12-29T17:15:20.507Z'),
|
382
393
|
kanbanorder: 'a2',
|
383
394
|
creator: 'John Doe',
|
384
|
-
})
|
385
|
-
|
395
|
+
}),
|
396
|
+
),
|
386
397
|
).toMatchInlineSnapshot(`
|
387
398
|
{
|
388
399
|
"bindValues": [
|
@@ -395,32 +406,35 @@ describe('query builder', () => {
|
|
395
406
|
"John Doe",
|
396
407
|
],
|
397
408
|
"query": "INSERT INTO 'issue' (id, title, priority, created, modified, kanbanorder, creator) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
409
|
+
"schema": "number",
|
398
410
|
}
|
399
411
|
`)
|
400
412
|
})
|
401
413
|
|
402
414
|
it('should handle UPDATE queries', () => {
|
403
|
-
expect(db.todos.update({ status: 'completed' }).where({ id: '123' })
|
415
|
+
expect(dump(db.todos.update({ status: 'completed' }).where({ id: '123' }))).toMatchInlineSnapshot(`
|
404
416
|
{
|
405
417
|
"bindValues": [
|
406
418
|
"completed",
|
407
419
|
"123",
|
408
420
|
],
|
409
421
|
"query": "UPDATE 'todos' SET status = ? WHERE id = ?",
|
422
|
+
"schema": "number",
|
410
423
|
}
|
411
424
|
`)
|
412
425
|
|
413
426
|
// empty update set
|
414
|
-
expect(db.todos.update({}).where({ id: '123' })
|
427
|
+
expect(dump(db.todos.update({}).where({ id: '123' }))).toMatchInlineSnapshot(`
|
415
428
|
{
|
416
429
|
"bindValues": [],
|
417
430
|
"query": "SELECT 1",
|
431
|
+
"schema": "number",
|
418
432
|
}
|
419
433
|
`)
|
420
434
|
})
|
421
435
|
|
422
436
|
it('should handle UPDATE queries with undefined values', () => {
|
423
|
-
expect(db.todos.update({ status: undefined, text: 'some text' }).where({ id: '123' })
|
437
|
+
expect(dump(db.todos.update({ status: undefined, text: 'some text' }).where({ id: '123' })))
|
424
438
|
.toMatchInlineSnapshot(`
|
425
439
|
{
|
426
440
|
"bindValues": [
|
@@ -428,12 +442,13 @@ describe('query builder', () => {
|
|
428
442
|
"123",
|
429
443
|
],
|
430
444
|
"query": "UPDATE 'todos' SET text = ? WHERE id = ?",
|
445
|
+
"schema": "number",
|
431
446
|
}
|
432
447
|
`)
|
433
448
|
})
|
434
449
|
|
435
450
|
it('should handle UPDATE queries with undefined values (issue)', () => {
|
436
|
-
expect(db.issue.update({ priority: 2, creator: 'John Doe' }).where({ id: 1 })
|
451
|
+
expect(dump(db.issue.update({ priority: 2, creator: 'John Doe' }).where({ id: 1 }))).toMatchInlineSnapshot(`
|
437
452
|
{
|
438
453
|
"bindValues": [
|
439
454
|
2,
|
@@ -441,23 +456,25 @@ describe('query builder', () => {
|
|
441
456
|
1,
|
442
457
|
],
|
443
458
|
"query": "UPDATE 'issue' SET priority = ?, creator = ? WHERE id = ?",
|
459
|
+
"schema": "number",
|
444
460
|
}
|
445
461
|
`)
|
446
462
|
})
|
447
463
|
|
448
464
|
it('should handle DELETE queries', () => {
|
449
|
-
expect(db.todos.delete().where({ status: 'completed' })
|
465
|
+
expect(dump(db.todos.delete().where({ status: 'completed' }))).toMatchInlineSnapshot(`
|
450
466
|
{
|
451
467
|
"bindValues": [
|
452
468
|
"completed",
|
453
469
|
],
|
454
470
|
"query": "DELETE FROM 'todos' WHERE status = ?",
|
471
|
+
"schema": "number",
|
455
472
|
}
|
456
473
|
`)
|
457
474
|
})
|
458
475
|
|
459
476
|
it('should handle INSERT with ON CONFLICT', () => {
|
460
|
-
expect(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict('id', 'ignore')
|
477
|
+
expect(dump(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict('id', 'ignore')))
|
461
478
|
.toMatchInlineSnapshot(`
|
462
479
|
{
|
463
480
|
"bindValues": [
|
@@ -466,14 +483,16 @@ describe('query builder', () => {
|
|
466
483
|
"active",
|
467
484
|
],
|
468
485
|
"query": "INSERT INTO 'todos' (id, text, status) VALUES (?, ?, ?) ON CONFLICT (id) DO NOTHING",
|
486
|
+
"schema": "number",
|
469
487
|
}
|
470
488
|
`)
|
471
489
|
|
472
490
|
expect(
|
473
|
-
|
474
|
-
.
|
475
|
-
|
476
|
-
|
491
|
+
dump(
|
492
|
+
db.todos
|
493
|
+
.insert({ id: '123', text: 'Buy milk', status: 'active' })
|
494
|
+
.onConflict('id', 'update', { text: 'Buy soy milk', status: 'active' }),
|
495
|
+
),
|
477
496
|
).toMatchInlineSnapshot(`
|
478
497
|
{
|
479
498
|
"bindValues": [
|
@@ -484,10 +503,11 @@ describe('query builder', () => {
|
|
484
503
|
"active",
|
485
504
|
],
|
486
505
|
"query": "INSERT INTO 'todos' (id, text, status) VALUES (?, ?, ?) ON CONFLICT (id) DO UPDATE SET text = ?, status = ?",
|
506
|
+
"schema": "number",
|
487
507
|
}
|
488
508
|
`)
|
489
509
|
|
490
|
-
expect(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict('id', 'replace')
|
510
|
+
expect(dump(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict('id', 'replace')))
|
491
511
|
.toMatchInlineSnapshot(`
|
492
512
|
{
|
493
513
|
"bindValues": [
|
@@ -496,16 +516,14 @@ describe('query builder', () => {
|
|
496
516
|
"active",
|
497
517
|
],
|
498
518
|
"query": "INSERT OR REPLACE INTO 'todos' (id, text, status) VALUES (?, ?, ?)",
|
519
|
+
"schema": "number",
|
499
520
|
}
|
500
521
|
`)
|
501
522
|
})
|
502
523
|
|
503
524
|
it('should handle ON CONFLICT with multiple columns', () => {
|
504
525
|
expect(
|
505
|
-
db.todos
|
506
|
-
.insert({ id: '123', text: 'Buy milk', status: 'active' })
|
507
|
-
.onConflict(['id', 'status'], 'ignore')
|
508
|
-
.asSql(),
|
526
|
+
dump(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict(['id', 'status'], 'ignore')),
|
509
527
|
).toMatchInlineSnapshot(`
|
510
528
|
{
|
511
529
|
"bindValues": [
|
@@ -514,40 +532,44 @@ describe('query builder', () => {
|
|
514
532
|
"active",
|
515
533
|
],
|
516
534
|
"query": "INSERT INTO 'todos' (id, text, status) VALUES (?, ?, ?) ON CONFLICT (id, status) DO NOTHING",
|
535
|
+
"schema": "number",
|
517
536
|
}
|
518
537
|
`)
|
519
538
|
})
|
520
539
|
|
521
540
|
it('should handle RETURNING clause', () => {
|
522
|
-
expect(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).returning('id')
|
541
|
+
expect(dump(db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).returning('id')))
|
523
542
|
.toMatchInlineSnapshot(`
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
543
|
+
{
|
544
|
+
"bindValues": [
|
545
|
+
"123",
|
546
|
+
"Buy milk",
|
547
|
+
"active",
|
548
|
+
],
|
549
|
+
"query": "INSERT INTO 'todos' (id, text, status) VALUES (?, ?, ?) RETURNING id",
|
550
|
+
"schema": "ReadonlyArray<{ readonly id: string }>",
|
551
|
+
}
|
552
|
+
`)
|
533
553
|
|
534
|
-
expect(db.todos.update({ status: 'completed' }).where({ id: '123' }).returning('id')
|
554
|
+
expect(dump(db.todos.update({ status: 'completed' }).where({ id: '123' }).returning('id')))
|
535
555
|
.toMatchInlineSnapshot(`
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
556
|
+
{
|
557
|
+
"bindValues": [
|
558
|
+
"completed",
|
559
|
+
"123",
|
560
|
+
],
|
561
|
+
"query": "UPDATE 'todos' SET status = ? WHERE id = ? RETURNING id",
|
562
|
+
"schema": "ReadonlyArray<{ readonly id: string }>",
|
563
|
+
}
|
564
|
+
`)
|
544
565
|
|
545
|
-
expect(db.todos.delete().where({ status: 'completed' }).returning('id')
|
566
|
+
expect(dump(db.todos.delete().where({ status: 'completed' }).returning('id'))).toMatchInlineSnapshot(`
|
546
567
|
{
|
547
568
|
"bindValues": [
|
548
569
|
"completed",
|
549
570
|
],
|
550
571
|
"query": "DELETE FROM 'todos' WHERE status = ? RETURNING id",
|
572
|
+
"schema": "ReadonlyArray<{ readonly id: string }>",
|
551
573
|
}
|
552
574
|
`)
|
553
575
|
})
|