Skip to content

Commit a3dff11

Browse files
committed
Example executeRaw.
1 parent 1cc8a94 commit a3dff11

File tree

11 files changed

+256
-74
lines changed

11 files changed

+256
-74
lines changed

packages/common/src/client/AbstractPowerSyncDatabase.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,11 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
603603
return this.database.execute(sql, parameters);
604604
}
605605

606+
async executeRaw(sql: string, parameters?: any[]) {
607+
await this.waitForReady();
608+
return this.database.executeRaw(sql, parameters);
609+
}
610+
606611
/**
607612
* Execute a write query (INSERT/UPDATE/DELETE) multiple times with each parameter set
608613
* and optionally return results.

packages/common/src/db/DBAdapter.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export interface DBGetUtils {
4444
export interface LockContext extends DBGetUtils {
4545
/** Execute a single write statement. */
4646
execute: (query: string, params?: any[] | undefined) => Promise<QueryResult>;
47+
executeRaw: (query: string, params?: any[] | undefined) => Promise<any[][]>;
4748
}
4849

4950
export interface Transaction extends LockContext {
@@ -95,6 +96,7 @@ export interface DBLockOptions {
9596
export interface DBAdapter extends BaseObserverInterface<DBAdapterListener>, DBGetUtils {
9697
close: () => void;
9798
execute: (query: string, params?: any[]) => Promise<QueryResult>;
99+
executeRaw: (query: string, params?: any[]) => Promise<any[][]>;
98100
executeBatch: (query: string, params?: any[][]) => Promise<QueryResult>;
99101
name: string;
100102
readLock: <T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions) => Promise<T>;
@@ -103,7 +105,7 @@ export interface DBAdapter extends BaseObserverInterface<DBAdapterListener>, DBG
103105
writeTransaction: <T>(fn: (tx: Transaction) => Promise<T>, options?: DBLockOptions) => Promise<T>;
104106
/**
105107
* This method refreshes the schema information across all connections. This is for advanced use cases, and should generally not be needed.
106-
*/
108+
*/
107109
refreshSchema: () => Promise<void>;
108110
}
109111

packages/drizzle-driver/src/sqlite/PowerSyncSQLitePreparedQuery.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export class PowerSyncSQLitePreparedQuery<
5555

5656
const rows = (await this.values(placeholderValues)) as unknown[][];
5757
const valueRows = rows.map((row) => Object.values(row));
58+
5859
if (customResultMapper) {
5960
const mapped = customResultMapper(valueRows) as T['all'];
6061
return mapped;
@@ -90,8 +91,11 @@ export class PowerSyncSQLitePreparedQuery<
9091
async values(placeholderValues?: Record<string, unknown>): Promise<T['values']> {
9192
const params = fillPlaceholders(this.query.params, placeholderValues ?? {});
9293
this.logger.logQuery(this.query.sql, params);
93-
const rs = await this.db.execute(this.query.sql, params);
94-
return rs.rows?._array ?? [];
94+
// const rs = await this.db.execute(this.query.sql, params);
95+
96+
return await this.db.executeRaw(this.query.sql, params);
97+
98+
// return rs.rows?._array ?? [];
9599
}
96100

97101
isResponseInArrayMode(): boolean {
Lines changed: 132 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,156 @@
1-
import { AbstractPowerSyncDatabase } from '@powersync/common';
2-
import { eq, sql } from 'drizzle-orm';
1+
import { AbstractPowerSyncDatabase, column, Schema, Table } from '@powersync/common';
2+
import { eq, relations, sql } from 'drizzle-orm';
33
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
44
import * as SUT from '../../src/sqlite/PowerSyncSQLiteDatabase';
55
import { DrizzleSchema, drizzleUsers, getDrizzleDb, getPowerSyncDb } from '../setup/db';
6+
import { alias, sqliteTable, text } from 'drizzle-orm/sqlite-core';
7+
import { DrizzleAppSchema, DrizzleTableWithPowerSyncOptions, toPowerSyncTable } from '../../src/utils/schema';
8+
import { PowerSyncDatabase } from '@powersync/web';
69

710
describe('Database operations', () => {
8-
let powerSyncDb: AbstractPowerSyncDatabase;
9-
let db: SUT.PowerSyncSQLiteDatabase<typeof DrizzleSchema>;
11+
// let powerSyncDb: AbstractPowerSyncDatabase;
12+
// let db: SUT.PowerSyncSQLiteDatabase<typeof DrizzleSchema>;
1013

11-
beforeEach(() => {
12-
powerSyncDb = getPowerSyncDb();
13-
db = getDrizzleDb(powerSyncDb);
14+
// beforeEach(() => {
15+
// powerSyncDb = getPowerSyncDb();
16+
// db = getDrizzleDb(powerSyncDb);
17+
// });
18+
19+
// afterEach(async () => {
20+
// await powerSyncDb.disconnectAndClear();
21+
// });
22+
23+
// it('should insert a user and select that user', async () => {
24+
// await db.insert(drizzleUsers).values({ id: '1', name: 'John' });
25+
// const result = await db.select().from(drizzleUsers);
26+
27+
// expect(result.length).toEqual(1);
28+
// });
29+
30+
// it('should insert a user and delete that user', async () => {
31+
// await db.insert(drizzleUsers).values({ id: '2', name: 'Ben' });
32+
// await db.delete(drizzleUsers).where(eq(drizzleUsers.name, 'Ben'));
33+
// const result = await db.select().from(drizzleUsers);
34+
35+
// expect(result.length).toEqual(0);
36+
// });
37+
38+
// it('should insert a user and update that user', async () => {
39+
// await db.insert(drizzleUsers).values({ id: '3', name: 'Lucy' });
40+
// await db.update(drizzleUsers).set({ name: 'Lucy Smith' }).where(eq(drizzleUsers.name, 'Lucy'));
41+
// const result = await db.select().from(drizzleUsers).get();
42+
43+
// expect(result!.name).toEqual('Lucy Smith');
44+
// });
45+
46+
// it('should insert a user and update that user within a transaction', async () => {
47+
// await db.transaction(async (transaction) => {
48+
// await transaction.insert(drizzleUsers).values({ id: '4', name: 'James' });
49+
// await transaction.update(drizzleUsers).set({ name: 'James Smith' }).where(eq(drizzleUsers.name, 'James'));
50+
// });
51+
// const result = await db.select().from(drizzleUsers).get();
52+
53+
// expect(result!.name).toEqual('James Smith');
54+
// });
55+
56+
// it('should insert a user and update that user within a transaction when raw sql is used', async () => {
57+
// await db.transaction(async (transaction) => {
58+
// await transaction.run(sql`INSERT INTO users (id, name) VALUES ('4', 'James');`);
59+
// await transaction.update(drizzleUsers).set({ name: 'James Smith' }).where(eq(drizzleUsers.name, 'James'));
60+
// });
61+
62+
// const result = await db.select().from(drizzleUsers).get();
63+
64+
// expect(result!.name).toEqual('James Smith');
65+
// });
66+
const users = new Table({
67+
name: column.text
1468
});
1569

16-
afterEach(async () => {
17-
await powerSyncDb.disconnectAndClear();
70+
const posts = new Table({
71+
content: column.text,
72+
title: column.text,
73+
xxx: column.text
1874
});
1975

20-
it('should insert a user and select that user', async () => {
21-
await db.insert(drizzleUsers).values({ id: '1', name: 'John' });
22-
const result = await db.select().from(drizzleUsers);
76+
const drizzleUsers = sqliteTable('users', {
77+
id: text('id').primaryKey().notNull(),
78+
name: text('name').notNull()
79+
});
2380

24-
expect(result.length).toEqual(1);
81+
const drizzlePosts = sqliteTable('posts', {
82+
id: text('id').primaryKey().notNull(),
83+
content: text('content').notNull(),
84+
title: text('title').notNull(),
85+
xxx: text('xxx')
86+
.notNull()
87+
.references(() => drizzleUsers.id)
2588
});
2689

27-
it('should insert a user and delete that user', async () => {
28-
await db.insert(drizzleUsers).values({ id: '2', name: 'Ben' });
29-
await db.delete(drizzleUsers).where(eq(drizzleUsers.name, 'Ben'));
30-
const result = await db.select().from(drizzleUsers);
90+
// Define relationships
91+
const usersRelations = relations(drizzleUsers, ({ one, many }) => ({
92+
posts: many(drizzlePosts) // One user has many posts
93+
}));
3194

32-
expect(result.length).toEqual(0);
33-
});
95+
const postsRelations = relations(drizzlePosts, ({ one }) => ({
96+
user: one(drizzleUsers, {
97+
fields: [drizzlePosts.xxx], // Foreign key in posts
98+
references: [drizzleUsers.id] // Primary key in users
99+
})
100+
}));
34101

35-
it('should insert a user and update that user', async () => {
36-
await db.insert(drizzleUsers).values({ id: '3', name: 'Lucy' });
37-
await db.update(drizzleUsers).set({ name: 'Lucy Smith' }).where(eq(drizzleUsers.name, 'Lucy'));
38-
const result = await db.select().from(drizzleUsers).get();
102+
const TestSchema = new Schema({ users, posts });
103+
// const DrizzleSchema = { users: drizzleUsers, posts: drizzlePosts };
104+
const DrizzleSchema = { users: drizzleUsers, posts: drizzlePosts, usersRelations, postsRelations };
39105

40-
expect(result!.name).toEqual('Lucy Smith');
106+
const psDb = new PowerSyncDatabase({
107+
database: {
108+
dbFilename: 'test.db'
109+
},
110+
flags: {
111+
useWebWorker: false
112+
},
113+
schema: TestSchema
41114
});
42115

43-
it('should insert a user and update that user within a transaction', async () => {
44-
await db.transaction(async (transaction) => {
45-
await transaction.insert(drizzleUsers).values({ id: '4', name: 'James' });
46-
await transaction.update(drizzleUsers).set({ name: 'James Smith' }).where(eq(drizzleUsers.name, 'James'));
47-
});
48-
const result = await db.select().from(drizzleUsers).get();
116+
const db = SUT.wrapPowerSyncWithDrizzle(psDb, { schema: DrizzleSchema, logger: { logQuery: () => {} } });
117+
it.only('left join', async () => {
118+
await db.insert(drizzleUsers).values({ id: '3', name: 'Lucy Smith' });
119+
await db.insert(drizzlePosts).values({ id: '1', title: 'Post 1', content: 'Content 1', xxx: '3' });
49120

50-
expect(result!.name).toEqual('James Smith');
51-
});
121+
// const users = await db.select().from(drizzleUsers);
122+
// const posts = await db.select().from(drizzlePosts);
123+
// console.log(users);
124+
// console.log(posts);
125+
126+
// Should fail without relationship definitions
127+
// console.warn(1);
128+
// console.log(db.query.users.findMany({ with: { posts: true } }).toSQL().sql);
129+
// const all = await db.query.users.findMany({ with: { posts: true } });
130+
// console.log(all);
52131

53-
it('should insert a user and update that user within a transaction when raw sql is used', async () => {
54-
await db.transaction(async (transaction) => {
55-
await transaction.run(sql`INSERT INTO users (id, name) VALUES ('4', 'James');`);
56-
await transaction.update(drizzleUsers).set({ name: 'James Smith' }).where(eq(drizzleUsers.name, 'James'));
57-
});
132+
// console.warn(2);
133+
// // const a = await psDb.execute('select * from users');
134+
// // const c = {"1": "Lucy Smith", "1": "3"}
135+
// // console.log('yoink', a);
136+
// // const joined = await db
137+
// // .select()
138+
// // .from(drizzleUsers)
139+
// // .leftJoin(drizzlePosts, eq(drizzlePosts.userId, drizzleUsers.id));
58140

59-
const result = await db.select().from(drizzleUsers).get();
141+
const joined = await db.select().from(drizzleUsers).leftJoin(drizzlePosts, eq(drizzleUsers.id, drizzlePosts.xxx));
60142

61-
expect(result!.name).toEqual('James Smith');
143+
// console.log(
144+
// db
145+
// .select({ id: drizzleUsers.id, postsId: sql`"posts"."id"`.as('postsId') })
146+
// .from(drizzleUsers)
147+
// .leftJoin(drizzlePosts, eq(drizzleUsers.id, drizzlePosts.xxx))
148+
// .toSQL().sql
149+
// );
150+
console.log(joined[0]);
151+
// console.log();
152+
// console.log(
153+
// db.select().from(drizzleUsers).leftJoin(drizzlePosts, eq(drizzleUsers.id, drizzlePosts.xxx)).toSQL().sql
154+
// );
62155
});
63156
});

packages/powersync-op-sqlite/src/db/OPSQLiteConnection.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ export class OPSQLiteConnection extends BaseObserver<DBAdapterListener> {
9797
};
9898
}
9999

100+
async executeRaw(query: string, params?: any[]): Promise<any[][]> {
101+
return await this.DB.executeRaw(query, params);
102+
}
103+
100104
async executeBatch(query: string, params: any[][] = []): Promise<QueryResult> {
101105
const tuple: SQLBatchTuple[] = [[query, params[0]]];
102106
params.slice(1).forEach((p) => tuple.push([query, p]));

packages/powersync-op-sqlite/src/db/OPSqliteAdapter.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ export class OPSQLiteDBAdapter extends BaseObserver<DBAdapterListener> implement
226226
return this.writeLock((ctx) => ctx.execute(query, params));
227227
}
228228

229+
executeRaw(query: string, params?: any[]) {
230+
return this.writeLock((ctx) => ctx.executeRaw(query, params));
231+
}
232+
229233
async executeBatch(query: string, params: any[][] = []): Promise<QueryResult> {
230234
return this.writeLock((ctx) => ctx.executeBatch(query, params));
231235
}
@@ -253,6 +257,7 @@ export class OPSQLiteDBAdapter extends BaseObserver<DBAdapterListener> implement
253257
await connection.execute('BEGIN');
254258
const result = await fn({
255259
execute: (query, params) => connection.execute(query, params),
260+
executeRaw: (query, params) => connection.executeRaw(query, params),
256261
get: (query, params) => connection.get(query, params),
257262
getAll: (query, params) => connection.getAll(query, params),
258263
getOptional: (query, params) => connection.getOptional(query, params),

packages/react-native/src/db/adapters/react-native-quick-sqlite/RNQSDBAdapter.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ export class RNQSDBAdapter extends BaseObserver<DBAdapterListener> implements DB
3030

3131
const topLevelUtils = this.generateDBHelpers({
3232
// Arrow function binds `this` for use in readOnlyExecute
33-
execute: (sql: string, params?: any[]) => this.readOnlyExecute(sql, params)
33+
execute: (sql: string, params?: any[]) => this.readOnlyExecute(sql, params),
34+
executeRaw: (sql, params) => this.executeRaw(sql, params)
3435
});
3536
// Only assigning get helpers
3637
this.getAll = topLevelUtils.getAll;
@@ -62,6 +63,10 @@ export class RNQSDBAdapter extends BaseObserver<DBAdapterListener> implements DB
6263
return this.baseDB.execute(query, params);
6364
}
6465

66+
executeRaw(query: string, params?: any[]) {
67+
return Promise.resolve([]);
68+
}
69+
6570
async executeBatch(query: string, params: any[][] = []): Promise<QueryResult> {
6671
const commands: any[] = [];
6772

@@ -90,9 +95,12 @@ export class RNQSDBAdapter extends BaseObserver<DBAdapterListener> implements DB
9095
* @param tx
9196
* @returns
9297
*/
93-
private generateDBHelpers<T extends { execute: (sql: string, params?: any[]) => Promise<QueryResult> }>(
94-
tx: T
95-
): T & DBGetUtils {
98+
private generateDBHelpers<
99+
T extends {
100+
execute: (sql: string, params?: any[]) => Promise<QueryResult>;
101+
executeRaw: (sql: string, params?: any[]) => Promise<any[][]>;
102+
}
103+
>(tx: T): T & DBGetUtils {
96104
return {
97105
...tx,
98106
/**

packages/web/src/db/adapters/SSRDBAdapter.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ export class SSRDBAdapter extends BaseObserver<DBAdapterListener> implements DBA
5353
return this.writeMutex.runExclusive(async () => MOCK_QUERY_RESPONSE);
5454
}
5555

56+
async executeRaw(query: string, params?: any[]): Promise<any[][]> {
57+
return this.writeMutex.runExclusive(async () => []);
58+
}
59+
5660
async executeBatch(query: string, params?: any[][]): Promise<QueryResult> {
5761
return this.writeMutex.runExclusive(async () => MOCK_QUERY_RESPONSE);
5862
}

0 commit comments

Comments
 (0)