Skip to content

Commit 3a37054

Browse files
feat(op-sqlite): allow extensions to be loaded (#469)
1 parent 6580f29 commit 3a37054

File tree

13 files changed

+742
-1794
lines changed

13 files changed

+742
-1794
lines changed

.changeset/hip-poems-repair.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@powersync/op-sqlite': minor
3+
---
4+
5+
* Allow users to load additional sqlite extensions
6+
* Remove `getBundledPath` function as `getDylibPath` can now be used instead

packages/powersync-op-sqlite/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,42 @@ const factory = new OPSqliteOpenFactory({
6868
});
6969
```
7070

71+
### Loading SQLite extensions
72+
73+
To load additional SQLite extensions include the `extensions` option in `sqliteOptions` which expects an array of objects with a `path` and an `entryPoint`:
74+
75+
```js
76+
sqliteOptions: {
77+
extensions: [
78+
{ path: libPath, entryPoint: 'sqlite3_powersync_init' }
79+
]
80+
}
81+
```
82+
83+
More info can be found in the [OP-SQLite docs](https://op-engineering.github.io/op-sqlite/docs/api/#loading-extensions).
84+
85+
Example usage:
86+
87+
```ts
88+
import { getDylibPath } from '@op-engineering/op-sqlite';
89+
90+
let libPath: string
91+
if (Platform.OS === 'ios') {
92+
libPath = getDylibPath('co.powersync.sqlitecore', 'powersync-sqlite-core')
93+
} else {
94+
libPath = 'libpowersync';
95+
}
96+
97+
const factory = new OPSqliteOpenFactory({
98+
dbFilename: 'sqlite.db',
99+
sqliteOptions: {
100+
extensions: [
101+
{ path: libPath, entryPoint: 'sqlite3_powersync_init' }
102+
]
103+
}
104+
});
105+
```
106+
71107
## Native Projects
72108

73109
This package uses native libraries. Create native Android and iOS projects (if not created already) by running:

packages/powersync-op-sqlite/android/src/main/java/com/powersync/opsqlite/PowerSyncOpSqliteModule.kt

Lines changed: 0 additions & 25 deletions
This file was deleted.

packages/powersync-op-sqlite/android/src/main/java/com/powersync/opsqlite/PowerSyncOpSqlitePackage.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,16 @@ import java.util.HashMap
99

1010
class PowerSyncOpSqlitePackage : TurboReactPackage() {
1111
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12-
return if (name == PowerSyncOpSqliteModule.NAME) {
13-
PowerSyncOpSqliteModule(reactContext)
14-
} else {
15-
null
16-
}
12+
return null
1713
}
1814

1915
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
2016
return ReactModuleInfoProvider {
2117
val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
2218
val isTurboModule: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
23-
moduleInfos[PowerSyncOpSqliteModule.NAME] = ReactModuleInfo(
24-
PowerSyncOpSqliteModule.NAME,
25-
PowerSyncOpSqliteModule.NAME,
19+
moduleInfos["PowerSyncOpSqlite"] = ReactModuleInfo(
20+
"PowerSyncOpSqlite",
21+
"PowerSyncOpSqlite",
2622
false, // canOverrideExistingModule
2723
false, // needsEagerInit
2824
true, // hasConstants

packages/powersync-op-sqlite/android/src/newarch/PowerSyncOpSqliteSpec.kt

Lines changed: 0 additions & 7 deletions
This file was deleted.

packages/powersync-op-sqlite/android/src/oldarch/PowerSyncOpSqliteSpec.kt

Lines changed: 0 additions & 11 deletions
This file was deleted.

packages/powersync-op-sqlite/ios/PowerSyncOpSqlite.mm

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,4 @@
33
@implementation PowerSyncOpSqlite
44
RCT_EXPORT_MODULE()
55

6-
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getBundlePath)
7-
{
8-
return [NSBundle mainBundle].bundlePath;
9-
}
10-
116
@end

packages/powersync-op-sqlite/src/NativePowerSyncOpSqlite.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.

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

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
1-
import { BaseObserver, DBAdapter, DBAdapterListener, DBLockOptions, QueryResult, Transaction } from '@powersync/common';
2-
import { ANDROID_DATABASE_PATH, IOS_LIBRARY_PATH, open, type DB } from '@op-engineering/op-sqlite';
1+
import {
2+
BaseObserver,
3+
DBAdapter,
4+
DBAdapterListener,
5+
DBLockOptions,
6+
QueryResult,
7+
Transaction
8+
} from '@powersync/common';
9+
import {
10+
ANDROID_DATABASE_PATH,
11+
getDylibPath,
12+
IOS_LIBRARY_PATH,
13+
open,
14+
type DB
15+
} from '@op-engineering/op-sqlite';
316
import Lock from 'async-lock';
417
import { OPSQLiteConnection } from './OPSQLiteConnection';
5-
import { NativeModules, Platform } from 'react-native';
18+
import { Platform } from 'react-native';
619
import { SqliteOptions } from './SqliteOptions';
720

821
/**
@@ -44,7 +57,7 @@ export class OPSQLiteDBAdapter extends BaseObserver<DBAdapterListener> implement
4457
}
4558

4659
protected async init() {
47-
const { lockTimeoutMs, journalMode, journalSizeLimit, synchronous, encryptionKey } = this.options.sqliteOptions;
60+
const { lockTimeoutMs, journalMode, journalSizeLimit, synchronous } = this.options.sqliteOptions!;
4861
const dbFilename = this.options.name;
4962

5063
this.writeConnection = await this.openConnection(dbFilename);
@@ -86,10 +99,11 @@ export class OPSQLiteDBAdapter extends BaseObserver<DBAdapterListener> implement
8699

87100
protected async openConnection(filenameOverride?: string): Promise<OPSQLiteConnection> {
88101
const dbFilename = filenameOverride ?? this.options.name;
89-
const DB: DB = this.openDatabase(dbFilename, this.options.sqliteOptions.encryptionKey);
102+
const DB: DB = this.openDatabase(dbFilename, this.options.sqliteOptions?.encryptionKey ?? undefined);
90103

91-
//Load extension for all connections
92-
this.loadExtension(DB);
104+
//Load extensions for all connections
105+
this.loadAdditionalExtensions(DB);
106+
this.loadPowerSyncExtension(DB);
93107

94108
await DB.execute('SELECT powersync_init()');
95109

@@ -124,10 +138,17 @@ export class OPSQLiteDBAdapter extends BaseObserver<DBAdapterListener> implement
124138
}
125139
}
126140

127-
private loadExtension(DB: DB) {
141+
private loadAdditionalExtensions(DB: DB) {
142+
if (this.options.sqliteOptions?.extensions && this.options.sqliteOptions.extensions.length > 0) {
143+
for (const extension of this.options.sqliteOptions.extensions) {
144+
DB.loadExtension(extension.path, extension.entryPoint);
145+
}
146+
}
147+
}
148+
149+
private async loadPowerSyncExtension(DB: DB) {
128150
if (Platform.OS === 'ios') {
129-
const bundlePath: string = NativeModules.PowerSyncOpSqlite.getBundlePath();
130-
const libPath = `${bundlePath}/Frameworks/powersync-sqlite-core.framework/powersync-sqlite-core`;
151+
const libPath = getDylibPath('co.powersync.sqlitecore', 'powersync-sqlite-core');
131152
DB.loadExtension(libPath, 'sqlite3_powersync_init');
132153
} else {
133154
DB.loadExtension('libpowersync', 'sqlite3_powersync_init');
@@ -271,8 +292,10 @@ export class OPSQLiteDBAdapter extends BaseObserver<DBAdapterListener> implement
271292
await this.initialized;
272293
await this.writeConnection!.refreshSchema();
273294

274-
for (let readConnection of this.readConnections) {
275-
await readConnection.connection.refreshSchema();
295+
if(this.readConnections) {
296+
for (let readConnection of this.readConnections) {
297+
await readConnection.connection.refreshSchema();
298+
}
276299
}
277300
}
278301
}

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,16 @@ export interface SqliteOptions {
2828
* Encryption key for the database.
2929
* If set, the database will be encrypted using SQLCipher.
3030
*/
31-
encryptionKey?: string;
31+
encryptionKey?: string | null;
32+
33+
/**
34+
* Load extensions using the path and entryPoint.
35+
* More info can be found here https://op-engineering.github.io/op-sqlite/docs/api#loading-extensions.
36+
*/
37+
extensions?: Array<{
38+
path: string;
39+
entryPoint?: string;
40+
}>;
3241
}
3342

3443
// SQLite journal mode. Set on the primary connection.
@@ -57,5 +66,6 @@ export const DEFAULT_SQLITE_OPTIONS: Required<SqliteOptions> = {
5766
synchronous: SqliteSynchronous.normal,
5867
journalSizeLimit: 6 * 1024 * 1024,
5968
lockTimeoutMs: 30000,
60-
encryptionKey: null
69+
encryptionKey: null,
70+
extensions: []
6171
};

0 commit comments

Comments
 (0)