├── dart
├── .gitignore
├── dart_test.yaml
├── README.md
├── pubspec.yaml
├── test
│ ├── goldens
│ │ ├── starting_stream.json
│ │ └── simple_iteration.json
│ ├── update_hooks_test.dart
│ ├── utils
│ │ ├── test_utils.dart
│ │ ├── fix_035_fixtures.dart
│ │ ├── tracking_vfs.dart
│ │ ├── schema.dart
│ │ └── native_test_utils.dart
│ ├── error_test.dart
│ └── js_key_encoding_test.dart
├── benchmark
│ └── apply_lines.dart
└── tool
│ └── download_sqlite3.dart
├── crates
├── sqlite
│ ├── src
│ │ └── main.rs
│ ├── README.md
│ ├── Cargo.toml
│ └── build.rs
├── loadable
│ ├── README.md
│ ├── Cargo.toml
│ └── src
│ │ └── lib.rs
├── static
│ ├── README.md
│ ├── Cargo.toml
│ └── src
│ │ └── lib.rs
├── shell
│ ├── README.md
│ ├── Cargo.toml
│ ├── build.rs
│ └── src
│ │ └── main.rs
├── sqlite_nostd
│ ├── src
│ │ ├── lib.rs
│ │ └── allocator.rs
│ ├── Cargo.toml
│ ├── build.rs
│ └── README.md
└── core
│ ├── src
│ ├── sync
│ │ ├── mod.rs
│ │ ├── checkpoint.rs
│ │ ├── bucket_priority.rs
│ │ ├── subscriptions.rs
│ │ ├── checksum.rs
│ │ └── operations.rs
│ ├── constants.rs
│ ├── schema
│ │ ├── mod.rs
│ │ ├── inspection.rs
│ │ └── table_info.rs
│ ├── ext.rs
│ ├── version.rs
│ ├── uuid.rs
│ ├── checkpoint.rs
│ ├── operations.rs
│ ├── vtab_util.rs
│ ├── kv.rs
│ ├── json_util.rs
│ ├── macros.rs
│ ├── lib.rs
│ ├── bson
│ │ └── error.rs
│ ├── diff.rs
│ ├── operations_vtab.rs
│ ├── state.rs
│ ├── util.rs
│ ├── update_hooks.rs
│ ├── view_admin.rs
│ └── fix_data.rs
│ ├── build.rs
│ ├── README.md
│ └── Cargo.toml
├── NOTICE
├── .gitattributes
├── rust-toolchain.toml
├── android
├── src
│ ├── prefab
│ │ ├── modules
│ │ │ └── powersync
│ │ │ │ ├── module.json
│ │ │ │ ├── libs
│ │ │ │ ├── android.x86
│ │ │ │ │ └── abi.json
│ │ │ │ ├── android.x86_64
│ │ │ │ │ └── abi.json
│ │ │ │ ├── android.arm64-v8a
│ │ │ │ │ └── abi.json
│ │ │ │ └── android.armeabi-v7a
│ │ │ │ │ └── abi.json
│ │ │ │ └── include
│ │ │ │ └── powersync.h
│ │ └── prefab.json
│ └── AndroidManifest.xml
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── .gitignore
├── settings.gradle.kts
├── .gitattributes
└── build.gradle.kts
├── .gitignore
├── wasm
└── asyncify_imports.json
├── Package.swift
├── .github
├── actions
│ ├── macos
│ │ └── action.yml
│ ├── upload
│ │ └── action.yml
│ ├── wasm
│ │ └── action.yml
│ ├── windows
│ │ └── action.yml
│ ├── linux
│ │ └── action.yml
│ ├── xcframework
│ │ └── action.yml
│ └── android
│ │ └── action.yml
└── workflows
│ └── tests.yml
├── tool
├── build_windows.sh
├── build_macos.sh
├── build_linux.sh
└── build_wasm.sh
├── Cargo.toml
├── powersync-sqlite-core.podspec
├── docs
├── RELEASING.md
├── schema.md
└── sync.md
├── README.md
├── UUID.md
└── .cargo
└── config.toml
/dart/.gitignore:
--------------------------------------------------------------------------------
1 | .dart_tool
2 |
--------------------------------------------------------------------------------
/crates/sqlite/src/main.rs:
--------------------------------------------------------------------------------
1 | #![no_main]
2 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright 2023 Journey Mobile, Inc.
2 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | crates/shell/sqlite/* linguist-vendored
2 |
--------------------------------------------------------------------------------
/rust-toolchain.toml:
--------------------------------------------------------------------------------
1 | [toolchain]
2 | channel = "nightly-2025-10-31"
3 |
--------------------------------------------------------------------------------
/dart/dart_test.yaml:
--------------------------------------------------------------------------------
1 | tags:
2 | slow:
3 |
4 | presets:
5 | skip_slow:
6 | exclude_tags: slow
7 |
--------------------------------------------------------------------------------
/android/src/prefab/modules/powersync/module.json:
--------------------------------------------------------------------------------
1 | {
2 | "export_libraries": [],
3 | "android": {}
4 | }
--------------------------------------------------------------------------------
/crates/loadable/README.md:
--------------------------------------------------------------------------------
1 | # powersync_loadable
2 |
3 | Builds the loadable extension as a dynamic library.
4 |
--------------------------------------------------------------------------------
/crates/static/README.md:
--------------------------------------------------------------------------------
1 | Builds the core extension as a static library, exposing the `powersync_init_static` function to load it.
2 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powersync-ja/powersync-sqlite-core/HEAD/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/src/prefab/prefab.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "powersync_sqlite_core",
3 | "schema_version": 2,
4 | "dependencies": [],
5 | "version": "0.4.10"
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .vscode
3 | dist/
4 | *.db
5 | build/
6 | target/
7 | .idea
8 | *.xcframework
9 | *.tar.gz
10 | *.tar.xz
11 | *.zip
12 | .build
13 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore Gradle project-specific cache directory
2 | .gradle
3 |
4 | # Ignore Gradle build output directory
5 | build
6 |
7 | local.properties
8 |
--------------------------------------------------------------------------------
/android/src/prefab/modules/powersync/libs/android.x86/abi.json:
--------------------------------------------------------------------------------
1 | {
2 | "abi": "x86",
3 | "api": 21,
4 | "ndk": 25,
5 | "stl": "none",
6 | "static": false
7 | }
8 |
--------------------------------------------------------------------------------
/android/src/prefab/modules/powersync/libs/android.x86_64/abi.json:
--------------------------------------------------------------------------------
1 | {
2 | "abi": "x86_64",
3 | "api": 21,
4 | "ndk": 25,
5 | "stl": "none",
6 | "static": false
7 | }
8 |
--------------------------------------------------------------------------------
/android/src/prefab/modules/powersync/libs/android.arm64-v8a/abi.json:
--------------------------------------------------------------------------------
1 | {
2 | "abi": "arm64-v8a",
3 | "api": 21,
4 | "ndk": 25,
5 | "stl": "none",
6 | "static": false
7 | }
8 |
--------------------------------------------------------------------------------
/android/src/prefab/modules/powersync/libs/android.armeabi-v7a/abi.json:
--------------------------------------------------------------------------------
1 | {
2 | "abi": "armeabi-v7a",
3 | "api": 21,
4 | "ndk": 25,
5 | "stl": "none",
6 | "static": false
7 | }
8 |
--------------------------------------------------------------------------------
/crates/sqlite/README.md:
--------------------------------------------------------------------------------
1 | # sqlite
2 |
3 | This builds a plain sqlite3 shell.
4 |
5 | We could build this with plain gcc/clang, or download a pre-built binary, but it's simple enough with Rust tooling.
6 |
--------------------------------------------------------------------------------
/android/settings.gradle.kts:
--------------------------------------------------------------------------------
1 |
2 | pluginManagement {
3 | repositories {
4 | gradlePluginPortal()
5 | google()
6 | }
7 | }
8 |
9 | rootProject.name = "powersync-sqlite-core"
10 |
11 |
--------------------------------------------------------------------------------
/android/src/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/crates/shell/README.md:
--------------------------------------------------------------------------------
1 | # powersync-sqlite
2 |
3 | Builds sqlite with powersync extension embedded.
4 |
5 | SQLite itself is built using [build.rs](./build.rs), and linked into the Rust binary.
6 |
7 | The main function is defined in SQLite, so we use `#![no_main]` here.
8 |
--------------------------------------------------------------------------------
/android/.gitattributes:
--------------------------------------------------------------------------------
1 | #
2 | # https://help.github.com/articles/dealing-with-line-endings/
3 | #
4 | # Linux start script should use lf
5 | /gradlew text eol=lf
6 |
7 | # These are Windows script files and should use crlf
8 | *.bat text eol=crlf
9 |
10 |
--------------------------------------------------------------------------------
/android/src/prefab/modules/powersync/include/powersync.h:
--------------------------------------------------------------------------------
1 | #ifndef POWERSYNC_H
2 | #define POWERSYNC_H
3 |
4 | #include "sqlite3.h"
5 |
6 | extern "C" int sqlite3_powersync_init(sqlite3 *db, char **pzErrMsg,
7 | const sqlite3_api_routines *pApi);
8 |
9 | #endif
10 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/dart/README.md:
--------------------------------------------------------------------------------
1 | # Dart Tests
2 |
3 | This folder contains tests written in Dart, as a convenient higher-level language.
4 |
5 | The tests loads the compiled debug library. Before testing, build first using:
6 |
7 | ```sh
8 | cargo build -p powersync_loadable
9 | ```
10 |
11 | Then test here:
12 |
13 | ```sh
14 | dart test
15 | ```
16 |
--------------------------------------------------------------------------------
/wasm/asyncify_imports.json:
--------------------------------------------------------------------------------
1 | [
2 | "sqlite3_close",
3 | "sqlite3_finalize",
4 | "sqlite3_open_v2",
5 | "sqlite3_prepare",
6 | "sqlite3_prepare16",
7 | "sqlite3_prepare_v2",
8 | "sqlite3_prepare16_v2",
9 | "sqlite3_prepare_v3",
10 | "sqlite3_prepare16_v3",
11 | "sqlite3_reset",
12 | "sqlite3_step",
13 | "sqlite3_exec"
14 | ]
15 |
--------------------------------------------------------------------------------
/crates/sqlite/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "sqlite3"
3 | edition.workspace = true
4 | version.workspace = true
5 | homepage.workspace = true
6 | repository.workspace = true
7 | license.workspace = true
8 | authors.workspace = true
9 | keywords.workspace = true
10 |
11 | [dependencies]
12 |
13 | [features]
14 |
15 | [build-dependencies]
16 | cc = "1.0.46"
17 |
--------------------------------------------------------------------------------
/crates/sqlite_nostd/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 | #![allow(non_upper_case_globals)]
3 | #![allow(non_camel_case_types)]
4 | #![allow(non_snake_case)]
5 |
6 | pub mod bindings {
7 | include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
8 | }
9 |
10 | mod allocator;
11 | mod capi;
12 | mod nostd;
13 |
14 | pub use allocator::SQLite3Allocator;
15 | pub use nostd::*;
16 |
--------------------------------------------------------------------------------
/dart/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: powersync_sqlite_core_tests
2 | publish_to: "none"
3 | version: 0.0.1
4 | description: Tests for powersync-sqlite-core
5 | environment:
6 | sdk: ^3.4.0
7 | dependencies:
8 | sqlite3: ^2.7.6
9 | bson: ^5.0.5
10 |
11 | dev_dependencies:
12 | test: ^1.25.0
13 | test_descriptor: ^2.0.2
14 | file: ^7.0.1
15 | sqlite3_test: ^0.1.1
16 | fake_async: ^1.3.3
17 | convert: ^3.1.2
18 | meta: ^1.16.0
19 | path: ^1.9.1
20 | http: ^1.5.0
21 | archive: ^4.0.7
22 |
--------------------------------------------------------------------------------
/crates/shell/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "powersync_sqlite"
3 | edition.workspace = true
4 | version.workspace = true
5 | homepage.workspace = true
6 | repository.workspace = true
7 | license.workspace = true
8 | authors.workspace = true
9 | keywords.workspace = true
10 |
11 | [dependencies]
12 | powersync_core = { path="../core" }
13 | powersync_sqlite_nostd = { path = "../sqlite_nostd" }
14 |
15 | [features]
16 | default = ["powersync_core/static", "powersync_sqlite_nostd/static"]
17 |
18 | [build-dependencies]
19 | cc = "1.0.46"
20 |
--------------------------------------------------------------------------------
/crates/static/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "powersync_static"
3 | edition.workspace = true
4 | version.workspace = true
5 | homepage.workspace = true
6 | repository.workspace = true
7 | license.workspace = true
8 | authors.workspace = true
9 | keywords.workspace = true
10 |
11 | [lib]
12 | name = "powersync"
13 | crate-type = ["staticlib"]
14 |
15 | [dependencies]
16 | powersync_sqlite_nostd = { path = "../sqlite_nostd" }
17 |
18 | [dependencies.powersync_core]
19 | path = "../core"
20 | default-features = false
21 | features = []
22 |
23 | [features]
24 | nightly = []
25 | default = ["powersync_core/static"]
26 |
--------------------------------------------------------------------------------
/crates/core/src/sync/mod.rs:
--------------------------------------------------------------------------------
1 | use alloc::rc::Rc;
2 | use powersync_sqlite_nostd::{self as sqlite, ResultCode};
3 |
4 | mod bucket_priority;
5 | pub mod checkpoint;
6 | mod checksum;
7 | mod interface;
8 | pub mod line;
9 | pub mod operations;
10 | pub mod storage_adapter;
11 | mod streaming_sync;
12 | mod subscriptions;
13 | mod sync_status;
14 |
15 | pub use bucket_priority::BucketPriority;
16 | pub use checksum::Checksum;
17 |
18 | use crate::state::DatabaseState;
19 |
20 | pub fn register(db: *mut sqlite::sqlite3, state: Rc) -> Result<(), ResultCode> {
21 | interface::register(db, state)
22 | }
23 |
--------------------------------------------------------------------------------
/crates/loadable/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "powersync_loadable"
3 | edition.workspace = true
4 | version.workspace = true
5 | homepage.workspace = true
6 | repository.workspace = true
7 | license.workspace = true
8 | authors.workspace = true
9 | keywords.workspace = true
10 |
11 | [lib]
12 | name = "powersync"
13 | crate-type = ["cdylib", "staticlib"]
14 |
15 | [dependencies]
16 | powersync_sqlite_nostd = { path = "../sqlite_nostd" }
17 |
18 | [dependencies.powersync_core]
19 | path = "../core"
20 | default-features = false
21 | features = []
22 |
23 | [features]
24 | nightly = []
25 | static = ["powersync_core/static"]
26 | default = ["powersync_core/getrandom"]
27 |
--------------------------------------------------------------------------------
/crates/sqlite_nostd/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "powersync_sqlite_nostd"
3 | edition.workspace = true
4 | version.workspace = true
5 | homepage.workspace = true
6 | repository.workspace = true
7 | license.workspace = true
8 | authors.workspace = true
9 | keywords.workspace = true
10 | description = "Lightweight, semi-unsafe, nostd bindings to sqlite3"
11 | readme = "README.md"
12 |
13 | [lib]
14 | name = "powersync_sqlite_nostd"
15 | crate-type = ["rlib"]
16 |
17 |
18 | [dependencies]
19 | num-derive = "0.4.2"
20 | num-traits = { version = "0.2.19", default-features = false }
21 |
22 | [features]
23 | static = []
24 |
25 | [build-dependencies]
26 | bindgen = "0.72.1"
27 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version: 5.7
2 |
3 | // NOTE! This is never released, we're only using this to support local builds builds for the
4 | // Swift SDK.
5 | import PackageDescription
6 | let packageName = "PowerSyncSQLiteCore"
7 |
8 | let package = Package(
9 | name: packageName,
10 | platforms: [
11 | .iOS(.v13),
12 | .macOS(.v10_15),
13 | .watchOS(.v9)
14 | ],
15 | products: [
16 | .library(
17 | name: packageName,
18 | targets: [packageName]),
19 | ],
20 | targets: [
21 | .binaryTarget(
22 | name: packageName,
23 | path: "powersync-sqlite-core.xcframework"
24 | )
25 | ]
26 | )
27 |
--------------------------------------------------------------------------------
/crates/core/build.rs:
--------------------------------------------------------------------------------
1 | use std::process::Command;
2 | fn main() {
3 | let mut git_hash = Command::new("git")
4 | .args(&["rev-parse", "HEAD"])
5 | .output()
6 | .ok()
7 | .and_then(|output| String::from_utf8(output.stdout).ok())
8 | .unwrap_or_default();
9 |
10 | if git_hash.is_empty() {
11 | // We can't compute the git hash for versions pushed to crates.io. That's fine, we'll use a
12 | // separate designator for that instead. The designator needs to be 8 chars in length since
13 | // that's the substring used in version numbers.
14 | git_hash = "cratesio".to_owned();
15 | }
16 |
17 | println!("cargo:rustc-env=GIT_HASH={}", git_hash);
18 | }
19 |
--------------------------------------------------------------------------------
/crates/core/src/constants.rs:
--------------------------------------------------------------------------------
1 | use core::ffi::c_int;
2 |
3 | pub const CORE_PKG_VERSION: &'static str = env!("CARGO_PKG_VERSION");
4 | pub const FULL_GIT_HASH: &'static str = env!("GIT_HASH");
5 |
6 | // We need 3.44 or later to use an `ORDER BY` in an aggregate function invocation.
7 | //
8 | // When raising the minimum version requirement, also change it in download_sqlite3.dart to ensure
9 | // we're testing with the minimum version we claim to support.
10 | pub const MIN_SQLITE_VERSION_NUMBER: c_int = 3044000;
11 |
12 | pub const SUBTYPE_JSON: u32 = 'J' as u32;
13 |
14 | pub fn short_git_hash() -> &'static str {
15 | if FULL_GIT_HASH.len() >= 8 {
16 | &FULL_GIT_HASH[..8]
17 | } else {
18 | "no-git-unknown"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.github/actions/macos/action.yml:
--------------------------------------------------------------------------------
1 | name: "Build macoS libraries"
2 | description: "Create artifact for macOS libraries"
3 |
4 | runs:
5 | using: "composite"
6 | steps:
7 | - name: Install Rust Nightly
8 | uses: dtolnay/rust-toolchain@stable
9 | with:
10 | toolchain: nightly-2025-10-31
11 | components: rust-src
12 | targets: x86_64-apple-darwin,aarch64-apple-darwin
13 |
14 | - name: Build binaries
15 | shell: bash
16 | run: |
17 | ./tool/build_macos.sh x64
18 | ./tool/build_macos.sh aarch64
19 |
20 | - uses: actions/upload-artifact@v4
21 | with:
22 | name: macos-library
23 | retention-days: 14
24 | path: |
25 | *.dylib
26 | *.a
27 |
--------------------------------------------------------------------------------
/crates/core/src/schema/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod inspection;
2 | mod management;
3 | mod table_info;
4 |
5 | use alloc::{rc::Rc, vec::Vec};
6 | use powersync_sqlite_nostd as sqlite;
7 | use serde::Deserialize;
8 | use sqlite::ResultCode;
9 | pub use table_info::{
10 | Column, DiffIncludeOld, PendingStatement, PendingStatementValue, RawTable, Table,
11 | TableInfoFlags,
12 | };
13 |
14 | use crate::state::DatabaseState;
15 |
16 | #[derive(Deserialize, Default)]
17 | pub struct Schema {
18 | pub tables: Vec,
19 | #[serde(default)]
20 | pub raw_tables: Vec,
21 | }
22 |
23 | pub fn register(db: *mut sqlite::sqlite3, state: Rc) -> Result<(), ResultCode> {
24 | management::register(db, state)
25 | }
26 |
--------------------------------------------------------------------------------
/.github/actions/upload/action.yml:
--------------------------------------------------------------------------------
1 | name: "Upload binary file"
2 | description: "Upload binary file to GitHub releases"
3 | inputs:
4 | repo-token:
5 | required: true
6 | description: "The secret created for the workflow run"
7 | file-name:
8 | required: true
9 | description: "The file name to be uploaded"
10 | tag:
11 | required: false
12 | description: "The short ref name of the branch or tag that triggered the workflow run."
13 | default: ${{ github.ref_name }}
14 | runs:
15 | using: "composite"
16 | steps:
17 | - name: Upload binary
18 | shell: bash
19 | env:
20 | GH_TOKEN: ${{ github.token }}
21 | GH_REPO: ${{ github.repository }}
22 | run: |
23 | gh release upload "${{ inputs.tag }}" "${{ inputs.file-name }}"
24 |
--------------------------------------------------------------------------------
/.github/actions/wasm/action.yml:
--------------------------------------------------------------------------------
1 | name: "Build wasm libraries"
2 | description: "Create artifact for wasm libraries"
3 |
4 | runs:
5 | using: "composite"
6 | steps:
7 | - name: Install Rust Nightly
8 | uses: dtolnay/rust-toolchain@stable
9 | with:
10 | toolchain: nightly-2025-10-31
11 | components: rust-src
12 |
13 | - name: Setup emsdk
14 | uses: mymindstorm/setup-emsdk@v14
15 | with:
16 | version: 4.0.10
17 |
18 | - name: Build WASM
19 | shell: bash
20 | run: ./tool/build_wasm.sh
21 |
22 | - uses: actions/upload-artifact@v4
23 | with:
24 | name: wasm-library
25 | retention-days: 14
26 | path: |
27 | libpowersync-async.wasm
28 | libpowersync.wasm
29 | libpowersync-wasm.a
30 |
--------------------------------------------------------------------------------
/tool/build_windows.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | function compile() {
5 | local triple=$1
6 | local suffix=$2
7 |
8 | cargo build -p powersync_loadable -Z build-std=panic_abort,core,alloc --features=nightly --release --target $triple
9 | cargo build -p powersync_static -Z build-std=panic_abort,core,alloc --features=nightly --release --target $triple
10 |
11 | mv "target/$triple/release/powersync.dll" "powersync_$suffix.dll"
12 | mv "target/$triple/release/powersync.lib" "powersync_$suffix.lib"
13 | }
14 |
15 | case "$1" in
16 | x64)
17 | compile x86_64-pc-windows-msvc x64
18 | ;;
19 | x86)
20 | compile i686-pc-windows-msvc x86
21 | ;;
22 | aarch64)
23 | compile aarch64-pc-windows-msvc aarch64
24 | ;;
25 | *)
26 | echo "Unknown architecture"
27 | exit 1
28 | ;;
29 | esac
30 |
--------------------------------------------------------------------------------
/.github/actions/windows/action.yml:
--------------------------------------------------------------------------------
1 | name: "Build Windows libraries"
2 | description: "Create artifact for Windows libraries"
3 |
4 | runs:
5 | using: "composite"
6 | steps:
7 | - name: Install Rust Nightly
8 | uses: dtolnay/rust-toolchain@stable
9 | with:
10 | toolchain: nightly-2025-10-31
11 | components: rust-src
12 | targets: x86_64-pc-windows-msvc,aarch64-pc-windows-msvc,i686-pc-windows-msvc
13 |
14 | - name: Build binaries
15 | shell: bash
16 | run: |
17 | ./tool/build_windows.sh x64
18 | ./tool/build_windows.sh aarch64
19 | ./tool/build_windows.sh x86
20 |
21 | - uses: actions/upload-artifact@v4
22 | with:
23 | name: windows-library
24 | retention-days: 14
25 | path: |
26 | *.dll
27 | *.lib
28 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 |
3 | members = [
4 | "crates/*"
5 | ]
6 | resolver = "2"
7 | # We cannot build shell and lodable and the same time
8 | default-members = ["crates/shell", "crates/sqlite"]
9 |
10 | [profile.dev]
11 | panic = "abort"
12 |
13 | [profile.release]
14 | panic = "abort"
15 | strip = true
16 | opt-level = "z"
17 | lto = true
18 |
19 | [profile.release_apple]
20 | inherits = "release"
21 | strip = false
22 | debug = true
23 |
24 | [profile.wasm]
25 | inherits = "release"
26 |
27 | [profile.wasm_asyncify]
28 | inherits = "wasm"
29 |
30 | [workspace.package]
31 | version = "0.4.10"
32 | edition = "2024"
33 | authors = ["JourneyApps"]
34 | keywords = ["sqlite", "powersync"]
35 | license = "Apache-2.0"
36 | homepage = "https://powersync.com"
37 | repository = "https://github.com/powersync-ja/powersync-sqlite-core"
38 |
--------------------------------------------------------------------------------
/crates/sqlite/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | let mut cfg = cc::Build::new();
3 |
4 | // Compile the SQLite source
5 | cfg.file("./sqlite/sqlite3.c");
6 | cfg.file("./sqlite/shell.c");
7 | cfg.include("./sqlite");
8 |
9 | // General SQLite options
10 | cfg.define("SQLITE_THREADSAFE", Some("0"));
11 | cfg.define("SQLITE_ENABLE_BYTECODE_VTAB", Some("1"));
12 |
13 | // Compile with readline support (also requires -lreadline / cargo:rustc-link-lib=readline below)
14 | cfg.define("HAVE_READLINE", Some("1"));
15 |
16 | // Silence warnings generated for SQLite
17 | cfg.flag("-Wno-implicit-fallthrough");
18 | cfg.flag("-Wno-unused-parameter");
19 | cfg.flag("-Wno-null-pointer-subtraction");
20 |
21 | cfg.compile("sqlite");
22 |
23 | println!("cargo:rustc-link-lib=readline");
24 | }
25 |
--------------------------------------------------------------------------------
/crates/sqlite_nostd/src/allocator.rs:
--------------------------------------------------------------------------------
1 | use core::alloc::{GlobalAlloc, Layout};
2 |
3 | /// A [GlobalAlloc] implementation forwarding allocations to the
4 | /// [memory allocation subsystem](https://sqlite.org/c3ref/free.html) in SQLite.
5 | ///
6 | /// Using this allocator allows moving allocated Rust values to SQLite.
7 | pub struct SQLite3Allocator {}
8 |
9 | unsafe impl GlobalAlloc for SQLite3Allocator {
10 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
11 | crate::capi::malloc(layout.size())
12 | }
13 |
14 | unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
15 | crate::capi::free(ptr as *mut core::ffi::c_void);
16 | }
17 |
18 | unsafe fn realloc(&self, ptr: *mut u8, _layout: Layout, new_size: usize) -> *mut u8 {
19 | crate::capi::realloc(ptr.cast(), new_size)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/powersync-sqlite-core.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'powersync-sqlite-core'
3 | s.version = '0.4.10'
4 | s.summary = 'PowerSync SQLite Extension'
5 | s.description = <<-DESC
6 | PowerSync extension for SQLite.
7 | DESC
8 |
9 | s.homepage = 'https://github.com/powersync-ja/powersync-sqlite-core'
10 | s.license = 'Apache License, Version 2.0'
11 | s.author = 'Journey Mobile, Inc.'
12 |
13 | s.source = { :http => "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v#{s.version}/powersync-sqlite-core.xcframework.zip" }
14 | s.vendored_frameworks = 'powersync-sqlite-core.xcframework'
15 |
16 | s.ios.deployment_target = '11.0'
17 | s.osx.deployment_target = '10.13'
18 | s.watchos.deployment_target = '9.0'
19 | end
20 |
--------------------------------------------------------------------------------
/tool/build_macos.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | function compile() {
5 | local triple=$1
6 | local suffix=$2
7 | local os=$3
8 |
9 | cargo build -p powersync_loadable -Z build-std=panic_abort,core,alloc --features nightly --release --target $triple
10 | cargo build -p powersync_static -Z build-std=panic_abort,core,alloc --features nightly --release --target $triple
11 |
12 | mv "target/$triple/release/libpowersync.dylib" "libpowersync_$suffix.$os.dylib"
13 | mv "target/$triple/release/libpowersync.a" "libpowersync_$suffix.$os.a"
14 | }
15 |
16 | case "$1" in
17 | x64)
18 | compile x86_64-apple-darwin x64 macos
19 | compile x86_64-apple-ios x64 ios-sim
20 | ;;
21 | aarch64)
22 | compile aarch64-apple-darwin aarch64 macos
23 | compile aarch64-apple-ios-sim aarch64 ios-sim
24 | compile aarch64-apple-ios aarch64 ios
25 | ;;
26 | *)
27 | echo "Unknown architecture"
28 | exit 1;
29 | ;;
30 | esac
31 |
--------------------------------------------------------------------------------
/crates/shell/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | let mut cfg = cc::Build::new();
3 |
4 | // Compile the SQLite source
5 | cfg.file("../sqlite/sqlite/sqlite3.c");
6 | cfg.file("../sqlite/sqlite/shell.c");
7 | cfg.include("../sqlite/sqlite");
8 |
9 | // General SQLite options
10 | cfg.define("SQLITE_THREADSAFE", Some("0"));
11 | cfg.define("SQLITE_ENABLE_BYTECODE_VTAB", Some("1"));
12 |
13 | // Call core_init() in main.rs
14 | cfg.define("SQLITE_EXTRA_INIT", Some("core_init"));
15 |
16 | // Compile with readline support (also requires -lreadline / cargo:rustc-link-lib=readline below)
17 | cfg.define("HAVE_READLINE", Some("1"));
18 |
19 | // Silence warnings generated for SQLite
20 | cfg.flag("-Wno-implicit-fallthrough");
21 | cfg.flag("-Wno-unused-parameter");
22 | cfg.flag("-Wno-null-pointer-subtraction");
23 |
24 | cfg.compile("sqlite-ps");
25 |
26 | println!("cargo:rustc-link-lib=readline");
27 | }
28 |
--------------------------------------------------------------------------------
/crates/static/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 | #![allow(internal_features)]
3 | #![cfg_attr(feature = "nightly", feature(core_intrinsics))]
4 |
5 | extern crate alloc;
6 |
7 | // Defines sqlite3_powersync_init
8 | #[allow(unused_imports)]
9 | use powersync_core;
10 |
11 | // Use the SQLite allocator, allowing us to freely transfer memory between SQLite and Rust.
12 | #[cfg(not(test))]
13 | use powersync_sqlite_nostd::SQLite3Allocator;
14 |
15 | #[cfg(not(test))]
16 | #[global_allocator]
17 | static ALLOCATOR: SQLite3Allocator = SQLite3Allocator {};
18 |
19 | // Custom Panic handler for WASM and other no_std builds
20 | #[cfg(not(test))]
21 | mod panic_handler {
22 | #[cfg(feature = "nightly")]
23 | #[panic_handler]
24 | fn panic(_info: &core::panic::PanicInfo) -> ! {
25 | core::intrinsics::abort()
26 | }
27 |
28 | #[cfg(not(feature = "nightly"))]
29 | #[panic_handler]
30 | fn panic(_info: &core::panic::PanicInfo) -> ! {
31 | loop {}
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/crates/shell/src/main.rs:
--------------------------------------------------------------------------------
1 | #![no_main]
2 | #![no_std]
3 | #![allow(internal_features)]
4 | #![feature(lang_items)]
5 | #![feature(core_intrinsics)]
6 |
7 | use core::ffi::{c_char, c_int};
8 |
9 | use powersync_core::powersync_init_static;
10 |
11 | // Use the SQLite allocator, allowing us to freely transfer memory between SQLite and Rust.
12 | #[cfg(not(test))]
13 | use powersync_sqlite_nostd::SQLite3Allocator;
14 |
15 | #[cfg(not(test))]
16 | #[global_allocator]
17 | static ALLOCATOR: SQLite3Allocator = SQLite3Allocator {};
18 |
19 | // Custom Panic handler for WASM and other no_std builds
20 | #[cfg(not(test))]
21 | #[panic_handler]
22 | fn panic(_info: &core::panic::PanicInfo) -> ! {
23 | core::intrinsics::abort()
24 | }
25 |
26 | #[cfg(not(target_family = "wasm"))]
27 | #[cfg(not(test))]
28 | #[lang = "eh_personality"]
29 | extern "C" fn eh_personality() {}
30 |
31 | #[unsafe(no_mangle)]
32 | pub extern "C" fn core_init(_dummy: *mut c_char) -> c_int {
33 | powersync_init_static()
34 | }
35 |
--------------------------------------------------------------------------------
/tool/build_linux.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | function compile() {
5 | local triple=$1
6 | local suffix=$2
7 |
8 | cargo build -p powersync_loadable -Z build-std=panic_abort,core,alloc --features nightly --release --target $triple
9 | cargo build -p powersync_static -Z build-std=panic_abort,core,alloc --features nightly --release --target $triple
10 |
11 | mv "target/$triple/release/libpowersync.so" "libpowersync_$suffix.linux.so"
12 | mv "target/$triple/release/libpowersync.a" "libpowersync_$suffix.linux.a"
13 | }
14 |
15 | case "$1" in
16 | x64)
17 | compile x86_64-unknown-linux-gnu x64
18 | ;;
19 | x86)
20 | compile i686-unknown-linux-gnu x86
21 | ;;
22 | aarch64)
23 | compile aarch64-unknown-linux-gnu aarch64
24 | ;;
25 | armv7)
26 | compile armv7-unknown-linux-gnueabihf armv7
27 | ;;
28 | riscv64gc)
29 | compile riscv64gc-unknown-linux-gnu riscv64gc
30 | ;;
31 | *)
32 | echo "Unknown architecture"
33 | exit 1;
34 | ;;
35 | esac
36 |
--------------------------------------------------------------------------------
/dart/test/goldens/starting_stream.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "operation": "start",
4 | "data": {
5 | "parameters": {
6 | "foo": "bar"
7 | }
8 | },
9 | "output": [
10 | {
11 | "UpdateSyncStatus": {
12 | "status": {
13 | "connected": false,
14 | "connecting": true,
15 | "priority_status": [],
16 | "downloading": null,
17 | "streams": []
18 | }
19 | }
20 | },
21 | {
22 | "EstablishSyncStream": {
23 | "request": {
24 | "buckets": [],
25 | "include_checksum": true,
26 | "raw_data": true,
27 | "binary_data": true,
28 | "client_id": "test-test-test-test",
29 | "parameters": {
30 | "foo": "bar"
31 | },
32 | "streams": {
33 | "include_defaults": true,
34 | "subscriptions": []
35 | }
36 | }
37 | }
38 | }
39 | ]
40 | }
41 | ]
--------------------------------------------------------------------------------
/crates/core/src/ext.rs:
--------------------------------------------------------------------------------
1 | use powersync_sqlite_nostd::{Connection, Destructor, ManagedStmt, ResultCode, sqlite3};
2 |
3 | pub trait SafeManagedStmt {
4 | fn exec(&self) -> Result<(), ResultCode>;
5 | }
6 |
7 | impl SafeManagedStmt for ManagedStmt {
8 | fn exec(&self) -> Result<(), ResultCode> {
9 | loop {
10 | let rs = self.step()?;
11 | if rs == ResultCode::ROW {
12 | continue;
13 | }
14 |
15 | self.reset()?;
16 | if rs == ResultCode::DONE {
17 | break;
18 | } else {
19 | return Err(rs);
20 | }
21 | }
22 | Ok(())
23 | }
24 | }
25 |
26 | pub trait ExtendedDatabase {
27 | fn exec_text(&self, sql: &str, param: &str) -> Result<(), ResultCode>;
28 | }
29 |
30 | impl ExtendedDatabase for *mut sqlite3 {
31 | fn exec_text(&self, sql: &str, param: &str) -> Result<(), ResultCode> {
32 | let statement = self.prepare_v2(sql)?;
33 | statement.bind_text(1, param, Destructor::STATIC)?;
34 |
35 | statement.exec()?;
36 | Ok(())
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/crates/core/src/version.rs:
--------------------------------------------------------------------------------
1 | extern crate alloc;
2 |
3 | use alloc::format;
4 | use alloc::string::String;
5 | use core::ffi::c_int;
6 |
7 | use powersync_sqlite_nostd as sqlite;
8 | use powersync_sqlite_nostd::{Connection, Context};
9 | use sqlite::ResultCode;
10 |
11 | use crate::constants::{CORE_PKG_VERSION, short_git_hash};
12 | use crate::create_sqlite_text_fn;
13 | use crate::error::PowerSyncError;
14 |
15 | fn powersync_rs_version_impl(
16 | _ctx: *mut sqlite::context,
17 | _args: &[*mut sqlite::value],
18 | ) -> Result {
19 | let version = format!("{}/{}", CORE_PKG_VERSION, short_git_hash());
20 | Ok(version)
21 | }
22 |
23 | create_sqlite_text_fn!(
24 | powersync_rs_version,
25 | powersync_rs_version_impl,
26 | "powersync_rs_version"
27 | );
28 |
29 | pub fn register(db: *mut sqlite::sqlite3) -> Result<(), ResultCode> {
30 | db.create_function_v2(
31 | "powersync_rs_version",
32 | 0,
33 | sqlite::UTF8,
34 | None,
35 | Some(powersync_rs_version),
36 | None,
37 | None,
38 | None,
39 | )?;
40 |
41 | Ok(())
42 | }
43 |
--------------------------------------------------------------------------------
/.github/actions/linux/action.yml:
--------------------------------------------------------------------------------
1 | name: "Build Linux libraries"
2 | description: "Create artifact for Linux libraries"
3 |
4 | runs:
5 | using: "composite"
6 | steps:
7 | - name: Install Rust Nightly
8 | uses: dtolnay/rust-toolchain@stable
9 | with:
10 | toolchain: nightly-2025-10-31
11 | components: rust-src
12 | targets: aarch64-unknown-linux-gnu,x86_64-unknown-linux-gnu,i686-unknown-linux-gnu,riscv64gc-unknown-linux-gnu,armv7-unknown-linux-gnueabihf
13 |
14 | - name: Install cross-compiling GCC
15 | shell: bash
16 | run: |
17 | sudo apt update
18 | sudo apt install -y gcc-aarch64-linux-gnu gcc-riscv64-linux-gnu gcc-arm-linux-gnueabihf gcc-i686-linux-gnu
19 |
20 | - name: Build binaries
21 | shell: bash
22 | run: |
23 | ./tool/build_linux.sh x64
24 | ./tool/build_linux.sh aarch64
25 | ./tool/build_linux.sh x86
26 | ./tool/build_linux.sh armv7
27 | ./tool/build_linux.sh riscv64gc
28 |
29 | - uses: actions/upload-artifact@v4
30 | with:
31 | name: linux-library
32 | retention-days: 14
33 | path: |
34 | *.so
35 | *.linux.a
36 |
--------------------------------------------------------------------------------
/crates/core/src/uuid.rs:
--------------------------------------------------------------------------------
1 | extern crate alloc;
2 |
3 | use alloc::string::String;
4 | use alloc::string::ToString;
5 | use core::ffi::c_int;
6 |
7 | use powersync_sqlite_nostd as sqlite;
8 | use powersync_sqlite_nostd::{Connection, Context};
9 | use sqlite::ResultCode;
10 |
11 | use crate::create_sqlite_text_fn;
12 | use crate::error::PowerSyncError;
13 | use crate::util::*;
14 |
15 | fn uuid_v4_impl(
16 | _ctx: *mut sqlite::context,
17 | _args: &[*mut sqlite::value],
18 | ) -> Result {
19 | let id = gen_uuid();
20 | Ok(id.hyphenated().to_string())
21 | }
22 |
23 | create_sqlite_text_fn!(uuid_v4, uuid_v4_impl, "gen_random_uuid");
24 |
25 | pub fn register(db: *mut sqlite::sqlite3) -> Result<(), ResultCode> {
26 | db.create_function_v2(
27 | "gen_random_uuid",
28 | 0,
29 | sqlite::UTF8,
30 | None,
31 | Some(uuid_v4),
32 | None,
33 | None,
34 | None,
35 | )?;
36 |
37 | db.create_function_v2(
38 | "uuid",
39 | 0,
40 | sqlite::UTF8,
41 | None,
42 | Some(uuid_v4),
43 | None,
44 | None,
45 | None,
46 | )?;
47 |
48 | Ok(())
49 | }
50 |
--------------------------------------------------------------------------------
/crates/sqlite_nostd/build.rs:
--------------------------------------------------------------------------------
1 | extern crate bindgen;
2 |
3 | use std::env;
4 | use std::path::PathBuf;
5 |
6 | fn main() {
7 | println!("cargo:rerun-if-changed=deps/sqlite3ext.h");
8 |
9 | let bindings = bindgen::Builder::default()
10 | // The input header we would like to generate
11 | // bindings for.
12 | .header("deps/sqlite3ext.h")
13 | .clang_arg("-fvisibility=default")
14 | // ^-- to get functions exposed in wasm
15 | // https://github.com/rust-lang/rust-bindgen/issues/751
16 | .use_core()
17 | // Tell cargo to invalidate the built crate whenever any of the
18 | // included header files changed.
19 | .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
20 | // Finish the builder and generate the bindings.
21 | .generate()
22 | // Unwrap the Result and panic on failure.
23 | .expect("Unable to generate bindings");
24 |
25 | // Write the bindings to the $OUT_DIR/bindings.rs file.
26 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
27 | bindings
28 | .write_to_file(out_path.join("bindings.rs"))
29 | .expect("Couldn't write bindings!");
30 | }
31 |
--------------------------------------------------------------------------------
/docs/RELEASING.md:
--------------------------------------------------------------------------------
1 | # Preparing Release
2 |
3 | First, bump the version number in these places:
4 |
5 | 1. Cargo.toml
6 | 2. powersync-sqlite-core.podspec.
7 | 3. android/build.gradle.kts
8 | 4. android/src/prefab/prefab.json
9 | 5. tool/build_xcframework.sh - `VERSION` variable.
10 | 6. Version dependency from `crates/core` to `crates/sqlite_nostd`.
11 | 7. `cargo build` to update Cargo.lock
12 |
13 | Next, open a PR with these changes and wait for it to get approved and merged.
14 |
15 | # Perform Release
16 |
17 | Create a tag, which will trigger a release workflow when pushed:
18 |
19 | ```sh
20 | git tag -am v1.2.3 v1.2.3
21 | git push --tags
22 | ```
23 |
24 | The publishing workflow does the following:
25 |
26 | 1. Create a draft GitHub release.
27 | 2. Build the xcframework for iOS and macOS, and upload to GitHub (attached to the above release).
28 | 3. Build and publish an Android aar to Sonatype. Afterwards, you can monitor the status of the publishing step [here](https://central.sonatype.com/publishing/deployments).
29 |
30 | The cocoapod needs to be published manually:
31 |
32 | ```sh
33 | pod trunk push powersync-sqlite-core.podspec
34 | ```
35 |
36 | # Updating SDKs
37 |
38 | The release workflow will create an issue with a list of items to update the individual SDKs and intermediate packages.
39 |
--------------------------------------------------------------------------------
/tool/build_wasm.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | emcc --version
4 |
5 | # Normal build
6 | # target/wasm32-unknown-emscripten/wasm/powersync.wasm
7 | RUSTFLAGS="-C link-arg=-sSIDE_MODULE=2" \
8 | cargo build \
9 | -p powersync_loadable \
10 | --profile wasm \
11 | --no-default-features \
12 | --features "static nightly" \
13 | -Z build-std=panic_abort,core,alloc \
14 | --target wasm32-unknown-emscripten
15 |
16 | cp "target/wasm32-unknown-emscripten/wasm/powersync.wasm" "libpowersync.wasm"
17 |
18 | # Asyncify
19 | # target/wasm32-unknown-emscripten/wasm_asyncify/powersync.wasm
20 | RUSTFLAGS="-C link-arg=-sSIDE_MODULE=2 -C link-arg=-sASYNCIFY=1 -C link-arg=-sJSPI_IMPORTS=@wasm/asyncify_imports.json" \
21 | cargo build \
22 | -p powersync_loadable \
23 | --profile wasm_asyncify \
24 | --no-default-features \
25 | --features "static nightly" \
26 | -Z build-std=panic_abort,core,alloc \
27 | --target wasm32-unknown-emscripten
28 |
29 | cp "target/wasm32-unknown-emscripten/wasm_asyncify/powersync.wasm" "libpowersync-async.wasm"
30 |
31 |
32 | # Static lib.
33 | # Works for both sync and asyncify builds.
34 | # Works for both emscripten and wasi.
35 | # target/wasm32-wasip1/wasm/libpowersync.a
36 | cargo build \
37 | -p powersync_loadable \
38 | --profile wasm \
39 | --no-default-features \
40 | --features "static nightly" \
41 | -Z build-std=panic_abort,core,alloc \
42 | --target wasm32-wasip1
43 |
44 | cp "target/wasm32-wasip1/wasm/libpowersync.a" "libpowersync-wasm.a"
45 |
--------------------------------------------------------------------------------
/crates/core/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | _[PowerSync](https://www.powersync.com) is a sync engine for building local-first apps with instantly-responsive UI/UX and simplified state transfer. Syncs between SQLite on the client-side and Postgres, MongoDB or MySQL on the server-side._
6 |
7 | # powersync_core
8 |
9 | This is the core SQLite extension, containing all the logic. This is used internally by PowerSync SDKs,
10 | and would typically not be used by users directly.
11 |
12 | The role of the extension is to create user-defined functions that higher-level SDKs would use to implement
13 | schema management and a PowerSync client.
14 | Not all of this is documented, but [this directory](https://github.com/powersync-ja/powersync-sqlite-core/tree/main/docs)
15 | provides some hints on how a custom PowerSync SDK could be implemented.
16 |
17 | For this reason, the crate doesn't have much of a public API. In the default build mode, it doesn't expect
18 | SQLite to be linked and exposes a single function: `sqlite3_powersync_init`,
19 | a [loadable extension](https://sqlite.org/loadext.html) entrypoint.
20 |
21 | For applications linking SQLite, the `static` feature of this crate can be enabled.
22 | With that feature, `powersync_init_static()` can be called to load the
23 | extension for all new connections.
24 | The application is responsible for linking SQLite in that case.
25 |
--------------------------------------------------------------------------------
/crates/core/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "powersync_core"
3 | edition.workspace = true
4 | version.workspace = true
5 | homepage.workspace = true
6 | repository.workspace = true
7 | license.workspace = true
8 | authors.workspace = true
9 | keywords.workspace = true
10 | description = "The PowerSync SQLite extension"
11 | readme = "README.md"
12 |
13 | [lib]
14 | name = "powersync_core"
15 | crate-type = ["rlib"]
16 |
17 | [dependencies]
18 | powersync_sqlite_nostd = { version = "=0.4.10", path = "../sqlite_nostd" }
19 | bytes = { version = "1.4", default-features = false }
20 | num-traits = { version = "0.2.15", default-features = false }
21 | num-derive = "0.3"
22 | serde_json = { version = "1.0", default-features = false, features = ["alloc", "raw_value"] }
23 | serde = { version = "1.0", default-features = false, features = ["alloc", "derive", "rc"] }
24 | const_format = "0.2.34"
25 | futures-lite = { version = "2.6.0", default-features = false, features = ["alloc"] }
26 | rustc-hash = { version = "2.1", default-features = false }
27 | thiserror = { version = "2", default-features = false }
28 | serde_with = { version = "3.14.0", default-features = false, features = ["alloc", "macros"] }
29 |
30 | [dependencies.uuid]
31 | version = "1.4.1"
32 | default-features = false
33 | features = []
34 |
35 |
36 | [dev-dependencies]
37 |
38 |
39 | [features]
40 | default = ["getrandom"]
41 |
42 | static = ["powersync_sqlite_nostd/static"]
43 | # Enable to use the getrandom crate instead of sqlite3_randomness
44 | # Enable for Windows builds; do not enable for WASM
45 | getrandom = ["uuid/v4"]
46 |
47 |
--------------------------------------------------------------------------------
/crates/sqlite_nostd/README.md:
--------------------------------------------------------------------------------
1 | This is a fork of https://github.com/vlcn-io/sqlite-rs-embedded with adaptations for the PowerSync core extension.
2 |
3 | # SQLite no_std
4 |
5 | > Note: these bindings are faithful to the base SQLite C-API as much as possible for minimum rust<->c overhead. This, however, means that the bindings are not entirely safe. E.g., the SQLite statement object will clear returned values out from under you if you step or finalize it while those references exist in your Rust program.
6 |
7 | SQLite is lite. Its bindings should be lite too. They should be able to be used _anywhere_ SQLite is used, _not_ incur any performance impact, _not_ include any extra dependencies, and be usable against _any_ SQLite version.
8 |
9 | Thus this repository was born.
10 |
11 | These bindings:
12 |
13 | - Do not require the rust standard library
14 | - Can use the SQLite memory subsystem if no allocator exists
15 | - Can be used to write SQLite extensions that compile to WASM and run in the browser
16 | - Does 0 copying. E.g., through some tricks, Rust strings are passed directly to SQLite with no conversion to or copying to CString.
17 |
18 | ## Features
19 |
20 | By default, this crate compiles to be used in a loadable SQLite extension: All calls are dispatched through
21 | the `sqlite3_api_routines` struct, and one needs to call `EXTENSION_INIT2()` from an entrypoint before using
22 | the library.
23 |
24 | Outside of loadable extensions, one can enable the `static` feature. When enabled, calls go to `sqlite3_`
25 | functions directly, SQLite needs to be linked for this library to work.
26 |
--------------------------------------------------------------------------------
/.github/actions/xcframework/action.yml:
--------------------------------------------------------------------------------
1 | name: "Build xcframework"
2 | description: "Create artifact with XCFramework for apple targets"
3 |
4 | runs:
5 | using: "composite"
6 | steps:
7 | - name: Setup
8 | shell: bash
9 | run: |
10 | rustup toolchain install nightly-2025-10-31-aarch64-apple-darwin
11 | rustup component add rust-src --toolchain nightly-2025-10-31-aarch64-apple-darwin
12 | rustup target add \
13 | x86_64-apple-darwin \
14 | aarch64-apple-darwin \
15 | aarch64-apple-ios \
16 | aarch64-apple-ios-sim \
17 | x86_64-apple-ios
18 |
19 | - name: setup-cocoapods
20 | uses: maxim-lobanov/setup-cocoapods@v1
21 | with:
22 | version: 1.16.2
23 |
24 | - name: Set up XCode
25 | uses: maxim-lobanov/setup-xcode@v1
26 | with:
27 | # TODO: Update to latest-stable once GH installs iOS 26 simulators
28 | xcode-version: '^16.4.0'
29 |
30 | - name: List simulators
31 | shell: bash
32 | run: |
33 | xcrun xctrace list devices
34 |
35 | - name: Build iOS & macOS xcframework
36 | shell: bash
37 | run: |
38 | ./tool/build_xcframework.sh
39 |
40 | - name: Lint pod
41 | shell: bash
42 | run: |
43 | pod lib lint
44 |
45 | - uses: actions/upload-artifact@v4
46 | with:
47 | name: xcframework
48 | retention-days: 14
49 | compression-level: 0 # We're uploading a zip archive, no need to compress agan
50 | path: |
51 | powersync-sqlite-core.xcframework.zip
52 |
--------------------------------------------------------------------------------
/crates/loadable/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 | #![allow(internal_features)]
3 | #![cfg_attr(feature = "nightly", feature(core_intrinsics))]
4 | #![cfg_attr(feature = "nightly", feature(lang_items))]
5 |
6 | extern crate alloc;
7 |
8 | // Defines sqlite3_powersync_init
9 | #[allow(unused_imports)]
10 | use powersync_core;
11 |
12 | // Use the SQLite allocator, allowing us to freely transfer memory between SQLite and Rust.
13 | #[cfg(not(test))]
14 | use powersync_sqlite_nostd::SQLite3Allocator;
15 |
16 | #[cfg(not(test))]
17 | #[global_allocator]
18 | static ALLOCATOR: SQLite3Allocator = SQLite3Allocator {};
19 |
20 | // Custom Panic handler for WASM and other no_std builds
21 | #[cfg(not(test))]
22 | mod panic_handler {
23 | #[cfg(feature = "nightly")]
24 | #[panic_handler]
25 | fn panic(_info: &core::panic::PanicInfo) -> ! {
26 | core::intrinsics::abort()
27 | }
28 |
29 | #[cfg(not(feature = "nightly"))]
30 | #[panic_handler]
31 | fn panic(_info: &core::panic::PanicInfo) -> ! {
32 | loop {}
33 | }
34 |
35 | #[cfg(not(target_family = "wasm"))]
36 | #[cfg(feature = "nightly")]
37 | #[lang = "eh_personality"]
38 | extern "C" fn eh_personality() {}
39 |
40 | #[cfg(not(target_family = "wasm"))]
41 | #[cfg(not(feature = "nightly"))]
42 | #[unsafe(no_mangle)]
43 | extern "C" fn rust_eh_personality() {
44 | // This avoids missing _rust_eh_personality symbol errors.
45 | // This isn't used for any builds we distribute, but it's heplful to compile the library
46 | // with stable Rust, which we do for testing.
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/dart/benchmark/apply_lines.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'dart:typed_data';
3 |
4 | import '../test/utils/native_test_utils.dart';
5 |
6 | /// Usage: dart run benchmark/apply_lines.dart path/to/lines.bin
7 | ///
8 | /// This creates a new in-memory database and applies concatenated BSON sync
9 | /// lines from a file.
10 | void main(List args) {
11 | if (args.length != 1) {
12 | throw 'Usage: dart run benchmark/apply_lines.dart path/to/lines.bin';
13 | }
14 |
15 | final [path] = args;
16 | final file = File(path).openSync();
17 | final db = openTestDatabase();
18 |
19 | db
20 | ..execute('select powersync_init()')
21 | ..execute('select powersync_control(?, null)', ['start']);
22 |
23 | final stopwatch = Stopwatch()..start();
24 |
25 | final lengthBuffer = Uint8List(4);
26 | while (file.positionSync() < file.lengthSync()) {
27 | // BSON document:
28 | final bytesRead = file.readIntoSync(lengthBuffer);
29 | if (bytesRead != 4) {
30 | throw 'short read, expected length';
31 | }
32 | final length = lengthBuffer.buffer.asByteData().getInt32(0, Endian.little);
33 | file.setPositionSync(file.positionSync() - 4);
34 |
35 | final syncLineBson = file.readSync(length);
36 | if (syncLineBson.length != length) {
37 | throw 'short read for bson document';
38 | }
39 |
40 | db
41 | ..execute('BEGIN')
42 | ..execute('SELECT powersync_control(?, ?)', ['line_binary', syncLineBson])
43 | ..execute('COMMIT;');
44 | }
45 |
46 | stopwatch.stop();
47 | print('Applying $path took ${stopwatch.elapsed}');
48 | }
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PowerSync SQLite Extension
2 |
3 | This extension is used by PowerSync client SDKs.
4 |
5 | The APIs here not currently stable, and may change in any release. The APIs are intended to be used by PowerSync SDKs only.
6 |
7 | # API
8 |
9 | Primary APIs:
10 |
11 | ```sql
12 | -- Load the extension
13 | -- Sets up functions and views, but does not touch the database itself.
14 | .load powersync
15 |
16 | -- Configure the schemas.
17 | -- Creates data tables, indexes and views.
18 | SELECT powersync_replace_schema('{"tables": [{"name": "test", "columns": [{"name": "name", "type": "text"}]}]}');
19 |
20 | ```
21 |
22 | Other APIs:
23 |
24 | ```sql
25 | -- Initialize the extension data (creates internal tables).
26 | -- Optional - also called as part of powersync_replace_schema().
27 | -- Only useful to ensure internal tables are configured without touching the schema.
28 | SELECT powersync_init();
29 |
30 | ```
31 |
32 | # Building and running
33 |
34 | Initialize submodules recursively
35 |
36 | ```
37 | git submodule update --init --recursive
38 | ```
39 |
40 | ```sh
41 | # Build the shell
42 | cargo build -p powersync_sqlite
43 | ./target/debug/powersync_sqlite test.db "select powersync_rs_version()"
44 |
45 | # Build the loadable extension
46 | cargo build -p powersync_loadable
47 | sqlite3 ":memory:" ".load ./target/debug/libpowersync" "select powersync_rs_version()" #This requires sqlite3 installed
48 |
49 | # Build the release loadable extension
50 | cargo build -p powersync_loadable --release
51 |
52 | # Build for iOS
53 | ./tool/build_xcframework.sh
54 | ```
55 |
56 | # Acknowledgements
57 |
58 | Structure of the SQLite extension using Rust is inspired by [cr-sqlite](https://github.com/vlcn-io/cr-sqlite/).
59 |
--------------------------------------------------------------------------------
/dart/test/update_hooks_test.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:sqlite3/common.dart';
4 | import 'package:test/test.dart';
5 |
6 | import 'utils/native_test_utils.dart';
7 |
8 | void main() {
9 | late CommonDatabase db;
10 |
11 | setUp(() async {
12 | db = openTestDatabase()
13 | ..select('select powersync_init()')
14 | ..execute('CREATE TABLE foo (bar INTEGER);')
15 | ..select("SELECT powersync_update_hooks('install')");
16 | });
17 |
18 | tearDown(() {
19 | db.dispose();
20 | });
21 |
22 | List collectUpdates() {
23 | final [row] = db.select("SELECT powersync_update_hooks('get')");
24 | return (json.decode(row.values[0] as String) as List).cast();
25 | }
26 |
27 | test('is empty initially', () {
28 | expect(collectUpdates(), isEmpty);
29 | });
30 |
31 | test('reports changed tables', () {
32 | db.execute('INSERT INTO foo DEFAULT VALUES');
33 | expect(collectUpdates(), ['foo']);
34 | });
35 |
36 | test('deduplicates tables', () {
37 | final stmt = db.prepare('INSERT INTO foo (bar) VALUES (?)');
38 | for (var i = 0; i < 1000; i++) {
39 | stmt.execute([i]);
40 | }
41 | stmt.dispose();
42 |
43 | expect(collectUpdates(), ['foo']);
44 | });
45 |
46 | test('does not report changes before end of transaction', () {
47 | db.execute('BEGIN');
48 | db.execute('INSERT INTO foo DEFAULT VALUES');
49 | expect(collectUpdates(), isEmpty);
50 | db.execute('COMMIT');
51 |
52 | expect(collectUpdates(), ['foo']);
53 | });
54 |
55 | test('does not report rollbacks', () {
56 | db.execute('BEGIN');
57 | db.execute('INSERT INTO foo DEFAULT VALUES');
58 | expect(collectUpdates(), isEmpty);
59 | db.execute('ROLLBACK');
60 |
61 | expect(collectUpdates(), isEmpty);
62 | });
63 | }
64 |
--------------------------------------------------------------------------------
/dart/test/utils/test_utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:sqlite3/common.dart';
2 | import 'package:test/test.dart';
3 |
4 | /// Creates a `checkpoint` line.
5 | Object checkpoint({
6 | required int lastOpId,
7 | List