Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/hip-poems-repair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@powersync/op-sqlite': minor
---

* Allow users to load additional sqlite extensions
* Remove `getBundledPath` function as `getDylibPath` can now be used instead
36 changes: 36 additions & 0 deletions packages/powersync-op-sqlite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,42 @@ 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
import { getDylibPath } from '@op-engineering/op-sqlite';

let libPath: string
if (Platform.OS === 'ios') {
libPath = getDylibPath('co.powersync.sqlitecore', '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

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,16 @@ import java.util.HashMap

class PowerSyncOpSqlitePackage : TurboReactPackage() {
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
return if (name == PowerSyncOpSqliteModule.NAME) {
PowerSyncOpSqliteModule(reactContext)
} else {
null
}
return null
}

override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
return ReactModuleInfoProvider {
val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
val isTurboModule: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
moduleInfos[PowerSyncOpSqliteModule.NAME] = ReactModuleInfo(
PowerSyncOpSqliteModule.NAME,
PowerSyncOpSqliteModule.NAME,
moduleInfos["PowerSyncOpSqlite"] = ReactModuleInfo(
"PowerSyncOpSqlite",
"PowerSyncOpSqlite",
false, // canOverrideExistingModule
false, // needsEagerInit
true, // hasConstants
Expand Down

This file was deleted.

This file was deleted.

5 changes: 0 additions & 5 deletions packages/powersync-op-sqlite/ios/PowerSyncOpSqlite.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,4 @@
@implementation PowerSyncOpSqlite
RCT_EXPORT_MODULE()

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getBundlePath)
{
return [NSBundle mainBundle].bundlePath;
}

@end
8 changes: 0 additions & 8 deletions packages/powersync-op-sqlite/src/NativePowerSyncOpSqlite.ts

This file was deleted.

47 changes: 35 additions & 12 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 @@ -44,7 +57,7 @@ export class OPSQLiteDBAdapter extends BaseObserver<DBAdapterListener> implement
}

protected async init() {
const { lockTimeoutMs, journalMode, journalSizeLimit, synchronous, encryptionKey } = this.options.sqliteOptions;
const { lockTimeoutMs, journalMode, journalSizeLimit, synchronous } = this.options.sqliteOptions!;
const dbFilename = this.options.name;

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

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

//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 && this.options.sqliteOptions.extensions.length > 0) {
for (const extension of this.options.sqliteOptions.extensions) {
DB.loadExtension(extension.path, extension.entryPoint);
}
}
}

private async 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('co.powersync.sqlitecore', 'powersync-sqlite-core');
DB.loadExtension(libPath, 'sqlite3_powersync_init');
} else {
DB.loadExtension('libpowersync', 'sqlite3_powersync_init');
Expand Down Expand Up @@ -271,8 +292,10 @@ export class OPSQLiteDBAdapter extends BaseObserver<DBAdapterListener> implement
await this.initialized;
await this.writeConnection!.refreshSchema();

for (let readConnection of this.readConnections) {
await readConnection.connection.refreshSchema();
if(this.readConnections) {
for (let readConnection of this.readConnections) {
await readConnection.connection.refreshSchema();
}
}
}
}
14 changes: 12 additions & 2 deletions packages/powersync-op-sqlite/src/db/SqliteOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,16 @@ export interface SqliteOptions {
* Encryption key for the database.
* If set, the database will be encrypted using SQLCipher.
*/
encryptionKey?: string;
encryptionKey?: string | null;

/**
* 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: []
};
34 changes: 4 additions & 30 deletions packages/powersync-op-sqlite/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,4 @@
import { NativeModules, Platform } from 'react-native';

const LINKING_ERROR =
`The package '@powersync/op-sqlite' doesn't seem to be linked. Make sure: \n\n` +
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
'- You rebuilt the app after installing the package\n' +
'- You are not using Expo Go\n';

const isTurboModuleEnabled = global.__turboModuleProxy != null;

const PowerSyncOpSqliteModule = isTurboModuleEnabled
? require('./NativePowerSyncOpSqlite').default
: NativeModules.PowerSyncOpSqlite;

const PowerSyncOpSqlite = PowerSyncOpSqliteModule
? PowerSyncOpSqliteModule
: new Proxy(
{},
{
get() {
throw new Error(LINKING_ERROR);
}
}
);

export function getBundlePath(): string {
return PowerSyncOpSqlite.getBundlePath();
}

export { OPSqliteOpenFactory, OPSQLiteOpenFactoryOptions } from './db/OPSqliteDBOpenFactory';
export {
OPSqliteOpenFactory,
OPSQLiteOpenFactoryOptions
} from './db/OPSqliteDBOpenFactory';
1 change: 1 addition & 0 deletions packages/powersync-op-sqlite/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"noEmit": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"strict": true,
"esModuleInterop": true
},
"references": [
Expand Down
Loading
Loading