Skip to content

fix(android): allow user sqliteFlags to override performanceMode defaults#402

Merged
ospfranco merged 1 commit into
OP-Engineering:mainfrom
pbbadenhorst:fix/android-sqliteflags-override
May 14, 2026
Merged

fix(android): allow user sqliteFlags to override performanceMode defaults#402
ospfranco merged 1 commit into
OP-Engineering:mainfrom
pbbadenhorst:fix/android-sqliteflags-override

Conversation

@pbbadenhorst
Copy link
Copy Markdown
Contributor

@pbbadenhorst pbbadenhorst commented May 14, 2026

Problem

On Android, user-supplied sqliteFlags from package.json cannot override flags that performanceMode: true adds. With

"op-sqlite": {
  "performanceMode": true,
  "sqliteFlags": "-DSQLITE_DQS=3"
}

a query that relies on DQS string literals (SELECT 1 FROM t WHERE col = "some-string") succeeds on iOS but fails on Android with no such column: "some-string". iOS works because the podspec builds OTHER_CFLAGS as optimizedCflags + sqliteFlags, so user flags arrive after the defaults and win under clang's last-define-wins rule. Android does not have that property today.

Root cause

Two-part ordering bug on the clang command line:

  1. android/build.gradle puts the performanceMode flags on gradle's external-native-build cFlags, which the Android Gradle Plugin appends to CMAKE_C_FLAGS. They arrive late on the clang command line.
  2. android/CMakeLists.txt adds user sqliteFlags via add_definitions(${SQLITE_FLAGS_LIST}). These land in COMPILE_DEFINITIONS, which CMake emits early on the command line — and, importantly, sorts alphabetically before emission.

Net effect: user -DSQLITE_DQS=3 is sorted into early COMPILE_DEFINITIONS, the performanceMode -DSQLITE_DQS=0 lands later in CMAKE_C_FLAGS, and clang takes the late one. The library default silently beats the user override.

The documented intent of sqliteFlags (https://op-engineering.github.io/op-sqlite/docs/installation/) is that user flags customize the build. Today on Android they can only add flags, not override performanceMode ones.

Fix

Route both library defaults (performanceMode, fts5, rtree) and user sqliteFlags through the same CMake channel — add_compile_options — with defaults declared first and user flags last:

  • android/build.gradle now collects all default flags into a defaultSqliteFlags list at config time and passes them to CMake as -DDEFAULT_SQLITE_FLAGS=.... The old cFlags += [...] blocks for performanceMode, enableFTS5 and enableRtree are removed.
  • android/CMakeLists.txt calls add_compile_options(${DEFAULT_SQLITE_FLAGS_LIST}) and then add_compile_options(${SQLITE_FLAGS_LIST}). add_compile_options preserves declaration order; add_definitions does not (CMake sorts COMPILE_DEFINITIONS alphabetically, so add_definitions cannot reliably encode override intent — e.g. a default =1 would beat a user =0 due to character order).

Defaults remain unchanged when no user flags are set. When user flags are set, they land after the defaults on the clang command line and win under clang's last-define-wins rule. This matches the iOS behavior.

Verification

Built the example app's native target twice on arm64-v8a and inspected android/.cxx/Debug/*/arm64-v8a/compile_commands.json for sqlite3.c.

Default (no sqliteFlags, performanceMode: true, fts5: true, rtree: true):

... -DSQLITE_DQS=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1
    -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_OMIT_DEPRECATED=1
    -DSQLITE_OMIT_PROGRESS_CALLBACK=1 -DSQLITE_OMIT_SHARED_CACHE=1 -DSQLITE_USE_ALLOCA=1
    -DSQLITE_THREADSAFE=1 -DSQLITE_ENABLE_FTS5=1 -DSQLITE_ENABLE_RTREE=1 ...

All existing defaults still applied. -DSQLITE_DQS=0 present.

Override ("sqliteFlags": "-DSQLITE_DQS=3 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=0"):

... -DSQLITE_DQS=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1
    ... -DSQLITE_ENABLE_RTREE=1 -DSQLITE_DQS=3 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=0

Both override flags appear after their defaults. Notably, -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=0 overrides =1 — this is the case add_definitions would have broken (since '0' < '1' alphabetically), confirming the need for add_compile_options.

Scope notes

  • No behavior change for users who don't set sqliteFlags — all existing defaults remain.
  • iOS podspec untouched (already correct).
  • No product-level tests added; this is a build-system fix verified via the generated compile commands and a clean rebuild of the example app.
  • Docs updated in docs/docs/installation.md to mention override semantics on both platforms.

@pbbadenhorst pbbadenhorst force-pushed the fix/android-sqliteflags-override branch from be91294 to 16ffb0c Compare May 14, 2026 18:33
@ospfranco
Copy link
Copy Markdown
Contributor

Not a cmake expert, so this is really useful to have debugged. Thanks!

@ospfranco ospfranco merged commit 08178d4 into OP-Engineering:main May 14, 2026
10 checks passed
@pbbadenhorst pbbadenhorst deleted the fix/android-sqliteflags-override branch May 15, 2026 20:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants