Skip to content

Add SQLite session support #300

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[submodule "sqlite3_android"]
path = sqlite3_android
url = https://github.com/rodydavis/sqlite-native-libraries
[submodule "sqlite_swift_package"]
path = sqlite_swift_package
url = https://github.com/rodydavis/CSQLite
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"rust-analyzer.linkedProjects": [
"sqlite3/example/custom_wasm_build/Cargo.toml"
],
"cSpell.words": [
"Patchset"
]
}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
43 changes: 43 additions & 0 deletions integration_tests/flutter_libs_swiftpm/ios/Podfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}

def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end

File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end

require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)

flutter_ios_podfile_setup

target 'Runner' do
use_frameworks!

flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end

post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"
42 changes: 42 additions & 0 deletions integration_tests/flutter_libs_swiftpm/macos/Podfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
platform :osx, '10.14'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}

def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
end

File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
end

require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)

flutter_macos_podfile_setup

target 'Runner' do
use_frameworks!

flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end

post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_macos_build_settings(target)
end
end
80 changes: 80 additions & 0 deletions sqlite3/assets/sqlite3.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ typedef struct sqlite3 sqlite3;
typedef struct sqlite3_stmt sqlite3_stmt;
typedef struct sqlite3_backup sqlite3_backup;
typedef struct sqlite3_api_routines sqlite3_api_routines;
typedef struct sqlite3_session sqlite3_session;
typedef struct sqlite3_changeset_iter sqlite3_changeset_iter;

sqlite3_char *sqlite3_temp_directory;

int sqlite3_initialize();
void sqlite3_free(void*);

int sqlite3_open_v2(sqlite3_char *filename, sqlite3 **ppDb, int flags,
sqlite3_char *zVfs);
Expand Down Expand Up @@ -216,3 +219,80 @@ struct sqlite3_vfs {
};
int sqlite3_vfs_register(sqlite3_vfs *, int makeDflt);
int sqlite3_vfs_unregister(sqlite3_vfs *);

// Session

int sqlite3session_create(
sqlite3 *db, /* Database handle */
const sqlite3_char *zDb, /* Name of db (e.g. "main") */
sqlite3_session **ppSession /* OUT: New session object */
);
void sqlite3session_delete(sqlite3_session *pSession);
int sqlite3session_enable(sqlite3_session *pSession, int bEnable);
int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect);

int sqlite3changeset_start(
sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */
int nChangeset, /* Size of changeset blob in bytes */
void *pChangeset /* Pointer to blob containing changeset */
);
int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter);
int sqlite3changeset_next(sqlite3_changeset_iter *pIter);
int sqlite3changeset_op(
sqlite3_changeset_iter *pIter, /* Iterator object */
const sqlite3_char **pzTab, /* OUT: Pointer to table name */
int *pnCol, /* OUT: Number of columns in table */
int *pOp, /* OUT: SQLITE_INSERT, DELETE or UPDATE */
int *pbIndirect /* OUT: True for an 'indirect' change */
);
int sqlite3changeset_old(
sqlite3_changeset_iter *pIter, /* Changeset iterator */
int iVal, /* Column number */
sqlite3_value **ppValue /* OUT: Old value (or NULL pointer) */
);
int sqlite3changeset_new(
sqlite3_changeset_iter *pIter, /* Changeset iterator */
int iVal, /* Column number */
sqlite3_value **ppValue /* OUT: Old value (or NULL pointer) */
);
int sqlite3changeset_apply(
sqlite3 *db, /* Apply change to "main" db of this handle */
int nChangeset, /* Size of changeset in bytes */
void *pChangeset, /* Changeset blob */
int(*xFilter)(
void *pCtx, /* Copy of sixth arg to _apply() */
const char *zTab /* Table name */
),
int(*xConflict)(
void *pCtx, /* Copy of sixth arg to _apply() */
int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
sqlite3_changeset_iter *p /* Handle describing change and conflict */
),
void *pCtx /* First argument passed to xConflict */
);

int sqlite3changeset_invert(
int nIn, const void *pIn, /* Input changeset */
int *pnOut, void **ppOut /* OUT: Inverse of input */
);
int sqlite3session_patchset(
sqlite3_session *pSession, /* Session object */
int *pnPatchset, /* OUT: Size of buffer at *ppPatchset */
void **ppPatchset /* OUT: Buffer containing patchset */
);
int sqlite3session_changeset(
sqlite3_session *pSession, /* Session object */
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
void **ppChangeset /* OUT: Buffer containing changeset */
);
int sqlite3session_isempty(sqlite3_session *pSession);
int sqlite3session_attach(
sqlite3_session *pSession, /* Session object */
const sqlite3_char *zTab /* Table name */
);
int sqlite3session_diff(
sqlite3_session *pSession,
const sqlite3_char *zFromDb,
const sqlite3_char *zTbl,
sqlite3_char **pzErrMsg
);
4 changes: 3 additions & 1 deletion sqlite3/assets/wasm/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ include(FetchContent)
FetchContent_Declare(
sqlite3
# NOTE: When changing this, also update `test/wasm/sqlite3_test.dart`
URL https://sqlite.org/2025/sqlite-autoconf-3490100.tar.gz
URL https://sqlite.org/2025/sqlite-amalgamation-3490100.zip
DOWNLOAD_EXTRACT_TIMESTAMP NEW
)

Expand Down Expand Up @@ -80,6 +80,8 @@ macro(base_sqlite3_target name debug crypto)
${flags}
-o ${clang_output}
-I ${PROJECT_SOURCE_DIR} -I ${sqlite3_SOURCE_DIR}
-DSQLITE_ENABLE_SESSION
-DSQLITE_ENABLE_PREUPDATE_HOOK
-D_HAVE_SQLITE_CONFIG_H
-D__WASM__
-mcpu=generic
Expand Down
9 changes: 9 additions & 0 deletions sqlite3/assets/wasm/bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,12 @@ import_dart("function_compare") extern int dartXCompare(void *id, int lengthA,

import_dart("localtime") extern int dartLocalTime(int64_t time,
struct tm *result);
import_dart("changeset_apply_filter") extern int dartChangesetApplyFilter(
void* context,
const char *zTab
);
import_dart("changeset_apply_conflict") extern int dartChangesetApplyConflict(
void* context,
int eConflict,
sqlite3_changeset_iter *p
);
11 changes: 11 additions & 0 deletions sqlite3/assets/wasm/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,14 @@ SQLITE_API int dart_sqlite3_create_collation(sqlite3 *db, const char *zName,
SQLITE_API int dart_sqlite3_db_config_int(sqlite3 *db, int op, int arg) {
return sqlite3_db_config(db, op, arg);
}

SQLITE_API int dart_sqlite3changeset_apply(sqlite3 *db, int nChangeset, void *pChangeset, void *pCtx, bool filter) {
return sqlite3changeset_apply(
db,
nChangeset,
pChangeset,
filter ? &dartChangesetApplyFilter : 0,
&dartChangesetApplyConflict,
pCtx
);
}
2 changes: 2 additions & 0 deletions sqlite3/assets/wasm/sqlite_cfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
#define SQLITE_ENABLE_FTS5 1
#define SQLITE_ENABLE_MATH_FUNCTIONS 1
#define SQLITE_ENABLE_RTREE 1
#define SQLITE_ENABLE_SESSION 1
#define SQLITE_ENABLE_PREUPDATE_HOOK 1

// Disable things we don't need
#define SQLITE_OMIT_DEPRECATED
Expand Down
1 change: 1 addition & 0 deletions sqlite3/lib/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ library;

export 'src/constants.dart';
export 'src/database.dart';
export 'src/session.dart';
export 'src/exception.dart';
export 'src/functions.dart';
export 'src/in_memory_vfs.dart' show InMemoryFileSystem;
Expand Down
21 changes: 18 additions & 3 deletions sqlite3/lib/src/database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -256,13 +256,28 @@ enum SqliteUpdateKind {
// used in the sqlite3_web protocol.

/// Notification for a new row being inserted into the database.
insert,
insert(SQLITE_INSERT),

/// Notification for a row being updated.
update,
update(SQLITE_UPDATE),

/// Notification for a row being deleted.
delete
delete(SQLITE_DELETE);

/// The raw code to identify this update kind.
final int code;

const SqliteUpdateKind(this.code);

/// Attempts to extract a [SqliteUpdateKind] from the raw [flags].
static SqliteUpdateKind? fromCode(int code) {
return switch (code) {
SQLITE_INSERT => insert,
SQLITE_UPDATE => update,
SQLITE_DELETE => delete,
_ => null,
};
}
}

/// A data change notification from sqlite.
Expand Down
Loading
Loading