Skip to content

Commit cb3afd3

Browse files
committed
Added executeRaw to @powersync/node.
1 parent 7701fe2 commit cb3afd3

File tree

8 files changed

+138
-2
lines changed

8 files changed

+138
-2
lines changed

packages/node/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
},
4646
"devDependencies": {
4747
"@types/async-lock": "^1.4.0",
48+
"drizzle-orm": "^0.35.2",
49+
"@powersync/drizzle-driver": "workspace:*",
4850
"typescript": "^5.5.3",
4951
"vitest": "^3.0.5"
5052
},

packages/node/src/db/AsyncDatabase.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export interface AsyncDatabaseOpener {
1313

1414
export interface AsyncDatabase {
1515
execute: (query: string, params: any[]) => Promise<ProxiedQueryResult>;
16+
executeRaw: (query: string, params: any[]) => Promise<any[][]>;
1617
executeBatch: (query: string, params: any[][]) => Promise<ProxiedQueryResult>;
1718
close: () => Promise<void>;
1819
// Collect table updates made since the last call to collectCommittedUpdates.

packages/node/src/db/BetterSQLite3DBAdapter.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ export class BetterSQLite3DBAdapter extends BaseObserver<DBAdapterListener> impl
5353
}
5454

5555
const openWorker = async (isWriter: boolean) => {
56-
const worker = new Worker(new URL('./SqliteWorker.js', import.meta.url), {name: isWriter ? `write ${dbFilePath}` : `read ${dbFilePath}`});
56+
const worker = new Worker(new URL('./SqliteWorker.js', import.meta.url), {
57+
name: isWriter ? `write ${dbFilePath}` : `read ${dbFilePath}`
58+
});
5759
const listeners = new WeakMap<EventListenerOrEventListenerObject, (e: any) => void>();
5860

5961
const comlink = Comlink.wrap<AsyncDatabaseOpener>({
@@ -216,6 +218,7 @@ export class BetterSQLite3DBAdapter extends BaseObserver<DBAdapterListener> impl
216218
await connection.execute('BEGIN');
217219
const result = await fn({
218220
execute: (query, params) => connection.execute(query, params),
221+
executeRaw: (query, params) => connection.executeRaw(query, params),
219222
executeBatch: (query, params) => connection.executeBatch(query, params),
220223
get: (query, params) => connection.get(query, params),
221224
getAll: (query, params) => connection.getAll(query, params),
@@ -252,6 +255,10 @@ export class BetterSQLite3DBAdapter extends BaseObserver<DBAdapterListener> impl
252255
return this.writeLock((ctx) => ctx.execute(query, params));
253256
}
254257

258+
executeRaw(query: string, params?: any[] | undefined): Promise<any[][]> {
259+
return this.writeLock((ctx) => ctx.executeRaw(query, params));
260+
}
261+
255262
executeBatch(query: string, params?: any[][]): Promise<QueryResult> {
256263
return this.writeTransaction((ctx) => ctx.executeBatch(query, params));
257264
}

packages/node/src/db/RemoteConnection.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ export class RemoteConnection implements LockContext {
2929
return RemoteConnection.wrapQueryResult(result);
3030
}
3131

32+
async executeRaw(query: string, params?: any[] | undefined): Promise<any[][]> {
33+
return await this.database.executeRaw(query, params ?? []);
34+
}
35+
3236
async getAll<T>(sql: string, parameters?: any[]): Promise<T[]> {
3337
const res = await this.execute(sql, parameters);
3438
return res.rows?._array ?? [];

packages/node/src/db/SqliteWorker.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,17 @@ class BlockingAsyncDatabase implements AsyncDatabase {
6565
}
6666
}
6767

68+
async executeRaw(query: string, params: any[]) {
69+
const stmt = this.db.prepare(query);
70+
71+
if (stmt.reader) {
72+
return stmt.raw().all(params);
73+
} else {
74+
stmt.raw().run(params);
75+
return [];
76+
}
77+
}
78+
6879
async executeBatch(query: string, params: any[][]) {
6980
params = params ?? [];
7081

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
2+
import { eq, relations } from 'drizzle-orm';
3+
4+
import { databaseTest } from './utils';
5+
import { wrapPowerSyncWithDrizzle } from '@powersync/drizzle-driver';
6+
import { PowerSyncDatabase } from '../lib';
7+
import { expect } from 'vitest';
8+
9+
export const drizzleLists = sqliteTable('lists', {
10+
id: text('id'),
11+
name: text('name')
12+
});
13+
14+
export const drizzleTodos = sqliteTable('todos', {
15+
id: text('id'),
16+
content: text('content'),
17+
list_id: text('list_id')
18+
});
19+
20+
export const listsRelations = relations(drizzleLists, ({ one, many }) => ({
21+
todos: many(drizzleTodos)
22+
}));
23+
24+
export const todosRelations = relations(drizzleTodos, ({ one, many }) => ({
25+
list: one(drizzleLists, {
26+
fields: [drizzleTodos.list_id],
27+
references: [drizzleLists.id]
28+
})
29+
}));
30+
31+
export const drizzleSchema = {
32+
lists: drizzleLists,
33+
todos: drizzleTodos,
34+
listsRelations,
35+
todosRelations
36+
};
37+
38+
const setupDrizzle = async (database: PowerSyncDatabase) => {
39+
const db = wrapPowerSyncWithDrizzle(database, {
40+
schema: drizzleSchema
41+
});
42+
43+
await db.insert(drizzleLists).values({ id: '1', name: 'list 1' });
44+
await db.insert(drizzleTodos).values({ id: '33', content: 'Post content', list_id: '1' });
45+
return db;
46+
};
47+
48+
databaseTest('should retrieve a list with todos', async ({ database }) => {
49+
const db = await setupDrizzle(database);
50+
51+
const result = await db.query.lists.findMany({ with: { todos: true } });
52+
53+
expect(result).toEqual([{ id: '1', name: 'list 1', todos: [{ id: '33', content: 'Post content', list_id: '1' }] }]);
54+
});
55+
56+
databaseTest('should retrieve a todo with its list', async ({ database }) => {
57+
const db = await setupDrizzle(database);
58+
59+
const result = await db.query.todos.findMany({ with: { list: true } });
60+
61+
expect(result).toEqual([
62+
{
63+
id: '33',
64+
content: 'Post content',
65+
list_id: '1',
66+
list: { id: '1', name: 'list 1' }
67+
}
68+
]);
69+
});
70+
71+
databaseTest('should return a list and todos using leftJoin', async ({ database }) => {
72+
const db = await setupDrizzle(database);
73+
74+
const result = await db.select().from(drizzleLists).leftJoin(drizzleTodos, eq(drizzleLists.id, drizzleTodos.list_id));
75+
76+
expect(result[0].lists).toEqual({ id: '1', name: 'list 1' });
77+
expect(result[0].todos).toEqual({ id: '33', content: 'Post content', list_id: '1' });
78+
});
79+
80+
databaseTest('should return a list and todos using rightJoin', async ({ database }) => {
81+
const db = await setupDrizzle(database);
82+
83+
const result = await db
84+
.select()
85+
.from(drizzleLists)
86+
.rightJoin(drizzleTodos, eq(drizzleLists.id, drizzleTodos.list_id));
87+
88+
expect(result[0].lists).toEqual({ id: '1', name: 'list 1' });
89+
expect(result[0].todos).toEqual({ id: '33', content: 'Post content', list_id: '1' });
90+
});
91+
92+
databaseTest('should return a list and todos using fullJoin', async ({ database }) => {
93+
const db = await setupDrizzle(database);
94+
95+
const result = await db.select().from(drizzleLists).fullJoin(drizzleTodos, eq(drizzleLists.id, drizzleTodos.list_id));
96+
97+
expect(result[0].lists).toEqual({ id: '1', name: 'list 1' });
98+
expect(result[0].todos).toEqual({ id: '33', content: 'Post content', list_id: '1' });
99+
});

packages/node/tests/utils.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@ async function createTempDir() {
1313
export const LIST_TABLE = 'lists';
1414
export const TODO_TABLE = 'todos';
1515

16+
const lists = new Table({
17+
name: column.text
18+
});
19+
1620
const todos = new Table({
17-
content: column.text
21+
content: column.text,
22+
list_id: column.text
1823
});
1924

2025
export const AppSchema = new Schema({
26+
lists,
2127
todos
2228
});
2329

pnpm-lock.yaml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)