1- import { LockContext , QueryResult } from '@powersync/common' ;
2- import { Column , DriverValueDecoder , getTableName , SQL } from 'drizzle-orm' ;
1+ import type { LockContext , QueryResult } from '@powersync/common' ;
2+ import { Column , DriverValueDecoder , SQL , getTableName } from 'drizzle-orm' ;
3+ import type { Cache } from 'drizzle-orm/cache/core' ;
4+ import type { WithCacheConfig } from 'drizzle-orm/cache/core/types' ;
35import { entityKind , is } from 'drizzle-orm/entity' ;
46import type { Logger } from 'drizzle-orm/logger' ;
57import { fillPlaceholders , type Query } from 'drizzle-orm/sql/sql' ;
68import { SQLiteColumn } from 'drizzle-orm/sqlite-core' ;
79import type { SelectedFieldsOrdered } from 'drizzle-orm/sqlite-core/query-builders/select.types' ;
810import {
11+ SQLitePreparedQuery ,
912 type PreparedQueryConfig as PreparedQueryConfigBase ,
10- type SQLiteExecuteMethod ,
11- SQLitePreparedQuery
13+ type SQLiteExecuteMethod
1214} from 'drizzle-orm/sqlite-core/session' ;
1315
1416type PreparedQueryConfig = Omit < PreparedQueryConfigBase , 'statement' | 'run' > ;
1517
18+ /**
19+ * Callback which uses a LockContext for database operations.
20+ */
21+ export type LockCallback < T > = ( ctx : LockContext ) => Promise < T > ;
22+
23+ /**
24+ * Provider for specific database contexts.
25+ * Handlers are provided a context to the provided callback.
26+ * This does not necessarily need to acquire a database lock for each call.
27+ * Calls might use the same lock context for multiple operations.
28+ * The read/write context may relate to a single read OR write context.
29+ */
30+ export type ContextProvider = {
31+ useReadContext : < T > ( fn : LockCallback < T > ) => Promise < T > ;
32+ useWriteContext : < T > ( fn : LockCallback < T > ) => Promise < T > ;
33+ } ;
34+
1635export class PowerSyncSQLitePreparedQuery <
1736 T extends PreparedQueryConfig = PreparedQueryConfig
1837> extends SQLitePreparedQuery < {
@@ -25,36 +44,48 @@ export class PowerSyncSQLitePreparedQuery<
2544} > {
2645 static readonly [ entityKind ] : string = 'PowerSyncSQLitePreparedQuery' ;
2746
47+ private readOnly = false ;
48+
2849 constructor (
29- private db : LockContext ,
50+ private contextProvider : ContextProvider ,
3051 query : Query ,
3152 private logger : Logger ,
3253 private fields : SelectedFieldsOrdered | undefined ,
3354 executeMethod : SQLiteExecuteMethod ,
3455 private _isResponseInArrayMode : boolean ,
35- private customResultMapper ?: ( rows : unknown [ ] [ ] ) => unknown
56+ private customResultMapper ?: ( rows : unknown [ ] [ ] ) => unknown ,
57+ cache ?: Cache | undefined ,
58+ queryMetadata ?:
59+ | {
60+ type : 'select' | 'update' | 'delete' | 'insert' ;
61+ tables : string [ ] ;
62+ }
63+ | undefined ,
64+ cacheConfig ?: WithCacheConfig | undefined
3665 ) {
37- super ( 'async' , executeMethod , query ) ;
66+ super ( 'async' , executeMethod , query , cache , queryMetadata , cacheConfig ) ;
67+ this . readOnly = queryMetadata ?. type == 'select' ;
3868 }
3969
4070 async run ( placeholderValues ?: Record < string , unknown > ) : Promise < QueryResult > {
4171 const params = fillPlaceholders ( this . query . params , placeholderValues ?? { } ) ;
4272 this . logger . logQuery ( this . query . sql , params ) ;
43- const rs = await this . db . execute ( this . query . sql , params ) ;
44- return rs ;
73+ return this . useContext ( async ( ctx ) => {
74+ return await ctx . execute ( this . query . sql , params ) ;
75+ } ) ;
4576 }
4677
4778 async all ( placeholderValues ?: Record < string , unknown > ) : Promise < T [ 'all' ] > {
4879 const { fields, query, logger, customResultMapper } = this ;
4980 if ( ! fields && ! customResultMapper ) {
5081 const params = fillPlaceholders ( query . params , placeholderValues ?? { } ) ;
5182 logger . logQuery ( query . sql , params ) ;
52- const rs = await this . db . execute ( this . query . sql , params ) ;
53- return rs . rows ?. _array ?? [ ] ;
83+ return await this . useContext ( async ( ctx ) => {
84+ return await ctx . getAll ( this . query . sql , params ) ;
85+ } ) ;
5486 }
5587
5688 const rows = ( await this . values ( placeholderValues ) ) as unknown [ ] [ ] ;
57-
5889 if ( customResultMapper ) {
5990 const mapped = customResultMapper ( rows ) as T [ 'all' ] ;
6091 return mapped ;
@@ -69,7 +100,9 @@ export class PowerSyncSQLitePreparedQuery<
69100 const { fields, customResultMapper } = this ;
70101 const joinsNotNullableMap = ( this as any ) . joinsNotNullableMap ;
71102 if ( ! fields && ! customResultMapper ) {
72- return this . db . get ( this . query . sql , params ) ;
103+ return this . useContext ( async ( ctx ) => {
104+ return await ctx . get ( this . query . sql , params ) ;
105+ } ) ;
73106 }
74107
75108 const rows = ( await this . values ( placeholderValues ) ) as unknown [ ] [ ] ;
@@ -90,12 +123,22 @@ export class PowerSyncSQLitePreparedQuery<
90123 const params = fillPlaceholders ( this . query . params , placeholderValues ?? { } ) ;
91124 this . logger . logQuery ( this . query . sql , params ) ;
92125
93- return await this . db . executeRaw ( this . query . sql , params ) ;
126+ return await this . useContext ( async ( ctx ) => {
127+ return await ctx . executeRaw ( this . query . sql , params ) ;
128+ } ) ;
94129 }
95130
96131 isResponseInArrayMode ( ) : boolean {
97132 return this . _isResponseInArrayMode ;
98133 }
134+
135+ protected useContext < T > ( callback : LockCallback < T > ) : Promise < T > {
136+ if ( this . readOnly ) {
137+ return this . contextProvider . useReadContext ( callback ) ;
138+ } else {
139+ return this . contextProvider . useWriteContext ( callback ) ;
140+ }
141+ }
99142}
100143
101144/**
0 commit comments