### Setup Function Example Source: https://op-engineering.github.io/op-sqlite/docs/tokenizers This snippet shows a setup function within a React useEffect hook. It's used for initializing resources or performing side effects after the component mounts. The console.warn(res) indicates a placeholder for handling a result. ```javascript console.warn(res); }; setup(); }, []); ``` -------------------------------- ### Install op-sqlite with Expo Source: https://op-engineering.github.io/op-sqlite/docs/installation Install the op-sqlite library using Expo's command. Ensure to pre-build your app if using Expo Go. ```bash npx expo install @op-engineering/op-sqlite npx expo prebuild --clean ``` -------------------------------- ### iOS Podfile Patching Example Source: https://op-engineering.github.io/op-sqlite/docs/installation This is a conceptual example of how you might patch a pod's configuration within a `pre_install` hook in your Podfile. It's intended for situations where you need to modify a pod's dependencies, such as preventing `expo-updates` from depending on the sqlite pod. ```ruby pre_install do |installer| installer.pod_targets.each do |pod| if pod.name.eql?('expo-updates') # Modify the configuration of the pod so it doesn't depend on the sqlite pod end end end ``` -------------------------------- ### Install op-sqlite with npm Source: https://op-engineering.github.io/op-sqlite/docs/installation Use this command to install the op-sqlite library in your React Native project. ```bash npm i -s @op-engineering/op-sqlite ``` -------------------------------- ### Initialize and Use OP-SQLite Key-Value Storage Source: https://op-engineering.github.io/op-sqlite/docs/key_value_storage Demonstrates how to initialize the Storage class with optional location and encryption key, and perform basic key-value operations like getting, setting, and clearing items. Use this for simple data persistence needs. ```javascript import { Storage } from '@op-engineering/op-sqlite'; // Storage is backed by it's own database // You can set the location like any other op-sqlite database const storage = new Storage({ location: 'storage', // Optional, see location param on normal databases encryptionKey: 'myEncryptionKey', // Optional, only used when used against SQLCipher }); const item = storage.getItemSync('foo'); const item2 = await storage.getItem('foo'); await storage.setItem('foo', 'bar'); storage.setItemSync('foo', 'bar'); const allKeys = storage.getAllKeys(); // Clears the internal table storage.clear(); ``` -------------------------------- ### Add optional web dependency Source: https://op-engineering.github.io/op-sqlite/docs/installation Install the optional @sqlite.org/sqlite-wasm dependency if you are using op-sqlite on the web. ```bash yarn add @sqlite.org/sqlite-wasm ``` -------------------------------- ### Implement Word Tokenizer in C++ Source: https://op-engineering.github.io/op-sqlite/docs/tokenizers Provide the C++ implementation for your custom tokenizer, including `xCreate`, `xDelete`, and `xTokenize` functions. This example defines a basic word tokenizer that splits text by alphanumeric characters. ```cpp #include "tokenizers.h" #include #include #include namespace opsqlite { fts5_api *fts5_api_from_db(sqlite3 *db) { fts5_api *pRet = 0; sqlite3_stmt *pStmt = 0; if (SQLITE_OK == sqlite3_prepare_v2(db, "SELECT fts5(?1)", -1, &pStmt, 0)) { sqlite3_bind_pointer(pStmt, 1, (void *)&pRet, "fts5_api_ptr", NULL); sqlite3_step(pStmt); } sqlite3_finalize(pStmt); return pRet; } class WordTokenizer { public: WordTokenizer() = default; ~WordTokenizer() = default; }; // Define `xCreate`, which initializes the tokenizer int wordTokenizerCreate(void *pUnused, const char **azArg, int nArg, Fts5Tokenizer **ppOut) { auto tokenizer = std::make_unique(); *ppOut = reinterpret_cast( tokenizer.release()); // Cast to Fts5Tokenizer* return SQLITE_OK; } // Define `xDelete`, which frees the tokenizer void wordTokenizerDelete(Fts5Tokenizer *pTokenizer) { delete reinterpret_cast(pTokenizer); } // Define `xTokenize`, which performs the actual tokenization int wordTokenizerTokenize(Fts5Tokenizer *pTokenizer, void *pCtx, int flags, const char *pText, int nText, int (*xToken)(void *, int, const char *, int, int, int)) { int start = 0; int i = 0; while (i <= nText) { if (i == nText || !std::isalnum(static_cast(pText[i]))) { if (start < i) { // Found a token int rc = xToken(pCtx, 0, pText + start, i - start, start, i); if (rc != SQLITE_OK) return rc; } start = i + 1; } i++; } return SQLITE_OK; } int opsqlite_word_tokenizer_init(sqlite3 *db, char **error, sqlite3_api_routines const *api) { fts5_tokenizer wordtokenizer = {wordTokenizerCreate, wordTokenizerDelete, wordTokenizerTokenize}; fts5_api *ftsApi = (fts5_api *)fts5_api_from_db(db); if (ftsApi == NULL) return SQLITE_ERROR; return ftsApi->xCreateTokenizer(ftsApi, "word_tokenizer", NULL, &wordtokenizer, NULL); } } // namespace opsqlite ``` -------------------------------- ### Blob Data Handling Source: https://op-engineering.github.io/op-sqlite/docs/api Supports blobs via `ArrayBuffer` or typed arrays. This example demonstrates inserting and retrieving binary data. ```javascript db = open({ name: 'blobs', }); await db.execute('DROP TABLE IF EXISTS BlobTable;'); await db.execute( 'CREATE TABLE BlobTable ( id INT PRIMARY KEY, name TEXT NOT NULL, content BLOB) STRICT;' ); let binaryData = new Uint8Array(2); binaryData[0] = 42; await db.execute(`INSERT OR REPLACE INTO BlobTable VALUES (?, ?, ?);`, [ 1, 'myTestBlob', binaryData, ]); const result = await db.execute('SELECT content FROM BlobTable'); const finalUint8 = new Uint8Array(result.rows[0].content); ``` -------------------------------- ### Create FTS5 Table with Custom Tokenizer Source: https://op-engineering.github.io/op-sqlite/docs/tokenizers Use the custom tokenizer when creating an FTS5 virtual table in your application. This example demonstrates creating a table and inserting/querying data. ```javascript let db = open({ name: 'tokenizers.sqlite', encryptionKey: 'test', }); // inside your component or wherever you initialize your database // THIS IS SAMPLE CODE, use your head when creating your tables useEffect(() => { let setup = async () => { await db.execute( `CREATE VIRTUAL TABLE tokenizer_table USING fts5(content, tokenize = 'word_tokenizer');` ); await db.execute('INSERT INTO tokenizer_table(content) VALUES (?)', [ 'This is a test document', ]); const res = await db.execute( 'SELECT content FROM tokenizer_table WHERE content MATCH ?', ['test'] ); ``` -------------------------------- ### Get OP SQLite Database Path Source: https://op-engineering.github.io/op-sqlite/docs/debugging Use this snippet to get the file path of your SQLite database. The path is logged and copied to the clipboard for easy access. Ensure the 'open' function is imported. ```javascript const db = open({ name: 'dbPath.sqlite' }); await db.execute('CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT)'); const path = db.getDbPath(); console.warn(path); Clipboard.setString(path); ``` -------------------------------- ### Load SQLite Extension on Android and iOS Source: https://op-engineering.github.io/op-sqlite/docs/api Use this code to load SQLite extensions. On Android, pass the canonical name of the .so file. On iOS, first call `getDylibPath` to get the runtime path of the .xcframework, then load the extension. You can optionally specify a custom entry point function. ```javascript import {open, getDylibPath} from '@op-sqlite/op-engineering'; const db = open(...); let path = "libcrsqlite" // in Android it will be the name of the .so if (Platform.os == "ios") { path = getDylibPath("io.vlcn.crsqlite", "crsqlite"); // You need to get the bundle name from the .framework/plist.info inside of the .xcframework you created and then the canonical name inside the same plist } // Extensions usually have a default entry point to be loaded, if the documentation says nothing, you should assume no entry point change db.loadExtension(path); // Others might need a different entry point function, you can pass it as a second argument db.loadExtension(path, "entry_function_of_the_extension"); ``` -------------------------------- ### Subscribe to Specific Row Updates Source: https://op-engineering.github.io/op-sqlite/docs/reactive_queries This example shows how to subscribe to changes in a specific row. You first need to retrieve the row's ID, then use it in the `fireOn` configuration. The callback will be invoked whenever the specified row is updated. Transactions are required to trigger updates. ```javascript let rowid = db .execute('SELECT rowid WHERE id = ? FROM Users', [123]) .item(0).rowid; let unsubscribe = db.reactiveExecute({ query: 'SELECT * WHERE id = ? FROM Users', arguments: ['123'], fireOn: [ { table: 'Users', ids: [rowId], }, ], callback: (userResponse) => { console.log(usersReponse.item(0)); }, }); ``` -------------------------------- ### Complex Reactive Query Example Source: https://op-engineering.github.io/op-sqlite/docs/reactive_queries This snippet demonstrates a complex reactive query involving joins and aggregations. It subscribes to changes in both 'customers' and 'orders' tables. The query re-runs entirely upon detected changes, optimized by op-sqlite. Transactions are required to trigger updates. ```javascript let unsubscribe = db.reactiveExecute({ query: `SELECT c.customer_id, c.first_name, c.last_name, c.email, COUNT(o.order_id) AS total_orders, SUM(o.total_amount) AS total_spent FROM customers c LEFT JOIN orders o ON c.customer_id = o.customer_id GROUP BY c.customer_id, c.first_name, c.last_name, c.email ORDER BY total_spent DESC`, arguments: [], fireOn: [ { table: 'customers', }, { table: 'orders', }, ], callback: (data: any) => { // data = normal op-sqlite response }, }); ``` -------------------------------- ### Get Database File Path Source: https://op-engineering.github.io/op-sqlite/docs/api Retrieve the file path of the SQLite database on disk using `db.getDbPath()`. This is helpful for debugging or attaching the database file to bug reports. ```javascript const path = db.getDbPath(); ``` -------------------------------- ### Download and Open Database from Server Source: https://op-engineering.github.io/op-sqlite/docs/configuration Download a database from a server directly to the default directory and then open it. This is pseudo-code and requires proper FetchBlob implementation. ```javascript import FetchBlob from 'react-native-fetch-blob'; import { IOS_LIBRARY_PATH, // Default iOS ANDROID_DATABASE_PATH, // Default Android } from '@op-engineering/op-sqlite'; // Pseudo-code replace with the proper calls however you download the database async function downloadAndMove() { await FetchBlob.download( `/sample.sqlite`, Platform.OS === 'ios' ? IOS_LIBRARY_PATH : ANDROID_DATABASE_PATH ); } openDb = () => { const db = open({ name: 'sample.sqlite' }); const users = await db.execute('SELECT * FROM User'); console.log('users', users.rows?._array); }; ``` -------------------------------- ### Open a Local Database with Remote Sync Source: https://op-engineering.github.io/op-sqlite/docs/Libsql/start Use `openSync` to create a local database that syncs with a remote Turso database. Optional parameters include `syncInterval` and `encryptionKey`. ```javascript import { openSync } from '@op-engineering/op-sqlite'; const remoteDb = openSync({ name: 'myDb.sqlite', url: 'url', authToken: 'token', syncInterval: 1, // Optional, in seconds encryptionKey: 'my encryption key', // Optional, will encrypt the database on device. Will add overhead to your queries }); ``` -------------------------------- ### Re-compile Libsql C Binary for iOS Source: https://op-engineering.github.io/op-sqlite/docs/Libsql/updating Run this command in the `libsql/bindings/c` directory to recompile the C binary for iOS. ```bash make ios ``` -------------------------------- ### Open Async (Web) Source: https://op-engineering.github.io/op-sqlite/docs/api On web platforms, opening a database connection is async-only and requires OPFS (Origin Private File System). ```APIDOC ## Open Async (Web) ### Description On web platforms, opening a database connection is async-only and requires OPFS (Origin Private File System). Use `openAsync()` on web as `open()` intentionally throws. ### Method `openAsync(options: { name: string })` ### Parameters #### Request Body - **name** (string) - Required - The name of the database file. ### Request Example ```javascript import { openAsync } from '@op-engineering/op-sqlite'; export const db = await openAsync({ name: 'myDb.sqlite', }); ``` ``` -------------------------------- ### Re-compile Libsql C Binary for Android Source: https://op-engineering.github.io/op-sqlite/docs/Libsql/updating Run this command in the `libsql/bindings/c` directory to recompile the C binary for Android. ```bash make android ``` -------------------------------- ### Enable Turso Backend in package.json Source: https://op-engineering.github.io/op-sqlite/docs/Turso/start Configure your package.json to enable the Turso backend for op-sqlite. Ensure you do not use `turso` with `sqlcipher` or `libsql` simultaneously. ```json "op-sqlite": { "turso": true } ``` -------------------------------- ### Remote and Sync Open (Libsql/Turso) Source: https://op-engineering.github.io/op-sqlite/docs/api For remote or synchronized database scenarios, enable the `libsql` or `turso` backend and use `openRemote` or `openSync`. ```APIDOC ## Remote and Sync Open (Libsql/Turso) ### Description For remote or synchronized database scenarios, enable the `libsql` or `turso` backend in your package configuration, then use `openRemote` or `openSync`. ### Methods `openRemote(options: { url: string, authToken: string })` `openSync(options: { name: string, url: string, authToken: string })` ### Parameters #### `openRemote` Request Body - **url** (string) - Required - The URL of the remote database. - **authToken** (string) - Required - The authentication token for the remote database. #### `openSync` Request Body - **name** (string) - Required - The name of the database file. - **url** (string) - Required - The URL for synchronization. - **authToken** (string) - Required - The authentication token for synchronization. ### Request Example ```javascript import { openRemote, openSync } from '@op-engineering/op-sqlite'; const remoteDb = openRemote({ url: 'url', authToken: 'token', }); const syncDb = openSync({ name: 'myDb.sqlite', url: 'url', authToken: 'token', }); // Force a sync round when needed syncDb.sync(); ``` ``` -------------------------------- ### Open a Local Syncing Turso Database Source: https://op-engineering.github.io/op-sqlite/docs/Turso/start Use `openSync` to create or open a local SQLite database that synchronizes with a remote Turso database. Provide a database name, URL, and authentication token. An optional remote encryption key can also be specified. ```typescript import { openSync } from '@op-engineering/op-sqlite'; const syncDb = openSync({ name: 'myDb.sqlite', url: 'url', authToken: 'token', remoteEncryptionKey: 'optional-remote-encryption-key', }); ``` -------------------------------- ### Open In-Memory Database Source: https://op-engineering.github.io/op-sqlite/docs/configuration Use ':memory:' as the location for in-memory databases, which are faster as they avoid disk I/O. ```javascript import { open } from '@op-engineering/op-sqlite'; const largeDb = open({ name: 'inMemoryDb', location: ':memory:', }); ``` -------------------------------- ### Open SQLite Database Connection (Async Web) Source: https://op-engineering.github.io/op-sqlite/docs/api Opens a connection to a SQLite database asynchronously, specifically for web environments using OPFS. Use this instead of `open()` on the web as `open()` intentionally throws. ```javascript import { openAsync } from '@op-engineering/op-sqlite'; export const db = await openAsync({ name: 'myDb.sqlite', }); ``` -------------------------------- ### Force Static Compilation for op-sqlite in Podfile Source: https://op-engineering.github.io/op-sqlite/docs/installation Modify your Podfile to force op-sqlite to compile as a static library. This is a workaround for issues caused by `use_frameworks`. ```ruby pre_install do |installer| installer.pod_targets.each do |pod| if pod.name.eql?('op-sqlite') def pod.build_type Pod::BuildType.static_library end end end end ``` -------------------------------- ### Include SQLite Headers (Android/iOS) Source: https://op-engineering.github.io/op-sqlite/docs/cpp_usage Include the SQLite header files differently based on the platform. Use the `` path for Android and `` for iOS. ```c++ #ifdef __ANDROID__ #include #include #else #include #include #endif ``` -------------------------------- ### Enable libsql in package.json Source: https://op-engineering.github.io/op-sqlite/docs/Libsql/start Modify your package.json to enable the libsql implementation for op-sqlite. Ensure you do not use sqlcipher and libsql simultaneously. ```json "op-sqlite": { "libsql": true } ``` -------------------------------- ### Use Prepared Statements Source: https://op-engineering.github.io/op-sqlite/docs/api Creates and uses prepared statements for SQL queries that are executed multiple times with different arguments. This optimizes performance by parsing the query only once. ```javascript const statement = db.prepareStatement('SELECT * FROM User WHERE name = ?;'); // bind the variables in the order they appear await statement.bind(['Oscar']); // Or use the bindsync version statement.bindSync(['Luis']); let results1 = await statement.execute(); await statement.bind(['Carlos']); let results2 = await statement.execute(); ``` -------------------------------- ### Configure op-sqlite in package.json Source: https://op-engineering.github.io/op-sqlite/docs/installation Customize op-sqlite build options by adding the 'op-sqlite' key to your package.json. This is necessary for features like SQLCipher, cr-sqlite, performance mode, and more. Ensure this configuration is in the monorepo root package.json if applicable. ```json { // ... the rest of your package.json // All the keys are optional, see the usage below "op-sqlite": { "sqlcipher": false // "crsqlite": false, // "performanceMode": true, // "iosSqlite": false, // "sqliteFlags": "-DSQLITE_DQS=0 -DSQLITE_MY_FLAG=1", // "fts5": true, // "rtree": true, // "libsql": true, // "turso": true, // "sqliteVec": true, // "tokenizers": ["simple_tokenizer"] } } ``` -------------------------------- ### Open Remote and Sync Database Connections (Libsql/Turso) Source: https://op-engineering.github.io/op-sqlite/docs/api Opens connections for remote or synchronized SQLite databases using Libsql or Turso backends. `openRemote` is for remote access, and `openSync` is for synchronized access. `sync()` can be called to force a sync round. ```javascript import { openRemote, openSync } from '@op-engineering/op-sqlite'; const remoteDb = openRemote({ url: 'url', authToken: 'token', }); const syncDb = openSync({ name: 'myDb.sqlite', url: 'url', authToken: 'token', }); // Force a sync round when needed syncDb.sync(); ``` -------------------------------- ### Load SQL Dump Files Source: https://op-engineering.github.io/op-sqlite/docs/api Efficiently load large sets of SQL statements or restore database backups by loading directly from a `.sql` file. This minimizes JavaScript-C++ overhead. ```javascript const { rowsAffected, commands } = await db.loadFile( '/absolute/path/to/file.sql' ); ``` -------------------------------- ### Open Database with Platform-Specific Absolute Paths Source: https://op-engineering.github.io/op-sqlite/docs/configuration Utilize exported paths for iOS and Android to specify absolute locations. Ensure fallbacks for potential null paths. ```javascript import { IOS_LIBRARY_PATH, // Default iOS IOS_DOCUMENT_PATH, ANDROID_DATABASE_PATH, // Default Android ANDROID_FILES_PATH, ANDROID_EXTERNAL_FILES_PATH, // Android SD Card open, } from '@op-engineering/op-sqlite'; const db = open({ name: 'myDb', location: Platform.OS === 'ios' ? IOS_LIBRARY_PATH : ANDROID_DATABASE_PATH, }); ``` ```javascript const db = open({ name: 'myDB', location: Platform.OS === 'ios' ? IOS_LIBRARY_PATH : ANDROID_EXTERNAL_FILES_PATH ?? ANDROID_DATABASE_PATH, }); ``` ```javascript const db = open({ name: 'myDB', location: Platform.OS === 'ios' ? IOS_LIBRARY_PATH : `${ANDROID_EXTERNAL_FILES_PATH}/databases/`, }); ``` -------------------------------- ### Set Up Commit and Rollback Hooks Source: https://op-engineering.github.io/op-sqlite/docs/api Implement commit and rollback hooks to execute logic when a transaction is committed or rolled back. These hooks are useful for logging or performing cleanup operations. ```javascript // will fire whenever a transaction commits db.commitHook(() => { console.log('Transaction committed!'); }); db.rollbackHook(() => { console.log('Transaction rolled back!'); }); // will fire the commit hook db.transaction(async (tx) => { tx.execute( 'INSERT INTO "User" (id, name, age, networth) VALUES(?, ?, ?, ?)', [id, name, age, networth] ); }); // will fire the rollback hook try { await db.transaction(async (tx) => { throw new Error('Test Error'); }); } catch (e) { // intentionally left blank } ``` -------------------------------- ### Configure Tokenizers in package.json Source: https://op-engineering.github.io/op-sqlite/docs/tokenizers Declare custom tokenizers in your `package.json` to enable them for FTS5. Ensure `fts5` is enabled. ```json "op-sqlite": { // Leave whatever configuration you already have "fts5": true, // fts needs to be enabled "tokenizers": ["word_tokenizer"] // declare which tokenizers you will create } ``` -------------------------------- ### Execute with Host Objects Source: https://op-engineering.github.io/op-sqlite/docs/api Allows returning HostObjects from queries. HostObjects are created in C++ and converted to JavaScript values only when accessed, which can be beneficial for performance when dealing with large datasets where only a subset of data is accessed. ```APIDOC ## Execute with Host Objects ### Description Allows returning HostObjects from queries. HostObjects are created in C++ and converted to JavaScript values only when accessed, which can be beneficial for performance when dealing with large datasets where only a subset of data is accessed. Be mindful of HostObject limitations (e.g., no spread, no logging). ### Method `executeWithHostObjects(sql: string)` ### Parameters #### Request Body - **sql** (string) - Required - The SQL query string to execute. ### Request Example ```javascript let res = await db.executeWithHostObjects('select * from USERS'); ``` ``` -------------------------------- ### Move and Open Assets Database Source: https://op-engineering.github.io/op-sqlite/docs/configuration Use `moveAssetsDatabase` to move a database from assets to the default location, then open it. This function is idempotent. ```javascript import { moveAssetsDatabase, open } from '@op-engineering/op-sqlite'; const openAssetsDb = async () => { const moved = await moveAssetsDatabase({ filename: 'sample.sqlite' }); if (!moved) { throw new Error('Could not move assets database'); } const db = open({ name: 'sample.sqlite' }); const users = await db.execute('SELECT * FROM User'); console.log('users', users.rows); }; ``` -------------------------------- ### Open Database with Relative Path Source: https://op-engineering.github.io/op-sqlite/docs/configuration Use relative paths to navigate within the default location. Note iOS sandboxing restrictions. ```javascript import { open } from '@op-engineering/op-sqlite'; const db = open({ name: 'myDB', location: '../files/databases', }); ``` -------------------------------- ### Link React Native Assets Source: https://op-engineering.github.io/op-sqlite/docs/configuration Run the react-native-asset command to link assets, including databases, into your project. ```bash npx react-native-asset@latest ``` -------------------------------- ### Execute SQL with Host Objects Source: https://op-engineering.github.io/op-sqlite/docs/api Executes an SQL query and returns results as HostObjects. This is efficient for large datasets where only a subset of data is accessed, as conversions from C++ to JS values happen only upon access. ```javascript let res = await db.executeWithHostObjects('select * from USERS'); ``` -------------------------------- ### Trigger Manual Sync for Local Database Source: https://op-engineering.github.io/op-sqlite/docs/Turso/start Call the `sync()` method on a locally synced database object to manually initiate a synchronization cycle with the remote Turso database. The API for this method is consistent with libsql-backed remote/sync usage. ```typescript syncDb.sync(); ``` -------------------------------- ### Link OP SQLite in CMakeLists.txt (Android) Source: https://op-engineering.github.io/op-sqlite/docs/cpp_usage Add this to your `CMakeLists.txt` to link the OP SQLite library. Ensure `op-engineering_op-sqlite` is found and then link it along with other necessary libraries. ```cmake find_package(op-engineering_op-sqlite REQUIRED CONFIG) # Link all libraries together target_link_libraries( ${PACKAGE_NAME} ${LOG_LIB} android op-engineering_op-sqlite::op-sqlite ) ``` -------------------------------- ### Enable Memory Mapping in SQLite Source: https://op-engineering.github.io/op-sqlite/docs/configuration Turn on memory mapping by executing the PRAGMA mmap_size statement after opening a database. A value of 0 disables it, while any other number enables it with the specified cache size. Be aware that errors during queries may cause application crashes. ```javascript const db = open({ name: 'mydb.sqlite', }); // 0 turns off memory mapping, any other number enables it with the cache size await db.execute('PRAGMA mmap_size=268435456'); ``` -------------------------------- ### Configure react-native.config.js for Assets Source: https://op-engineering.github.io/op-sqlite/docs/configuration Configure the react-native.config.js file to include the assets directory for linking. ```javascript module.exports = { assets: ['./assets/'], }; ``` -------------------------------- ### Raw Execution Source: https://op-engineering.github.io/op-sqlite/docs/api Provides a simplified execution method that returns an array of scalars, bypassing the creation of objects with keys. This can be faster than regular execution when keys are not needed. ```APIDOC ## Raw Execution ### Description Provides a simplified execution method that returns an array of scalars, bypassing the creation of objects with keys. This can be faster than regular execution when keys are not needed. ### Method `executeRaw(sql: string)` ### Parameters #### Request Body - **sql** (string) - Required - The SQL query string to execute. ### Response Example ```javascript let result = await db.executeRaw('SELECT * FROM Users;'); // result = [[123, 'Katie', ...]] ``` ``` -------------------------------- ### Android JNILibs Directory Structure Source: https://op-engineering.github.io/op-sqlite/docs/api Illustrates the required directory structure for loading SQLite extensions on Android. Compiled extensions (.so files) should be placed in the `jniLibs` folder within the Android project, organized by architecture (e.g., `arm64-v8a`, `armeabi-v7a`). ```plaintext /android /app /src /main /jniLibs /arm64-v8a libcrsqlite.so /armeabi-v7a libcrsqlite.so /x86 libcrsqlite.so /x86_64 libcrsqlite.so ``` -------------------------------- ### Execute Batch of Commands Source: https://op-engineering.github.io/op-sqlite/docs/api Execute a series of commands in a single call, automatically wrapped in a transaction. If any statement fails, all are rolled back. ```javascript const commands = [ ['CREATE TABLE TEST (id integer)'], ['INSERT INTO TEST (id) VALUES (?)', [1]], [('INSERT INTO TEST (id) VALUES (?)', [2])], [('INSERT INTO TEST (id) VALUES (?)', [[3], [4], [5], [6]])], ]; const res = await db.executeBatch(commands); console.log(`Batch affected ${result.rowsAffected} rows`); ``` -------------------------------- ### Enable iOS SQLite Flag in package.json Source: https://op-engineering.github.io/op-sqlite/docs/installation Enable the `iosSqlite` flag in your `package.json` for op-sqlite. This workaround is for Expo packages and uses the iOS embedded version of SQLite, which may be outdated and does not support extension loading. ```json "op-sqlite": { "iosSqlite": true } ``` -------------------------------- ### Force Sync a Libsql Database Source: https://op-engineering.github.io/op-sqlite/docs/Libsql/start Call the `sync()` method on a libsql database instance to manually force a synchronization with the remote database. This method is only available for libsql databases. ```javascript remoteDb.sync(); ``` -------------------------------- ### Execute Raw SQL Query Source: https://op-engineering.github.io/op-sqlite/docs/api Executes a raw SQL query and returns results as an array of scalars, omitting keys. This method is faster than regular execution when keys are not needed. ```javascript let result = await db.executeRaw('SELECT * FROM Users;'); // result = [[123, 'Katie', ...]] ``` -------------------------------- ### Open Database Connection Source: https://op-engineering.github.io/op-sqlite/docs/api Opens a connection to a SQLite database. It's recommended to open only one connection per App session and reuse it throughout the application's lifecycle to avoid latency. ```APIDOC ## Open Database Connection ### Description Opens a connection to a SQLite database. It's recommended to open only one connection per App session and reuse it throughout the application's lifecycle to avoid latency. ### Method `open(options: { name: string })` ### Parameters #### Request Body - **name** (string) - Required - The name of the database file. ### Request Example ```javascript import { open } from '@op-engineering/op-sqlite'; export const db = open({ name: 'myDb.sqlite', }); ``` ``` -------------------------------- ### Prepared Statements Source: https://op-engineering.github.io/op-sqlite/docs/api Utilizes prepared statements for queries that are expensive but can be reused with different arguments. This approach optimizes query planning and execution, especially for read operations. ```APIDOC ## Prepared Statements ### Description Utilizes prepared statements for queries that are expensive but can be reused with different arguments. This approach optimizes query planning and execution, especially for read operations. The cost of parsing the query is paid only once, making subsequent executions faster. ### Methods `prepareStatement(sql: string)` `bind(values: any[])` `bindSync(values: any[])` `execute()` ### Parameters #### `prepareStatement` Request Body - **sql** (string) - Required - The SQL query string with placeholders (e.g., '?'). #### `bind` / `bindSync` Parameters - **values** (array) - Required - An array of values to bind to the placeholders in the prepared statement, in order. ### Request Example ```javascript const statement = db.prepareStatement('SELECT * FROM User WHERE name = ?;'); // Bind variables in order await statement.bind(['Oscar']); // Or use the bindSync version statement.bindSync(['Luis']); let results1 = await statement.execute(); await statement.bind(['Carlos']); let results2 = await statement.execute(); ``` ``` -------------------------------- ### Open SQLite Database Connection Source: https://op-engineering.github.io/op-sqlite/docs/api Opens a connection to a SQLite database. It's recommended to maintain a single connection per app session to avoid latency. ```javascript import { open } from '@op-engineering/op-sqlite'; export const db = open({ name: 'myDb.sqlite', }); ``` -------------------------------- ### SQLCipher Open Source: https://op-engineering.github.io/op-sqlite/docs/api For SQLCipher databases, the `open` method requires an additional `encryptionKey` to encrypt and decrypt the database. ```APIDOC ## SQLCipher Open ### Description For SQLCipher databases, the `open` method requires an additional `encryptionKey` to encrypt and decrypt the database. Keep your encryption key secure. ### Method `open(options: { name: string, encryptionKey: string })` ### Parameters #### Request Body - **name** (string) - Required - The name of the database file. - **encryptionKey** (string) - Required - The key used for encrypting/decrypting the database. ### Request Example ```javascript import { open } from '@op-engineering/op-sqlite'; export const db = open({ name: 'myDb.sqlite', encryptionKey: 'YOUR ENCRYPTION KEY, KEEP IT SOMEWHERE SAFE', }); ``` ``` -------------------------------- ### Open a Purely Remote Turso Database Source: https://op-engineering.github.io/op-sqlite/docs/Turso/start Use `openRemote` to establish a connection to a Turso database that is exclusively remote. This method requires the database URL and an authentication token. ```typescript import { openRemote } from '@op-engineering/op-sqlite'; const remoteDb = openRemote({ url: 'url', authToken: 'token', }); ``` -------------------------------- ### Configure Expo Updates to Use Third-Party SQLite Pod Source: https://op-engineering.github.io/op-sqlite/docs/installation Use this Expo plugin to automate the setting for `expo.updates.useThirdPartySQLitePod` in your `Podfile.properties.json`. This helps resolve duplicate symbols and header definition issues when `expo-updates` is the only conflicting package. ```typescript import type { ConfigPlugin } from '@expo/config-plugins'; import { withPodfileProperties } from '@expo/config-plugins'; const withUseThirdPartySQLitePod: ConfigPlugin = (expoConfig) => { return withPodfileProperties(expoConfig, (config) => { config.modResults = { ...config.modResults, 'expo.updates.useThirdPartySQLitePod': 'true', }; return config; }); }; export default withUseThirdPartySQLitePod; ``` -------------------------------- ### Expo Updates Workaround for AppDelegate Source: https://op-engineering.github.io/op-sqlite/docs/installation Apply this workaround in your `AppDelegate.mm` by calling `[OPSQLite expoUpdatesWorkaround];` before initializing the React Native view. This is necessary when using `expo-updates` and `libsql` simultaneously. ```objective-c #import "OPSQLite.h" // Add the header // Modify the didFinishLaunchingWithOptions function -(BOOL)application: (UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions { self moduleName = @"main"; self.initialProps = 0{}; // Add the call to the workaround [OPSQLite expoUpdatesWorkaround]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } ``` -------------------------------- ### Synchronous Execute Query Source: https://op-engineering.github.io/op-sqlite/docs/api Perform synchronous queries using `executeSync`. This method blocks the UI thread and is not available within transactions. Use sparingly. ```javascript let res = db.executeSync('SELECT 1'); ``` -------------------------------- ### TypeORM Driver for OP-SQLite Source: https://op-engineering.github.io/op-sqlite/docs/ORM_Libs This driver allows TypeORM to use OP-SQLite. Ensure an encryption key is provided when opening the database. ```typescript import { QueryResult, Transaction, open } from '@op-engineering/op-sqlite'; const enhanceQueryResult = (result: QueryResult): void => { result.rows.item = (idx: number) => result.rows[idx]; }; export const typeORMDriver = { openDatabase: ( options: { name: string; location?: string; encryptionKey: string; }, ok: (db: any) => void, fail: (msg: string) => void ): any => { try { if (!options.encryptionKey || options.encryptionKey.length === 0) { throw new Error('[op-sqlite]: Encryption key is required'); } const database = open({ location: options.location, name: options.name, encryptionKey: options.encryptionKey, }); const connection = { executeSql: async ( sql: string, params: any[] | undefined, ok: (res: QueryResult) => void, fail: (msg: string) => void ) => { try { const response = await database.execute(sql, params); enhanceQueryResult(response); ok(response); } catch (e) { fail(`[op-sqlite]: Error executing SQL: ${e as string}`); } }, transaction: ( fn: (tx: Transaction) => Promise ): Promise => { return database.transaction(fn); }, close: (ok: any, fail: any) => { try { database.close(); ok(); } catch (e) { fail(`[op-sqlite]: Error closing db: ${e as string}`); } }, attach: ( dbNameToAttach: string, alias: string, location: string | undefined, callback: () => void ) => { database.attach(options.name, dbNameToAttach, alias, location); callback(); }, detach: (alias: string, callback: () => void) => { database.detach(options.name, alias); callback(); }, }; ok(connection); return connection; } catch (e) { fail(`[op-sqlite]: Error opening database: ${e as string}`); } }, }; ``` -------------------------------- ### Clear Gradle Cache Source: https://op-engineering.github.io/op-sqlite/docs/installation If you encounter the 'Base module not found' error, clear your Gradle cache by removing the `caches/` directory within your Gradle user home directory and then rebuild your app. ```bash # Example command (actual path may vary) # rm -rf ~/.gradle/caches ``` -------------------------------- ### Execute SQL Queries Source: https://op-engineering.github.io/op-sqlite/docs/api Executes SQL commands asynchronously. All execute calls run on a single, dedicated thread to prevent blocking the JavaScript thread. It is recommended to always use transactions, as even read operations can potentially corrupt a SQLite database. ```APIDOC ## Execute SQL Queries ### Description Executes SQL commands asynchronously. All execute calls run on a single, dedicated thread to prevent blocking the JavaScript thread. It is recommended to always use transactions, as even read operations can potentially corrupt a SQLite database. ### Web Note On web, `execute()` runs the full SQL string passed to it. On native, `execute()` currently runs only the first prepared statement. Avoid multi-statement SQL strings for identical behavior across platforms. ### Method `execute(sql: string)` ### Parameters #### Request Body - **sql** (string) - Required - The SQL query string to execute. ### Request Example ```javascript import { open } from '@op-engineering/op-sqlite'; try { const db = open({ name: 'myDb.sqlite' }); let { rows } = await db.execute('SELECT somevalue FROM sometable'); rows.forEach((row) => { console.log(row); }); } catch (e) { console.error('Something went wrong executing SQL commands:', e.message); } ``` ``` -------------------------------- ### Move Assets Database Source: https://op-engineering.github.io/op-sqlite/docs/api Move a database file from the app's bundled assets to the documents folder using `moveAssetsDatabase`. This operation is necessary before using the database and ensures data persistence across app updates. The `overwrite` option can be used to replace an existing database. ```javascript const copied = await moveAssetsDatabase({ filename: 'sample2.sqlite', path: 'sqlite', // The path inside your assets folder on Android, on iOS the file structure is flat overwrite: true, // Always overwrite the database }); expect(copied).to.equal(true); ``` -------------------------------- ### Create New JS Object from HostObject Properties Source: https://op-engineering.github.io/op-sqlite/docs/gotchas When attempting to assign complex objects or if direct assignment fails, create a new pure JavaScript object by spreading the HostObject's properties. This avoids potential issues with C++ type casting and race conditions. ```javascript let newUser = { ...{}, ...results._array[0], newProp: { foo: 'bar' } }; ``` -------------------------------- ### Open SQLCipher Encrypted Database Source: https://op-engineering.github.io/op-sqlite/docs/api Opens a connection to a SQLCipher encrypted SQLite database. Requires an `encryptionKey` to be provided. ```javascript import { open } from '@op-engineering/op-sqlite'; export const db = open({ name: 'myDb.sqlite', encryptionKey: 'YOUR ENCRYPTION KEY, KEEP IT SOMEWHERE SAFE', // for example op-s2 }); ``` -------------------------------- ### Assign Scalar Properties to HostObjects Source: https://op-engineering.github.io/op-sqlite/docs/gotchas You can assign scalar values to properties of HostObjects returned by `executeWithHostObjects`. This is useful for simple data modifications. ```javascript let results = await db.executeWithHostObjects('SELECT * FROM USER;'); results._array[0].newProp = 'myNewProp'; ``` -------------------------------- ### Execute Multiple SQL Statements Source: https://op-engineering.github.io/op-sqlite/docs/api Execute multiple SQL statements in a single operation. Note that results and metadata may be mixed and should be discarded. This is not supported in libsql. ```javascript let res = await db.execute( `CREATE TABLE T1 (id INT PRIMARY KEY) STRICT; CREATE TABLE T2 (id INT PRIMARY KEY) STRICT;` ); ``` -------------------------------- ### Set Journal Mode to Memory in SQLite Source: https://op-engineering.github.io/op-sqlite/docs/configuration Increase speed by setting the journaling mode to MEMORY or OFF. Modifying the journal mode is dangerous as it affects SQLite's ability to ROLLBACK statements. Use with caution. ```javascript await db.execute('PRAGMA journal_mode = MEMORY;'); // or OFF ``` -------------------------------- ### Execute SQL Query Source: https://op-engineering.github.io/op-sqlite/docs/api Executes an SQL query against the database. All execute calls run on a separate thread to avoid blocking the JS thread. Transactions are recommended for all operations, including reads, to prevent data corruption. ```javascript import { open } from '@op-engineering/op-sqlite'; try { const db = open({ name: 'myDb.sqlite' }); let { rows } = await db.execute('SELECT somevalue FROM sometable'); rows.forEach((row) => { console.log(row); }); } catch (e) { console.error('Something went wrong executing SQL commands:', e.message); } ``` -------------------------------- ### Attach and Detach Databases Source: https://op-engineering.github.io/op-sqlite/docs/api Attach external SQLite databases to the main connection using an alias. Operations can span across attached databases. Detach databases when no longer needed. ```javascript // Follows similar API to the `open` call db.attach({ secondaryDbFileName: 'statistics.sqlite', // Filename of the database (JUST THE FILENAME) alias: 'stats', // Alias to be applied to the db location: '../databases', // Path to be prepended to secondaryFileName, in this case full db path: ../databases/statistics.sqlite }); const res = await db.execute( 'SELECT * FROM some_table_from_mainschema a INNER JOIN stats.some_table b on a.id_column = b.id_column' ); // You can detach databases at any moment db.detach('stats'); ``` -------------------------------- ### JSONB Support with op-sqlite Source: https://op-engineering.github.io/op-sqlite/docs/api Utilize SQLite's JSONB support directly through op-sqlite. You can insert JSON strings, query using JSONB operators like `->>` and `jsonb_extract`, and retrieve string responses. Ensure you understand SQLite's JSONB behavior. ```javascript await db.execute('CREATE TABLE states(data TEXT);'); await db.execute( `INSERT INTO states VALUES ('{"country":"Luxembourg","capital":"Luxembourg City","languages":["French","German","Luxembourgish"]}');` ); let res = await db.execute( `SELECT data->>'country' FROM states WHERE data->>'capital'=='Amsterdam';` ); let res2 = await db.execute( `SELECT jsonb_extract(data, '$.languages') FROM states;` ); ``` -------------------------------- ### Set Up Database Update Hook Source: https://op-engineering.github.io/op-sqlite/docs/api Subscribe to database changes using an update hook. Note that rowId is the internal SQLite rowId, not the table's primary key. This hook can be used to react to insert, update, or delete operations. ```javascript // Bear in mind: rowId is not your table primary key but the internal rowId sqlite uses // to keep track of the table rows db.updateHook(({ rowId, table, operation }) => { console.warn(`Hook has been called, rowId: ${rowId}, ${table}, ${operation}`); const changes = await db.execute('SELECT * FROM User WHERE rowid = ?', [ rowid, ]); }); await db.execute( 'INSERT INTO User (id, name, age, networth) VALUES(?, ?, ?, ?)', [id, name, age, networth] ); ``` -------------------------------- ### Enable Foreign Key Constraint Enforcement in SQLite Source: https://op-engineering.github.io/op-sqlite/docs/gotchas By default, SQLite does not enforce foreign key constraints. Enable this feature by executing the PRAGMA command after opening the connection. ```javascript db.executeSync('PRAGMA foreign_keys = true'); ``` -------------------------------- ### Execute within a Transaction Source: https://op-engineering.github.io/op-sqlite/docs/api Wrap database operations within a transaction. Any unhandled error will cause a rollback. For performance with many commands, consider `executeBatch`. ```javascript await db.transaction((tx) => { const { status } = await tx.execute( 'UPDATE sometable SET somecolumn = ? where somekey = ?', [0, 1] ); // Any uncatched error ROLLBACK transaction throw new Error('Random Error!'); // You can manually commit or rollback await tx.commit(); // or await tx.rollback(); }); ``` -------------------------------- ### Subscribe to Table Changes Source: https://op-engineering.github.io/op-sqlite/docs/reactive_queries Use this snippet to subscribe to any changes within a specific table. It's ideal for scenarios where you need to update a list of elements. Remember to call the returned unsubscribe function when no longer needed. Transactions are required to trigger updates. ```javascript let unsubscribe = db.reactiveExecute({ query: 'SELECT * FROM Users', fireOn: [ { table: 'User', }, ], callback: (usersResponse) => { console.log(usersReponse.rows); runInAction(() => { this.users = usersReponse.rows; }); }, }); unsubscribe(); await db.transaction(async () => { await db.execute('...'); }); ``` -------------------------------- ### Web Synchronous API Behavior Source: https://op-engineering.github.io/op-sqlite/docs/api On web platforms, synchronous APIs intentionally throw errors. Always use asynchronous methods for web-based operations.