orez 0.2.27 → 0.2.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/cf-do/worker.d.ts +3 -0
- package/dist/cf-do/worker.d.ts.map +1 -1
- package/dist/cf-do/worker.js +37 -15
- package/dist/cf-do/worker.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
- package/src/admin/admin-data.test.ts +0 -348
- package/src/admin/http-proxy.ts +0 -252
- package/src/admin/log-store.ts +0 -192
- package/src/admin/server.ts +0 -471
- package/src/admin/ui.ts +0 -1322
- package/src/bench/proxy-throughput.bench.ts +0 -343
- package/src/bench/serial-mutations.bench.ts +0 -270
- package/src/browser.ts +0 -203
- package/src/cf-do/.wrangler/cache/cf.json +0 -1
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm +0 -0
- package/src/cf-do/.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/0ffaabee41a60e04dd0eb7db3073f0a40139e6a97ccd26823967acb652b89a7b.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-shm +0 -0
- package/src/cf-do/.wrangler/state/v3/do/zero-do-ZeroDO/metadata.sqlite-wal +0 -0
- package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-insertion-facade.js +0 -11
- package/src/cf-do/.wrangler/tmp/bundle-0z4CpE/middleware-loader.entry.ts +0 -134
- package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-insertion-facade.js +0 -11
- package/src/cf-do/.wrangler/tmp/bundle-vYmw0E/middleware-loader.entry.ts +0 -134
- package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js +0 -1059
- package/src/cf-do/.wrangler/tmp/dev-cbILNo/worker.js.map +0 -8
- package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js +0 -1059
- package/src/cf-do/.wrangler/tmp/dev-qbho19/worker.js.map +0 -8
- package/src/cf-do/ARCHITECTURE.md +0 -93
- package/src/cf-do/CHAT_E2E.md +0 -213
- package/src/cf-do/watermark.test.ts +0 -103
- package/src/cf-do/watermark.ts +0 -118
- package/src/cf-do/worker.ts +0 -1041
- package/src/cf-do/wrangler.toml +0 -11
- package/src/cf-pglite/README.md +0 -19
- package/src/change-tracking.ts +0 -25
- package/src/child-process.test.ts +0 -147
- package/src/child-process.ts +0 -90
- package/src/cli-entry.ts +0 -72
- package/src/cli.test.ts +0 -40
- package/src/cli.ts +0 -1214
- package/src/config.ts +0 -150
- package/src/do-sql-tracking.test.ts +0 -19
- package/src/do-sql-tracking.ts +0 -19
- package/src/index.ts +0 -1215
- package/src/integration/integration.test.ts +0 -517
- package/src/integration/native-binary.guard.test.ts +0 -13
- package/src/integration/native-startup.test.ts +0 -44
- package/src/integration/replication-latency.test.ts +0 -428
- package/src/integration/restore-live-stress.test.ts +0 -433
- package/src/integration/restore-reset.test.ts +0 -400
- package/src/integration/restore.test.ts +0 -274
- package/src/integration/test-permissions.ts +0 -147
- package/src/load-config.ts +0 -46
- package/src/log.ts +0 -96
- package/src/mutex.ts +0 -47
- package/src/pg-proxy-browser.singledb.test.ts +0 -233
- package/src/pg-proxy-browser.ts +0 -2022
- package/src/pg-proxy-do-backend.test.ts +0 -3890
- package/src/pg-proxy-do-backend.ts +0 -7191
- package/src/pg-proxy.ts +0 -1087
- package/src/pg-sqlite-compiler/README.md +0 -53
- package/src/pg-sqlite-compiler/catalog/seed.ts +0 -524
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/arithmetic.json +0 -307
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/array.json +0 -377
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/cast.json +0 -12
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/catalog.json +0 -447
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/create-table.json +0 -32
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/datetime.json +0 -397
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/enum.json +0 -337
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/insert.json +0 -337
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/json.json +0 -537
- package/src/pg-sqlite-compiler/fixtures/pgsqlite/misc.json +0 -1837
- package/src/pg-sqlite-compiler/index.ts +0 -73
- package/src/pg-sqlite-compiler/integration.test.ts +0 -136
- package/src/pg-sqlite-compiler/passes/ast-utils.ts +0 -113
- package/src/pg-sqlite-compiler/passes/catalog.ts +0 -65
- package/src/pg-sqlite-compiler/passes/datetime.ts +0 -74
- package/src/pg-sqlite-compiler/passes/index.ts +0 -49
- package/src/pg-sqlite-compiler/passes/types.ts +0 -156
- package/src/pg-sqlite-compiler/smoke.test.ts +0 -69
- package/src/pg-sqlite-compiler/test/catalog.test.ts +0 -171
- package/src/pg-sqlite-compiler/test/corpus.test.ts +0 -161
- package/src/pg-sqlite-compiler/test/datetime.oracle.test.ts +0 -102
- package/src/pg-sqlite-compiler/test/oracle.ts +0 -237
- package/src/pg-sqlite-compiler/test/types.test.ts +0 -109
- package/src/pg-sqlite-compiler/types.ts +0 -63
- package/src/pglite-ipc.test.ts +0 -116
- package/src/pglite-ipc.ts +0 -266
- package/src/pglite-manager.ts +0 -557
- package/src/pglite-web-proxy.test.ts +0 -57
- package/src/pglite-web-proxy.ts +0 -221
- package/src/pglite-web-worker.ts +0 -152
- package/src/pglite-worker-thread.ts +0 -253
- package/src/port.ts +0 -25
- package/src/process-title.ts +0 -9
- package/src/recovery.ts +0 -155
- package/src/replication/change-tracker.test.ts +0 -357
- package/src/replication/change-tracker.ts +0 -279
- package/src/replication/handler.test.ts +0 -511
- package/src/replication/handler.ts +0 -1190
- package/src/replication/pgoutput-encoder.test.ts +0 -697
- package/src/replication/pgoutput-encoder.ts +0 -373
- package/src/replication/tcp-replication.test.ts +0 -876
- package/src/replication/zero-compat.test.ts +0 -1150
- package/src/restore-stress.test.ts +0 -188
- package/src/s3-local.ts +0 -203
- package/src/shim/hooks.mjs +0 -120
- package/src/shim/register.mjs +0 -4
- package/src/sqlite-mode/apply-mode.ts +0 -224
- package/src/sqlite-mode/index.ts +0 -15
- package/src/sqlite-mode/native-binary.ts +0 -89
- package/src/sqlite-mode/package-resolve.ts +0 -17
- package/src/sqlite-mode/resolve-mode.ts +0 -80
- package/src/sqlite-mode/shim-template.ts +0 -159
- package/src/sqlite-mode/sqlite-mode.test.ts +0 -427
- package/src/sqlite-mode/types.ts +0 -30
- package/src/vite-plugin.ts +0 -67
- package/src/wasm-sqlite.test.ts +0 -537
- package/src/worker/browser-admin.ts +0 -52
- package/src/worker/browser-build-config.test.ts +0 -71
- package/src/worker/browser-build-config.ts +0 -109
- package/src/worker/browser-embed-admin.test.ts +0 -75
- package/src/worker/browser-embed.ts +0 -345
- package/src/worker/cf-patches.ts +0 -384
- package/src/worker/embed-integration.test.ts +0 -321
- package/src/worker/index.ts +0 -138
- package/src/worker/shims/fastify.test.ts +0 -255
- package/src/worker/shims/fastify.ts +0 -306
- package/src/worker/shims/http-service.test.ts +0 -355
- package/src/worker/shims/http-service.ts +0 -293
- package/src/worker/shims/node-stub.ts +0 -290
- package/src/worker/shims/oxfmt.ts +0 -3
- package/src/worker/shims/postgres-browser.ts +0 -59
- package/src/worker/shims/postgres-socket.test.ts +0 -576
- package/src/worker/shims/postgres-socket.ts +0 -310
- package/src/worker/shims/postgres.test.ts +0 -364
- package/src/worker/shims/postgres.ts +0 -1454
- package/src/worker/shims/sqlite-browser.test.ts +0 -233
- package/src/worker/shims/sqlite-browser.ts +0 -175
- package/src/worker/shims/sqlite.test.ts +0 -786
- package/src/worker/shims/sqlite.ts +0 -978
- package/src/worker/shims/stream-browser.ts +0 -15
- package/src/worker/shims/ws-browser.test.ts +0 -205
- package/src/worker/shims/ws-browser.ts +0 -248
- package/src/worker/shims/ws.test.ts +0 -288
- package/src/worker/shims/ws.ts +0 -467
- package/src/worker/shims/zero-process-env.ts +0 -11
- package/src/worker/types.ts +0 -75
- package/src/worker/worker-integration.test.ts +0 -223
- package/src/worker/worker.test.ts +0 -136
- package/src/worker/zero-cache-embed-cf.ts +0 -463
- package/src/worker/zero-cache-embed.ts +0 -277
|
@@ -1,786 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* sqlite shim tests.
|
|
3
|
-
*
|
|
4
|
-
* uses a mock SqlStorageLike backed by better-sqlite3 to validate that our
|
|
5
|
-
* shim correctly bridges between the better-sqlite3 api and DO SqlStorage.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// @ts-expect-error - CJS module
|
|
9
|
-
import BedrockSqlite from 'bedrock-sqlite'
|
|
10
|
-
const BetterSqlite3 = BedrockSqlite.Database
|
|
11
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
12
|
-
|
|
13
|
-
import {
|
|
14
|
-
Database,
|
|
15
|
-
Statement,
|
|
16
|
-
StatementRunner,
|
|
17
|
-
SqliteError,
|
|
18
|
-
type SqlStorageLike,
|
|
19
|
-
type SqlStorageCursor,
|
|
20
|
-
type SqlStorageValue,
|
|
21
|
-
} from './sqlite.js'
|
|
22
|
-
|
|
23
|
-
// -- mock SqlStorageLike backed by better-sqlite3 --
|
|
24
|
-
|
|
25
|
-
function createMockSqlStorage(): SqlStorageLike {
|
|
26
|
-
const nativeDb = new BetterSqlite3(':memory:')
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
exec(query: string, ...bindings: SqlStorageValue[]): SqlStorageCursor {
|
|
30
|
-
const trimmed = query.trim()
|
|
31
|
-
|
|
32
|
-
// handle statements that don't return rows
|
|
33
|
-
const isWrite =
|
|
34
|
-
/^(INSERT|UPDATE|DELETE|CREATE|DROP|ALTER|BEGIN|COMMIT|ROLLBACK|PRAGMA|SAVEPOINT|RELEASE|VACUUM)/i.test(
|
|
35
|
-
trimmed
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
if (isWrite && !trimmed.toUpperCase().startsWith('PRAGMA')) {
|
|
39
|
-
const stmt = nativeDb.prepare(query)
|
|
40
|
-
const result = stmt.run(...bindings)
|
|
41
|
-
return {
|
|
42
|
-
toArray: () => [],
|
|
43
|
-
get rowsRead() {
|
|
44
|
-
return 0
|
|
45
|
-
},
|
|
46
|
-
get rowsWritten() {
|
|
47
|
-
return result.changes
|
|
48
|
-
},
|
|
49
|
-
get columnNames() {
|
|
50
|
-
return []
|
|
51
|
-
},
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// for pragmas — some are SET (contain =), some are GET
|
|
56
|
-
if (trimmed.toUpperCase().startsWith('PRAGMA')) {
|
|
57
|
-
try {
|
|
58
|
-
const stmt = nativeDb.prepare(query)
|
|
59
|
-
const rows = stmt.all(...bindings)
|
|
60
|
-
return {
|
|
61
|
-
toArray: () => rows as Record<string, SqlStorageValue>[],
|
|
62
|
-
get rowsRead() {
|
|
63
|
-
return rows.length
|
|
64
|
-
},
|
|
65
|
-
get rowsWritten() {
|
|
66
|
-
return 0
|
|
67
|
-
},
|
|
68
|
-
get columnNames() {
|
|
69
|
-
if (rows.length > 0) return Object.keys(rows[0] as object)
|
|
70
|
-
return []
|
|
71
|
-
},
|
|
72
|
-
}
|
|
73
|
-
} catch {
|
|
74
|
-
// pragma that modifies state (e.g., journal_mode = WAL)
|
|
75
|
-
nativeDb.pragma(query.replace(/^PRAGMA\s+/i, ''))
|
|
76
|
-
return {
|
|
77
|
-
toArray: () => [],
|
|
78
|
-
get rowsRead() {
|
|
79
|
-
return 0
|
|
80
|
-
},
|
|
81
|
-
get rowsWritten() {
|
|
82
|
-
return 0
|
|
83
|
-
},
|
|
84
|
-
get columnNames() {
|
|
85
|
-
return []
|
|
86
|
-
},
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// select / other read queries
|
|
92
|
-
const stmt = nativeDb.prepare(query)
|
|
93
|
-
const rows = stmt.all(...bindings)
|
|
94
|
-
const columns = stmt.columns().map((c: { name: string }) => c.name)
|
|
95
|
-
return {
|
|
96
|
-
toArray: () => rows as Record<string, SqlStorageValue>[],
|
|
97
|
-
get rowsRead() {
|
|
98
|
-
return rows.length
|
|
99
|
-
},
|
|
100
|
-
get rowsWritten() {
|
|
101
|
-
return 0
|
|
102
|
-
},
|
|
103
|
-
get columnNames() {
|
|
104
|
-
return columns
|
|
105
|
-
},
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
// expose native db for cleanup
|
|
109
|
-
_nativeDb: nativeDb,
|
|
110
|
-
} as SqlStorageLike & { _nativeDb: typeof nativeDb }
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// -- tests --
|
|
114
|
-
|
|
115
|
-
describe('SqliteError', () => {
|
|
116
|
-
it('creates error with message and code', () => {
|
|
117
|
-
const err = new SqliteError('table not found', 'SQLITE_ERROR')
|
|
118
|
-
expect(err).toBeInstanceOf(Error)
|
|
119
|
-
expect(err).toBeInstanceOf(SqliteError)
|
|
120
|
-
expect(err.message).toBe('table not found')
|
|
121
|
-
expect(err.code).toBe('SQLITE_ERROR')
|
|
122
|
-
expect(err.name).toBe('SqliteError')
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
it('captures stack trace', () => {
|
|
126
|
-
const err = new SqliteError('test', 'SQLITE_MISUSE')
|
|
127
|
-
expect(err.stack).toBeDefined()
|
|
128
|
-
expect(err.stack).toContain('SqliteError')
|
|
129
|
-
})
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
describe('Database', () => {
|
|
133
|
-
let mock: SqlStorageLike & { _nativeDb: any }
|
|
134
|
-
let db: Database
|
|
135
|
-
|
|
136
|
-
beforeEach(() => {
|
|
137
|
-
mock = createMockSqlStorage() as SqlStorageLike & { _nativeDb: any }
|
|
138
|
-
db = new Database(mock)
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
afterEach(() => {
|
|
142
|
-
db.close()
|
|
143
|
-
mock._nativeDb.close()
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it('constructs with SqlStorageLike', () => {
|
|
147
|
-
expect(db.open).toBe(true)
|
|
148
|
-
expect(db.name).toBe(':do-storage:')
|
|
149
|
-
expect(db.inTransaction).toBe(false)
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
it('throws when constructed with a string and no globalThis storage', () => {
|
|
153
|
-
// clear any global DO storage
|
|
154
|
-
const prev = (globalThis as any).__orez_do_sqlite
|
|
155
|
-
delete (globalThis as any).__orez_do_sqlite
|
|
156
|
-
try {
|
|
157
|
-
expect(() => new Database('/path/to/db' as unknown as SqlStorageLike)).toThrow(
|
|
158
|
-
'no DO storage on globalThis.__orez_do_sqlite'
|
|
159
|
-
)
|
|
160
|
-
} finally {
|
|
161
|
-
if (prev) (globalThis as any).__orez_do_sqlite = prev
|
|
162
|
-
}
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
it('close sets open to false', () => {
|
|
166
|
-
expect(db.open).toBe(true)
|
|
167
|
-
const ret = db.close()
|
|
168
|
-
expect(db.open).toBe(false)
|
|
169
|
-
expect(ret).toBe(db) // chainable
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
it('unsafeMode is a no-op that returns this', () => {
|
|
173
|
-
expect(db.unsafeMode(true)).toBe(db)
|
|
174
|
-
expect(db.unsafeMode(false)).toBe(db)
|
|
175
|
-
expect(db.unsafeMode()).toBe(db)
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
it('defaultSafeIntegers is a no-op that returns this', () => {
|
|
179
|
-
expect(db.defaultSafeIntegers(true)).toBe(db)
|
|
180
|
-
})
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
describe('Database.exec', () => {
|
|
184
|
-
let mock: SqlStorageLike & { _nativeDb: any }
|
|
185
|
-
let db: Database
|
|
186
|
-
|
|
187
|
-
beforeEach(() => {
|
|
188
|
-
mock = createMockSqlStorage() as SqlStorageLike & { _nativeDb: any }
|
|
189
|
-
db = new Database(mock)
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
afterEach(() => {
|
|
193
|
-
db.close()
|
|
194
|
-
mock._nativeDb.close()
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
it('executes single statement', () => {
|
|
198
|
-
const ret = db.exec('CREATE TABLE t (id INTEGER PRIMARY KEY)')
|
|
199
|
-
expect(ret).toBe(db) // chainable
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
it('executes multiple statements separated by semicolons', () => {
|
|
203
|
-
db.exec(
|
|
204
|
-
'CREATE TABLE t (id INTEGER PRIMARY KEY); CREATE TABLE t2 (id INTEGER PRIMARY KEY)'
|
|
205
|
-
)
|
|
206
|
-
// verify both tables exist
|
|
207
|
-
const r1 = db
|
|
208
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='t'")
|
|
209
|
-
.get()
|
|
210
|
-
const r2 = db
|
|
211
|
-
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='t2'")
|
|
212
|
-
.get()
|
|
213
|
-
expect(r1).toEqual({ name: 't' })
|
|
214
|
-
expect(r2).toEqual({ name: 't2' })
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
it('throws on closed database', () => {
|
|
218
|
-
db.close()
|
|
219
|
-
expect(() => db.exec('SELECT 1')).toThrow('not open')
|
|
220
|
-
})
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
describe('Database.prepare / Statement', () => {
|
|
224
|
-
let mock: SqlStorageLike & { _nativeDb: any }
|
|
225
|
-
let db: Database
|
|
226
|
-
|
|
227
|
-
beforeEach(() => {
|
|
228
|
-
mock = createMockSqlStorage() as SqlStorageLike & { _nativeDb: any }
|
|
229
|
-
db = new Database(mock)
|
|
230
|
-
db.exec('CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT, val INTEGER)')
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
afterEach(() => {
|
|
234
|
-
db.close()
|
|
235
|
-
mock._nativeDb.close()
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
it('prepare returns a Statement', () => {
|
|
239
|
-
const stmt = db.prepare('SELECT 1 as x')
|
|
240
|
-
expect(stmt).toBeInstanceOf(Statement)
|
|
241
|
-
expect(stmt.source).toBe('SELECT 1 as x')
|
|
242
|
-
})
|
|
243
|
-
|
|
244
|
-
it('throws prepare on closed db', () => {
|
|
245
|
-
db.close()
|
|
246
|
-
expect(() => db.prepare('SELECT 1')).toThrow('not open')
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
it('run executes write and returns RunResult', () => {
|
|
250
|
-
const result = db
|
|
251
|
-
.prepare('INSERT INTO items (name, val) VALUES (?, ?)')
|
|
252
|
-
.run('foo', 42)
|
|
253
|
-
expect(result).toHaveProperty('changes')
|
|
254
|
-
expect(result).toHaveProperty('lastInsertRowid')
|
|
255
|
-
expect(result.changes).toBe(1)
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
it('get returns first row or undefined', () => {
|
|
259
|
-
db.prepare('INSERT INTO items (name, val) VALUES (?, ?)').run('a', 1)
|
|
260
|
-
db.prepare('INSERT INTO items (name, val) VALUES (?, ?)').run('b', 2)
|
|
261
|
-
|
|
262
|
-
const row = db.prepare('SELECT * FROM items WHERE name = ?').get('a')
|
|
263
|
-
expect(row).toEqual({ id: 1, name: 'a', val: 1 })
|
|
264
|
-
|
|
265
|
-
const missing = db.prepare('SELECT * FROM items WHERE name = ?').get('zzz')
|
|
266
|
-
expect(missing).toBeUndefined()
|
|
267
|
-
})
|
|
268
|
-
|
|
269
|
-
it('all returns all rows', () => {
|
|
270
|
-
db.prepare('INSERT INTO items (name, val) VALUES (?, ?)').run('x', 10)
|
|
271
|
-
db.prepare('INSERT INTO items (name, val) VALUES (?, ?)').run('y', 20)
|
|
272
|
-
|
|
273
|
-
const rows = db.prepare('SELECT * FROM items ORDER BY id').all()
|
|
274
|
-
expect(rows).toEqual([
|
|
275
|
-
{ id: 1, name: 'x', val: 10 },
|
|
276
|
-
{ id: 2, name: 'y', val: 20 },
|
|
277
|
-
])
|
|
278
|
-
})
|
|
279
|
-
|
|
280
|
-
it('all returns empty array when no rows', () => {
|
|
281
|
-
const rows = db.prepare('SELECT * FROM items').all()
|
|
282
|
-
expect(rows).toEqual([])
|
|
283
|
-
})
|
|
284
|
-
|
|
285
|
-
it('iterate yields rows lazily', () => {
|
|
286
|
-
db.prepare('INSERT INTO items (name, val) VALUES (?, ?)').run('a', 1)
|
|
287
|
-
db.prepare('INSERT INTO items (name, val) VALUES (?, ?)').run('b', 2)
|
|
288
|
-
|
|
289
|
-
const iter = db.prepare('SELECT * FROM items ORDER BY id').iterate()
|
|
290
|
-
const results: unknown[] = []
|
|
291
|
-
for (const row of iter) {
|
|
292
|
-
results.push(row)
|
|
293
|
-
}
|
|
294
|
-
expect(results).toEqual([
|
|
295
|
-
{ id: 1, name: 'a', val: 1 },
|
|
296
|
-
{ id: 2, name: 'b', val: 2 },
|
|
297
|
-
])
|
|
298
|
-
})
|
|
299
|
-
|
|
300
|
-
it('safeIntegers is a no-op returning this', () => {
|
|
301
|
-
const stmt = db.prepare('SELECT 1 as x')
|
|
302
|
-
expect(stmt.safeIntegers(true)).toBe(stmt)
|
|
303
|
-
expect(stmt.safeIntegers()).toBe(stmt)
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
it('statement methods throw on closed db', () => {
|
|
307
|
-
const stmt = db.prepare('SELECT 1 as x')
|
|
308
|
-
db.close()
|
|
309
|
-
expect(() => stmt.run()).toThrow('not open')
|
|
310
|
-
expect(() => stmt.get()).toThrow('not open')
|
|
311
|
-
expect(() => stmt.all()).toThrow('not open')
|
|
312
|
-
})
|
|
313
|
-
})
|
|
314
|
-
|
|
315
|
-
describe('Database.pragma', () => {
|
|
316
|
-
let mock: SqlStorageLike & { _nativeDb: any }
|
|
317
|
-
let db: Database
|
|
318
|
-
|
|
319
|
-
beforeEach(() => {
|
|
320
|
-
mock = createMockSqlStorage() as SqlStorageLike & { _nativeDb: any }
|
|
321
|
-
db = new Database(mock)
|
|
322
|
-
})
|
|
323
|
-
|
|
324
|
-
afterEach(() => {
|
|
325
|
-
mock._nativeDb.close()
|
|
326
|
-
})
|
|
327
|
-
|
|
328
|
-
it('reads a pragma and returns array of objects', () => {
|
|
329
|
-
const result = db.pragma('journal_mode')
|
|
330
|
-
expect(Array.isArray(result)).toBe(true)
|
|
331
|
-
expect((result as unknown[]).length).toBeGreaterThan(0)
|
|
332
|
-
})
|
|
333
|
-
|
|
334
|
-
it('reads pragma with simple option', () => {
|
|
335
|
-
const result = db.pragma('journal_mode', { simple: true })
|
|
336
|
-
// should return a scalar value, not an array
|
|
337
|
-
expect(Array.isArray(result)).toBe(false)
|
|
338
|
-
expect(typeof result).toBe('string')
|
|
339
|
-
})
|
|
340
|
-
|
|
341
|
-
it('sets a pragma', () => {
|
|
342
|
-
const result = db.pragma('cache_size = 2000')
|
|
343
|
-
// set pragmas return the new value or empty
|
|
344
|
-
expect(Array.isArray(result)).toBe(true)
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
it('skips optimize pragma', () => {
|
|
348
|
-
const result = db.pragma('optimize')
|
|
349
|
-
expect(result).toEqual([])
|
|
350
|
-
|
|
351
|
-
const simple = db.pragma('optimize', { simple: true })
|
|
352
|
-
expect(simple).toBeUndefined()
|
|
353
|
-
})
|
|
354
|
-
|
|
355
|
-
it('throws on closed db', () => {
|
|
356
|
-
db.close()
|
|
357
|
-
expect(() => db.pragma('journal_mode')).toThrow('not open')
|
|
358
|
-
})
|
|
359
|
-
})
|
|
360
|
-
|
|
361
|
-
describe('Database.transaction', () => {
|
|
362
|
-
let mock: SqlStorageLike & { _nativeDb: any }
|
|
363
|
-
let db: Database
|
|
364
|
-
|
|
365
|
-
beforeEach(() => {
|
|
366
|
-
mock = createMockSqlStorage() as SqlStorageLike & { _nativeDb: any }
|
|
367
|
-
db = new Database(mock)
|
|
368
|
-
db.exec('CREATE TABLE t (id INTEGER PRIMARY KEY)')
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
afterEach(() => {
|
|
372
|
-
mock._nativeDb.close()
|
|
373
|
-
})
|
|
374
|
-
|
|
375
|
-
it('commits on success', () => {
|
|
376
|
-
const txn = db.transaction(() => {
|
|
377
|
-
db.prepare('INSERT INTO t VALUES (1)').run()
|
|
378
|
-
db.prepare('INSERT INTO t VALUES (2)').run()
|
|
379
|
-
})
|
|
380
|
-
|
|
381
|
-
txn()
|
|
382
|
-
|
|
383
|
-
const rows = db.prepare('SELECT * FROM t').all()
|
|
384
|
-
expect(rows).toEqual([{ id: 1 }, { id: 2 }])
|
|
385
|
-
})
|
|
386
|
-
|
|
387
|
-
it('rolls back on error', () => {
|
|
388
|
-
const txn = db.transaction(() => {
|
|
389
|
-
db.prepare('INSERT INTO t VALUES (1)').run()
|
|
390
|
-
throw new Error('boom')
|
|
391
|
-
})
|
|
392
|
-
|
|
393
|
-
expect(() => txn()).toThrow('boom')
|
|
394
|
-
|
|
395
|
-
const rows = db.prepare('SELECT * FROM t').all()
|
|
396
|
-
expect(rows).toEqual([])
|
|
397
|
-
})
|
|
398
|
-
|
|
399
|
-
it('inTransaction reflects state', () => {
|
|
400
|
-
expect(db.inTransaction).toBe(false)
|
|
401
|
-
|
|
402
|
-
db.transaction(() => {
|
|
403
|
-
expect(db.inTransaction).toBe(true)
|
|
404
|
-
})()
|
|
405
|
-
|
|
406
|
-
expect(db.inTransaction).toBe(false)
|
|
407
|
-
})
|
|
408
|
-
|
|
409
|
-
it('inTransaction is false after rollback', () => {
|
|
410
|
-
try {
|
|
411
|
-
db.transaction(() => {
|
|
412
|
-
throw new Error('fail')
|
|
413
|
-
})()
|
|
414
|
-
} catch {}
|
|
415
|
-
|
|
416
|
-
expect(db.inTransaction).toBe(false)
|
|
417
|
-
})
|
|
418
|
-
|
|
419
|
-
it('has deferred/immediate/exclusive variants', () => {
|
|
420
|
-
const txn = db.transaction(() => {
|
|
421
|
-
db.prepare('INSERT INTO t VALUES (99)').run()
|
|
422
|
-
})
|
|
423
|
-
|
|
424
|
-
expect(typeof txn.deferred).toBe('function')
|
|
425
|
-
expect(typeof txn.immediate).toBe('function')
|
|
426
|
-
expect(typeof txn.exclusive).toBe('function')
|
|
427
|
-
})
|
|
428
|
-
|
|
429
|
-
it('immediate variant works', () => {
|
|
430
|
-
const txn = db.transaction(() => {
|
|
431
|
-
db.prepare('INSERT INTO t VALUES (42)').run()
|
|
432
|
-
})
|
|
433
|
-
|
|
434
|
-
txn.immediate()
|
|
435
|
-
|
|
436
|
-
const rows = db.prepare('SELECT * FROM t').all()
|
|
437
|
-
expect(rows).toEqual([{ id: 42 }])
|
|
438
|
-
})
|
|
439
|
-
|
|
440
|
-
it('nested transaction calls run fn without extra BEGIN', () => {
|
|
441
|
-
const inner = db.transaction(() => {
|
|
442
|
-
db.prepare('INSERT INTO t VALUES (2)').run()
|
|
443
|
-
})
|
|
444
|
-
|
|
445
|
-
const outer = db.transaction(() => {
|
|
446
|
-
db.prepare('INSERT INTO t VALUES (1)').run()
|
|
447
|
-
inner() // should NOT issue another BEGIN
|
|
448
|
-
db.prepare('INSERT INTO t VALUES (3)').run()
|
|
449
|
-
})
|
|
450
|
-
|
|
451
|
-
outer()
|
|
452
|
-
|
|
453
|
-
const rows = db.prepare('SELECT * FROM t ORDER BY id').all()
|
|
454
|
-
expect(rows).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }])
|
|
455
|
-
})
|
|
456
|
-
|
|
457
|
-
it('passes arguments through to wrapped function', () => {
|
|
458
|
-
const txn = db.transaction((id: unknown, val: unknown) => {
|
|
459
|
-
db.prepare('INSERT INTO t VALUES (?)').run(id)
|
|
460
|
-
return val
|
|
461
|
-
})
|
|
462
|
-
|
|
463
|
-
const result = txn(7, 'hello')
|
|
464
|
-
expect(result).toBe('hello')
|
|
465
|
-
expect(db.prepare('SELECT * FROM t').all()).toEqual([{ id: 7 }])
|
|
466
|
-
})
|
|
467
|
-
})
|
|
468
|
-
|
|
469
|
-
describe('StatementRunner', () => {
|
|
470
|
-
let mock: SqlStorageLike & { _nativeDb: any }
|
|
471
|
-
let db: Database
|
|
472
|
-
let runner: StatementRunner
|
|
473
|
-
|
|
474
|
-
beforeEach(() => {
|
|
475
|
-
mock = createMockSqlStorage() as SqlStorageLike & { _nativeDb: any }
|
|
476
|
-
db = new Database(mock)
|
|
477
|
-
db.exec('CREATE TABLE foo (id INTEGER PRIMARY KEY, name TEXT)')
|
|
478
|
-
runner = new StatementRunner(db)
|
|
479
|
-
})
|
|
480
|
-
|
|
481
|
-
afterEach(() => {
|
|
482
|
-
mock._nativeDb.close()
|
|
483
|
-
})
|
|
484
|
-
|
|
485
|
-
it('run inserts data', () => {
|
|
486
|
-
const result = runner.run('INSERT INTO foo (name) VALUES (?)', 'bar')
|
|
487
|
-
expect(result).toHaveProperty('changes')
|
|
488
|
-
expect(result.changes).toBe(1)
|
|
489
|
-
})
|
|
490
|
-
|
|
491
|
-
it('get returns single row', () => {
|
|
492
|
-
runner.run('INSERT INTO foo (name) VALUES (?)', 'baz')
|
|
493
|
-
const row = runner.get('SELECT * FROM foo WHERE name = ?', 'baz')
|
|
494
|
-
expect(row).toEqual({ id: 1, name: 'baz' })
|
|
495
|
-
})
|
|
496
|
-
|
|
497
|
-
it('get returns undefined for no match', () => {
|
|
498
|
-
const row = runner.get('SELECT * FROM foo WHERE name = ?', 'nope')
|
|
499
|
-
expect(row).toBeUndefined()
|
|
500
|
-
})
|
|
501
|
-
|
|
502
|
-
it('all returns all rows', () => {
|
|
503
|
-
runner.run('INSERT INTO foo (name) VALUES (?)', 'a')
|
|
504
|
-
runner.run('INSERT INTO foo (name) VALUES (?)', 'b')
|
|
505
|
-
const rows = runner.all('SELECT * FROM foo ORDER BY id')
|
|
506
|
-
expect(rows).toEqual([
|
|
507
|
-
{ id: 1, name: 'a' },
|
|
508
|
-
{ id: 2, name: 'b' },
|
|
509
|
-
])
|
|
510
|
-
})
|
|
511
|
-
|
|
512
|
-
it('begin/commit transaction', () => {
|
|
513
|
-
runner.begin()
|
|
514
|
-
runner.run('INSERT INTO foo (name) VALUES (?)', 'x')
|
|
515
|
-
runner.run('INSERT INTO foo (name) VALUES (?)', 'y')
|
|
516
|
-
runner.commit()
|
|
517
|
-
|
|
518
|
-
expect(runner.all('SELECT * FROM foo ORDER BY id')).toEqual([
|
|
519
|
-
{ id: 1, name: 'x' },
|
|
520
|
-
{ id: 2, name: 'y' },
|
|
521
|
-
])
|
|
522
|
-
})
|
|
523
|
-
|
|
524
|
-
it('begin/rollback discards changes', () => {
|
|
525
|
-
runner.begin()
|
|
526
|
-
runner.run('INSERT INTO foo (name) VALUES (?)', 'x')
|
|
527
|
-
runner.rollback()
|
|
528
|
-
|
|
529
|
-
expect(runner.all('SELECT * FROM foo')).toEqual([])
|
|
530
|
-
})
|
|
531
|
-
|
|
532
|
-
it('beginConcurrent maps to regular BEGIN', () => {
|
|
533
|
-
runner.beginConcurrent()
|
|
534
|
-
runner.run('INSERT INTO foo (name) VALUES (?)', 'concurrent')
|
|
535
|
-
runner.commit()
|
|
536
|
-
|
|
537
|
-
expect(runner.all('SELECT * FROM foo')).toEqual([{ id: 1, name: 'concurrent' }])
|
|
538
|
-
})
|
|
539
|
-
|
|
540
|
-
it('beginImmediate works', () => {
|
|
541
|
-
runner.beginImmediate()
|
|
542
|
-
runner.run('INSERT INTO foo (name) VALUES (?)', 'immediate')
|
|
543
|
-
runner.commit()
|
|
544
|
-
|
|
545
|
-
expect(runner.all('SELECT * FROM foo')).toEqual([{ id: 1, name: 'immediate' }])
|
|
546
|
-
})
|
|
547
|
-
|
|
548
|
-
it('caches prepared statements', () => {
|
|
549
|
-
// run same SQL multiple times — statements should be reused
|
|
550
|
-
runner.run('INSERT INTO foo (name) VALUES (?)', 'a')
|
|
551
|
-
runner.run('INSERT INTO foo (name) VALUES (?)', 'b')
|
|
552
|
-
runner.run('INSERT INTO foo (name) VALUES (?)', 'c')
|
|
553
|
-
|
|
554
|
-
expect(runner.all('SELECT count(*) as c FROM foo')).toEqual([{ c: 3 }])
|
|
555
|
-
})
|
|
556
|
-
})
|
|
557
|
-
|
|
558
|
-
describe('DO snapshot transactions', () => {
|
|
559
|
-
let mock: SqlStorageLike & { _nativeDb: any; transactionSync: <T>(fn: () => T) => T }
|
|
560
|
-
let live: Database
|
|
561
|
-
let snapshot: Database
|
|
562
|
-
|
|
563
|
-
beforeEach(() => {
|
|
564
|
-
mock = createMockSqlStorage() as SqlStorageLike & {
|
|
565
|
-
_nativeDb: any
|
|
566
|
-
transactionSync: <T>(fn: () => T) => T
|
|
567
|
-
}
|
|
568
|
-
mock.transactionSync = (fn) => fn()
|
|
569
|
-
live = new Database(mock)
|
|
570
|
-
snapshot = new Database(mock)
|
|
571
|
-
live.exec('CREATE TABLE todo (id TEXT PRIMARY KEY, title TEXT, _0_version TEXT)')
|
|
572
|
-
live.prepare('INSERT INTO todo VALUES (?, ?, ?)').run('1', 'old', '01')
|
|
573
|
-
})
|
|
574
|
-
|
|
575
|
-
afterEach(() => {
|
|
576
|
-
live.close()
|
|
577
|
-
snapshot.close()
|
|
578
|
-
mock._nativeDb.close()
|
|
579
|
-
})
|
|
580
|
-
|
|
581
|
-
it('keeps BEGIN CONCURRENT reads stable until rollback', () => {
|
|
582
|
-
snapshot.prepare('BEGIN CONCURRENT').run()
|
|
583
|
-
expect(snapshot.prepare('SELECT title FROM todo WHERE id = ?').get('1')).toEqual({
|
|
584
|
-
title: 'old',
|
|
585
|
-
})
|
|
586
|
-
|
|
587
|
-
live
|
|
588
|
-
.prepare('UPDATE todo SET title = ?, _0_version = ? WHERE id = ?')
|
|
589
|
-
.run('new', '02', '1')
|
|
590
|
-
|
|
591
|
-
expect(live.prepare('SELECT title FROM todo WHERE id = ?').get('1')).toEqual({
|
|
592
|
-
title: 'new',
|
|
593
|
-
})
|
|
594
|
-
expect(snapshot.prepare('SELECT title FROM todo WHERE id = ?').get('1')).toEqual({
|
|
595
|
-
title: 'old',
|
|
596
|
-
})
|
|
597
|
-
|
|
598
|
-
snapshot.prepare('ROLLBACK').run()
|
|
599
|
-
expect(snapshot.prepare('SELECT title FROM todo WHERE id = ?').get('1')).toEqual({
|
|
600
|
-
title: 'new',
|
|
601
|
-
})
|
|
602
|
-
})
|
|
603
|
-
|
|
604
|
-
it('hides snapshot tables from sqlite catalog queries', () => {
|
|
605
|
-
snapshot.prepare('BEGIN CONCURRENT').run()
|
|
606
|
-
|
|
607
|
-
expect(
|
|
608
|
-
live
|
|
609
|
-
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name")
|
|
610
|
-
.all()
|
|
611
|
-
).toEqual([{ name: 'todo' }])
|
|
612
|
-
})
|
|
613
|
-
|
|
614
|
-
it('does not rewrite table names inside string literals during snapshots', () => {
|
|
615
|
-
live.exec('CREATE TABLE log (id TEXT PRIMARY KEY, table_name TEXT, _0_version TEXT)')
|
|
616
|
-
live.prepare('INSERT INTO log VALUES (?, ?, ?)').run('1', 'todo', '01')
|
|
617
|
-
live.prepare('INSERT INTO log VALUES (?, ?, ?)').run('2', '"todo"', '02')
|
|
618
|
-
|
|
619
|
-
snapshot.prepare('BEGIN CONCURRENT').run()
|
|
620
|
-
|
|
621
|
-
expect(
|
|
622
|
-
snapshot.prepare("SELECT table_name FROM log WHERE table_name = 'todo'").get()
|
|
623
|
-
).toEqual({ table_name: 'todo' })
|
|
624
|
-
expect(
|
|
625
|
-
snapshot.prepare('SELECT table_name FROM log WHERE table_name = ?').get('"todo"')
|
|
626
|
-
).toEqual({ table_name: '"todo"' })
|
|
627
|
-
})
|
|
628
|
-
|
|
629
|
-
it('cleans inactive snapshot tables when opening a database', () => {
|
|
630
|
-
mock.exec('CREATE TABLE _orez_snapshot_1_todo AS SELECT * FROM todo')
|
|
631
|
-
expect(
|
|
632
|
-
mock
|
|
633
|
-
.exec("SELECT name FROM sqlite_master WHERE name = '_orez_snapshot_1_todo'")
|
|
634
|
-
.toArray()
|
|
635
|
-
).toEqual([{ name: '_orez_snapshot_1_todo' }])
|
|
636
|
-
|
|
637
|
-
const reopened = new Database(mock)
|
|
638
|
-
try {
|
|
639
|
-
expect(
|
|
640
|
-
mock
|
|
641
|
-
.exec("SELECT name FROM sqlite_master WHERE name = '_orez_snapshot_1_todo'")
|
|
642
|
-
.toArray()
|
|
643
|
-
).toEqual([])
|
|
644
|
-
} finally {
|
|
645
|
-
reopened.close()
|
|
646
|
-
}
|
|
647
|
-
})
|
|
648
|
-
|
|
649
|
-
it('does not remove another open connection snapshot', () => {
|
|
650
|
-
snapshot.prepare('BEGIN CONCURRENT').run()
|
|
651
|
-
const snapshotTables = mock
|
|
652
|
-
.exec("SELECT name FROM sqlite_master WHERE name LIKE '_orez_snapshot_%'")
|
|
653
|
-
.toArray()
|
|
654
|
-
expect(snapshotTables).toHaveLength(1)
|
|
655
|
-
|
|
656
|
-
const other = new Database(mock)
|
|
657
|
-
try {
|
|
658
|
-
live
|
|
659
|
-
.prepare('UPDATE todo SET title = ?, _0_version = ? WHERE id = ?')
|
|
660
|
-
.run('new', '02', '1')
|
|
661
|
-
|
|
662
|
-
expect(snapshot.prepare('SELECT title FROM todo WHERE id = ?').get('1')).toEqual({
|
|
663
|
-
title: 'old',
|
|
664
|
-
})
|
|
665
|
-
} finally {
|
|
666
|
-
other.close()
|
|
667
|
-
}
|
|
668
|
-
})
|
|
669
|
-
|
|
670
|
-
it('persists BEGIN CONCURRENT writes for the zero-cache replica writer', () => {
|
|
671
|
-
const globalObject = globalThis as any
|
|
672
|
-
const previousRole = globalObject.__orez_zero_sqlite_role
|
|
673
|
-
globalObject.__orez_zero_sqlite_role = 'replica-writer'
|
|
674
|
-
let writer: Database
|
|
675
|
-
try {
|
|
676
|
-
writer = new Database(mock)
|
|
677
|
-
} finally {
|
|
678
|
-
if (previousRole === undefined) {
|
|
679
|
-
delete globalObject.__orez_zero_sqlite_role
|
|
680
|
-
} else {
|
|
681
|
-
globalObject.__orez_zero_sqlite_role = previousRole
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
writer.prepare('BEGIN CONCURRENT').run()
|
|
686
|
-
writer.prepare('INSERT INTO todo VALUES (?, ?, ?)').run('2', 'writer row', '02')
|
|
687
|
-
writer.prepare('COMMIT').run()
|
|
688
|
-
|
|
689
|
-
expect(live.prepare('SELECT title FROM todo WHERE id = ?').get('2')).toEqual({
|
|
690
|
-
title: 'writer row',
|
|
691
|
-
})
|
|
692
|
-
expect(
|
|
693
|
-
live
|
|
694
|
-
.prepare("SELECT name FROM sqlite_master WHERE name LIKE '_orez_snapshot_%'")
|
|
695
|
-
.all()
|
|
696
|
-
).toEqual([])
|
|
697
|
-
})
|
|
698
|
-
})
|
|
699
|
-
|
|
700
|
-
describe('StatementRunner: zero-cache replicator pattern', () => {
|
|
701
|
-
let mock: SqlStorageLike & { _nativeDb: any }
|
|
702
|
-
let db: Database
|
|
703
|
-
let runner: StatementRunner
|
|
704
|
-
|
|
705
|
-
beforeEach(() => {
|
|
706
|
-
mock = createMockSqlStorage() as SqlStorageLike & { _nativeDb: any }
|
|
707
|
-
db = new Database(mock)
|
|
708
|
-
db.exec(`
|
|
709
|
-
CREATE TABLE issues (
|
|
710
|
-
issueID INTEGER PRIMARY KEY,
|
|
711
|
-
title TEXT,
|
|
712
|
-
_0_version TEXT
|
|
713
|
-
)
|
|
714
|
-
`)
|
|
715
|
-
runner = new StatementRunner(db)
|
|
716
|
-
})
|
|
717
|
-
|
|
718
|
-
afterEach(() => {
|
|
719
|
-
mock._nativeDb.close()
|
|
720
|
-
})
|
|
721
|
-
|
|
722
|
-
it('simulates zero-cache batch processing', () => {
|
|
723
|
-
// batch 1
|
|
724
|
-
runner.begin()
|
|
725
|
-
runner.run(
|
|
726
|
-
'INSERT INTO issues (issueID, title, _0_version) VALUES (?, ?, ?)',
|
|
727
|
-
1,
|
|
728
|
-
'bug',
|
|
729
|
-
'01'
|
|
730
|
-
)
|
|
731
|
-
runner.run(
|
|
732
|
-
'INSERT INTO issues (issueID, title, _0_version) VALUES (?, ?, ?)',
|
|
733
|
-
2,
|
|
734
|
-
'feat',
|
|
735
|
-
'01'
|
|
736
|
-
)
|
|
737
|
-
runner.commit()
|
|
738
|
-
|
|
739
|
-
expect(runner.all('SELECT * FROM issues ORDER BY issueID')).toEqual([
|
|
740
|
-
{ issueID: 1, title: 'bug', _0_version: '01' },
|
|
741
|
-
{ issueID: 2, title: 'feat', _0_version: '01' },
|
|
742
|
-
])
|
|
743
|
-
|
|
744
|
-
// batch 2
|
|
745
|
-
runner.begin()
|
|
746
|
-
runner.run(
|
|
747
|
-
'INSERT OR REPLACE INTO issues (issueID, title, _0_version) VALUES (?, ?, ?)',
|
|
748
|
-
1,
|
|
749
|
-
'bug fix',
|
|
750
|
-
'02'
|
|
751
|
-
)
|
|
752
|
-
runner.commit()
|
|
753
|
-
|
|
754
|
-
expect(runner.get('SELECT title FROM issues WHERE issueID = ?', 1)).toEqual({
|
|
755
|
-
title: 'bug fix',
|
|
756
|
-
})
|
|
757
|
-
})
|
|
758
|
-
|
|
759
|
-
it('simulates rollback on conflict', () => {
|
|
760
|
-
runner.begin()
|
|
761
|
-
runner.run(
|
|
762
|
-
'INSERT INTO issues (issueID, title, _0_version) VALUES (?, ?, ?)',
|
|
763
|
-
1,
|
|
764
|
-
'ok',
|
|
765
|
-
'01'
|
|
766
|
-
)
|
|
767
|
-
runner.commit()
|
|
768
|
-
|
|
769
|
-
runner.begin()
|
|
770
|
-
try {
|
|
771
|
-
runner.run(
|
|
772
|
-
'INSERT INTO issues (issueID, title, _0_version) VALUES (?, ?, ?)',
|
|
773
|
-
1,
|
|
774
|
-
'dupe',
|
|
775
|
-
'02'
|
|
776
|
-
)
|
|
777
|
-
} catch {
|
|
778
|
-
runner.rollback()
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
// original row should still be there
|
|
782
|
-
expect(runner.get('SELECT title FROM issues WHERE issueID = ?', 1)).toEqual({
|
|
783
|
-
title: 'ok',
|
|
784
|
-
})
|
|
785
|
-
})
|
|
786
|
-
})
|