Skip to content

Commit 7908412

Browse files
feat: export rowsRead and rowsWritten from execute (#126)
This change exports `rowsRead` and `rowsWritten` from the `execute` function for both the D1 and Durable Objects query builders. For D1, `rows_read` and `rows_written` are extracted from the `meta` object in the response. For Durable Objects, `rowsRead` and `rowsWritten` are read from the `SqlStorageCursor`. The interfaces for the result types have been updated to include these new fields, and integration tests have been added to verify the changes. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent b211ea0 commit 7908412

File tree

5 files changed

+211
-57
lines changed

5 files changed

+211
-57
lines changed

src/databases/d1.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@ export class D1QB extends QueryBuilder<D1Result> {
2626
if (query.fetchType === FetchTypes.ONE || query.fetchType === FetchTypes.ALL) {
2727
const resp = await stmt.all()
2828

29+
const meta = resp.meta as any
2930
return {
30-
changes: resp.meta?.changes,
31-
duration: resp.meta?.duration,
32-
last_row_id: resp.meta?.last_row_id,
33-
served_by: resp.meta?.served_by,
31+
changes: meta?.changes,
32+
duration: meta?.duration,
33+
last_row_id: meta?.last_row_id,
34+
served_by: meta?.served_by,
35+
rowsRead: meta?.rows_read,
36+
rowsWritten: meta?.rows_written,
3437
meta: resp.meta,
3538
success: resp.success,
3639
results: query.fetchType === FetchTypes.ONE ? resp.results[0] : resp.results,
@@ -63,6 +66,8 @@ export class D1QB extends QueryBuilder<D1Result> {
6366
changes: any
6467
last_row_id: any
6568
served_by: any
69+
rows_read: any
70+
rows_written: any
6671
}
6772
},
6873
i: number
@@ -73,6 +78,8 @@ export class D1QB extends QueryBuilder<D1Result> {
7378
duration: resp.meta?.duration,
7479
last_row_id: resp.meta?.last_row_id,
7580
served_by: resp.meta?.served_by,
81+
rowsRead: resp.meta?.rows_read,
82+
rowsWritten: resp.meta?.rows_written,
7683
meta: resp.meta,
7784
success: resp.success,
7885
results: queryArray[i]?.fetchType === FetchTypes.ONE ? resp.results?.[0] : resp.results,
@@ -83,6 +90,8 @@ export class D1QB extends QueryBuilder<D1Result> {
8390
duration: resp.meta?.duration,
8491
last_row_id: resp.meta?.last_row_id,
8592
served_by: resp.meta?.served_by,
93+
rowsRead: resp.meta?.rows_read,
94+
rowsWritten: resp.meta?.rows_written,
8695
meta: resp.meta,
8796
success: resp.success,
8897
}

src/databases/do.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { QueryBuilder } from '../builder'
22
import { FetchTypes } from '../enums'
3-
import { QueryBuilderOptions } from '../interfaces'
3+
import { DOResult, QueryBuilderOptions } from '../interfaces'
44
import { syncLoggerWrapper } from '../logger'
55
import { MigrationOptions, syncMigrationsBuilder } from '../migrations'
66
import { Query } from '../tools'
@@ -12,7 +12,7 @@ export interface SqlStorage {
1212
Statement: any
1313
}
1414

15-
export class DOQB extends QueryBuilder<{}, false> {
15+
export class DOQB extends QueryBuilder<DOResult, false> {
1616
public db: SqlStorage
1717
loggerWrapper = syncLoggerWrapper
1818

@@ -22,7 +22,7 @@ export class DOQB extends QueryBuilder<{}, false> {
2222
}
2323

2424
migrations(options: MigrationOptions) {
25-
return new syncMigrationsBuilder<{}>(options, this)
25+
return new syncMigrationsBuilder<DOResult>(options, this)
2626
}
2727

2828
execute(query: Query<any, false>) {
@@ -35,15 +35,21 @@ export class DOQB extends QueryBuilder<{}, false> {
3535
}
3636

3737
const result = cursor.toArray()
38+
const rowsRead = cursor.rowsRead
39+
const rowsWritten = cursor.rowsWritten
3840

3941
if (query.fetchType == FetchTypes.ONE) {
4042
return {
4143
results: result.length > 0 ? result[0] : undefined,
44+
rowsRead,
45+
rowsWritten,
4246
}
4347
}
4448

4549
return {
4650
results: result,
51+
rowsRead,
52+
rowsWritten,
4753
}
4854
})
4955
}

src/interfaces.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,18 @@ export type D1Result = {
137137
duration: number
138138
last_row_id?: string | number
139139
served_by: string
140+
rowsRead?: number
141+
rowsWritten?: number
140142

141143
meta?: D1Meta
142144
success: boolean
143145
}
144146

147+
export type DOResult = {
148+
rowsRead: number
149+
rowsWritten: number
150+
}
151+
145152
export type PGResult = {
146153
command: string
147154
lastRowId?: string | number

tests/integration/crud-do.test.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { env, runInDurableObject } from 'cloudflare:test'
2+
import { describe, expect, it } from 'vitest'
3+
import { DOQB } from '../../src'
4+
5+
describe('Simple operations', () => {
6+
it('all operations', async () => {
7+
const id = env.TEST_DO.idFromName('test-crud')
8+
const stub = env.TEST_DO.get(id)
9+
10+
await runInDurableObject(stub, (instance, state) => {
11+
const qb = new DOQB(state.storage.sql)
12+
13+
qb.createTable({
14+
tableName: 'testTable',
15+
schema: `
16+
id INTEGER PRIMARY KEY AUTOINCREMENT,
17+
name TEXT NOT NULL
18+
`,
19+
ifNotExists: true,
20+
}).execute()
21+
22+
const insertResult = qb
23+
.insert({
24+
tableName: 'testTable',
25+
data: [
26+
{
27+
name: 'example',
28+
},
29+
{
30+
name: 'test2',
31+
},
32+
],
33+
returning: '*',
34+
})
35+
.execute()
36+
expect(insertResult.results).toEqual([
37+
{
38+
id: 1,
39+
name: 'example',
40+
},
41+
{
42+
id: 2,
43+
name: 'test2',
44+
},
45+
])
46+
expect(insertResult.rowsRead).toBeGreaterThanOrEqual(0)
47+
expect(insertResult.rowsWritten).toBeGreaterThanOrEqual(2)
48+
49+
const selectResult = qb.select('testTable').all()
50+
expect(selectResult.results).toEqual([
51+
{
52+
id: 1,
53+
name: 'example',
54+
},
55+
{
56+
id: 2,
57+
name: 'test2',
58+
},
59+
])
60+
expect(selectResult.rowsRead).toBeGreaterThanOrEqual(2)
61+
expect(selectResult.rowsWritten).toBeGreaterThanOrEqual(0)
62+
63+
const updateResult = qb
64+
.update({
65+
tableName: 'testTable',
66+
where: {
67+
conditions: 'name = ?1',
68+
params: ['example'],
69+
},
70+
data: {
71+
name: 'newName',
72+
},
73+
returning: '*',
74+
})
75+
.execute()
76+
expect(updateResult.results).toEqual([
77+
{
78+
id: 1,
79+
name: 'newName',
80+
},
81+
])
82+
// DO reads the row before updating it
83+
expect(updateResult.rowsRead).toBeGreaterThanOrEqual(1)
84+
expect(updateResult.rowsWritten).toBeGreaterThanOrEqual(1)
85+
86+
const deleteResult = qb
87+
.delete({
88+
tableName: 'testTable',
89+
where: {
90+
conditions: 'name = ?1',
91+
params: ['test2'],
92+
},
93+
returning: '*',
94+
})
95+
.execute()
96+
expect(deleteResult.results).toEqual([
97+
{
98+
id: 2,
99+
name: 'test2',
100+
},
101+
])
102+
expect(deleteResult.rowsRead).toBeGreaterThanOrEqual(0)
103+
expect(deleteResult.rowsWritten).toBeGreaterThanOrEqual(1)
104+
105+
expect(
106+
qb
107+
.delete({
108+
tableName: 'testTable',
109+
where: {
110+
conditions: 'name = ?1',
111+
params: ['abc'],
112+
},
113+
returning: '*',
114+
})
115+
.execute().results
116+
).toEqual([])
117+
118+
expect(qb.select('testTable').all().results).toEqual([
119+
{
120+
id: 1,
121+
name: 'newName',
122+
},
123+
])
124+
125+
qb.dropTable({
126+
tableName: 'testTable',
127+
}).execute()
128+
})
129+
})
130+
})

tests/integration/crud.test.ts

Lines changed: 52 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,25 @@ describe('Simple operations', () => {
1313
id INTEGER PRIMARY KEY AUTOINCREMENT,
1414
name TEXT NOT NULL
1515
`,
16+
ifNotExists: true,
1617
})
1718
.execute()
1819

19-
expect(
20-
(
21-
await qb
22-
.insert({
23-
tableName: 'testTable',
24-
data: [
25-
{
26-
name: 'example',
27-
},
28-
{
29-
name: 'test2',
30-
},
31-
],
32-
returning: '*',
33-
})
34-
.execute()
35-
).results
36-
).toEqual([
20+
const insertResult = await qb
21+
.insert({
22+
tableName: 'testTable',
23+
data: [
24+
{
25+
name: 'example',
26+
},
27+
{
28+
name: 'test2',
29+
},
30+
],
31+
returning: '*',
32+
})
33+
.execute()
34+
expect(insertResult.results).toEqual([
3735
{
3836
id: 1,
3937
name: 'example',
@@ -43,8 +41,11 @@ describe('Simple operations', () => {
4341
name: 'test2',
4442
},
4543
])
44+
expect(insertResult.rowsRead).toBeGreaterThanOrEqual(0)
45+
expect(insertResult.rowsWritten).toBeGreaterThanOrEqual(2)
4646

47-
expect((await qb.select('testTable').all()).results).toEqual([
47+
const selectResult = await qb.select('testTable').all()
48+
expect(selectResult.results).toEqual([
4849
{
4950
id: 1,
5051
name: 'example',
@@ -54,49 +55,50 @@ describe('Simple operations', () => {
5455
name: 'test2',
5556
},
5657
])
58+
expect(selectResult.rowsRead).toBeGreaterThanOrEqual(2)
59+
expect(selectResult.rowsWritten).toBeGreaterThanOrEqual(0)
5760

58-
expect(
59-
(
60-
await qb
61-
.update({
62-
tableName: 'testTable',
63-
where: {
64-
conditions: 'name = ?1',
65-
params: ['example'],
66-
},
67-
data: {
68-
name: 'newName',
69-
},
70-
returning: '*',
71-
})
72-
.execute()
73-
).results
74-
).toEqual([
61+
const updateResult = await qb
62+
.update({
63+
tableName: 'testTable',
64+
where: {
65+
conditions: 'name = ?1',
66+
params: ['example'],
67+
},
68+
data: {
69+
name: 'newName',
70+
},
71+
returning: '*',
72+
})
73+
.execute()
74+
expect(updateResult.results).toEqual([
7575
{
7676
id: 1,
7777
name: 'newName',
7878
},
7979
])
80+
// D1 reads the row before updating it
81+
expect(updateResult.rowsRead).toBeGreaterThanOrEqual(1)
82+
expect(updateResult.rowsWritten).toBeGreaterThanOrEqual(1)
8083

81-
expect(
82-
(
83-
await qb
84-
.delete({
85-
tableName: 'testTable',
86-
where: {
87-
conditions: 'name = ?1',
88-
params: ['test2'],
89-
},
90-
returning: '*',
91-
})
92-
.execute()
93-
).results
94-
).toEqual([
84+
const deleteResult = await qb
85+
.delete({
86+
tableName: 'testTable',
87+
where: {
88+
conditions: 'name = ?1',
89+
params: ['test2'],
90+
},
91+
returning: '*',
92+
})
93+
.execute()
94+
expect(deleteResult.results).toEqual([
9595
{
9696
id: 2,
9797
name: 'test2',
9898
},
9999
])
100+
expect(deleteResult.rowsRead).toBeGreaterThanOrEqual(0)
101+
expect(deleteResult.rowsWritten).toBeGreaterThanOrEqual(1)
100102

101103
expect(
102104
(

0 commit comments

Comments
 (0)