Skip to content
Merged
5 changes: 5 additions & 0 deletions .changeset/hip-poems-repair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@powersync/op-sqlite': patch
---

Allow users to load additional sqlite extensions
34 changes: 34 additions & 0 deletions packages/powersync-op-sqlite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,40 @@ const factory = new OPSqliteOpenFactory({
});
```

### Loading SQLite extensions

To load additional SQLite extensions include the `extensions` option in `sqliteOptions` which expects an array of objects with a `path` and an `entryPoint`:

```js
sqliteOptions: {
extensions: [
{ path: libPath, entryPoint: 'sqlite3_powersync_init' }
]
}
```

More info can be found in the [OP-SQLite docs](https://op-engineering.github.io/op-sqlite/docs/api/#loading-extensions).

Example usage:

```ts
let libPath: string
if (Platform.OS === 'ios') {
libPath = getDylibPath('powersync-sqlite-core', 'powersync-sqlite-core')
} else {
libPath = 'libpowersync';
}

const factory = new OPSqliteOpenFactory({
dbFilename: 'sqlite.db',
sqliteOptions: {
extensions: [
{ path: libPath, entryPoint: 'sqlite3_powersync_init' }
]
}
});
```

## Native Projects

This package uses native libraries. Create native Android and iOS projects (if not created already) by running:
Expand Down
37 changes: 29 additions & 8 deletions packages/powersync-op-sqlite/src/db/OPSqliteAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
import { BaseObserver, DBAdapter, DBAdapterListener, DBLockOptions, QueryResult, Transaction } from '@powersync/common';
import { ANDROID_DATABASE_PATH, IOS_LIBRARY_PATH, open, type DB } from '@op-engineering/op-sqlite';
import {
BaseObserver,
DBAdapter,
DBAdapterListener,
DBLockOptions,
QueryResult,
Transaction
} from '@powersync/common';
import {
ANDROID_DATABASE_PATH,
getDylibPath,
IOS_LIBRARY_PATH,
open,
type DB
} from '@op-engineering/op-sqlite';
import Lock from 'async-lock';
import { OPSQLiteConnection } from './OPSQLiteConnection';
import { NativeModules, Platform } from 'react-native';
import { Platform } from 'react-native';
import { SqliteOptions } from './SqliteOptions';

/**
Expand Down Expand Up @@ -88,8 +101,9 @@ export class OPSQLiteDBAdapter extends BaseObserver<DBAdapterListener> implement
const dbFilename = filenameOverride ?? this.options.name;
const DB: DB = this.openDatabase(dbFilename, this.options.sqliteOptions.encryptionKey);

//Load extension for all connections
this.loadExtension(DB);
//Load extensions for all connections
this.loadAdditionalExtensions(DB);
this.loadPowerSyncExtension(DB);

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

Expand Down Expand Up @@ -124,10 +138,17 @@ export class OPSQLiteDBAdapter extends BaseObserver<DBAdapterListener> implement
}
}

private loadExtension(DB: DB) {
private loadAdditionalExtensions(DB: DB) {
if (this.options.sqliteOptions.extensions.length > 0) {
for (const extension of this.options.sqliteOptions.extensions) {
DB.loadExtension(extension.path, extension.entryPoint);
}
}
}

private loadPowerSyncExtension(DB: DB) {
if (Platform.OS === 'ios') {
const bundlePath: string = NativeModules.PowerSyncOpSqlite.getBundlePath();
const libPath = `${bundlePath}/Frameworks/powersync-sqlite-core.framework/powersync-sqlite-core`;
const libPath = getDylibPath('powersync-sqlite-core', 'powersync-sqlite-core')
DB.loadExtension(libPath, 'sqlite3_powersync_init');
} else {
DB.loadExtension('libpowersync', 'sqlite3_powersync_init');
Expand Down
12 changes: 11 additions & 1 deletion packages/powersync-op-sqlite/src/db/SqliteOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ export interface SqliteOptions {
* If set, the database will be encrypted using SQLCipher.
*/
encryptionKey?: string;

/**
* Load extensions using the path and entryPoint.
* More info can be found here https://op-engineering.github.io/op-sqlite/docs/api#loading-extensions.
*/
extensions?: Array<{
path: string;
entryPoint?: string;
}>;
}

// SQLite journal mode. Set on the primary connection.
Expand Down Expand Up @@ -57,5 +66,6 @@ export const DEFAULT_SQLITE_OPTIONS: Required<SqliteOptions> = {
synchronous: SqliteSynchronous.normal,
journalSizeLimit: 6 * 1024 * 1024,
lockTimeoutMs: 30000,
encryptionKey: null
encryptionKey: null,
extensions: []
};
Loading