### Start Local Development Server Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/README.md Starts a local development server for live preview. Changes are reflected without restarting. ```bash yarn start ``` -------------------------------- ### Install op-sqlite with Expo Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/installation.md For Expo projects, use these commands to install the library and pre-build your application, as expo-go is not supported. ```bash npx expo install @op-engineering/op-sqlite npx expo prebuild --clean ``` -------------------------------- ### Install Dependencies Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/README.md Installs project dependencies using Yarn. ```bash yarn ``` -------------------------------- ### Download and Open Database from Server Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/configuration.md Download an existing database file from a remote server to the default database directory and then open it. This example uses pseudo-code for the download process. ```typescript 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); }; ``` -------------------------------- ### C++ Tokenizer Implementation Example Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/tokenizers.md Provide your tokenizer implementation in `c_sources/tokenizers.cpp`. This includes functions for creation, deletion, and tokenization. The `opsqlite_word_tokenizer_init` function registers the tokenizer with FTS5. ```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 ``` -------------------------------- ### Install op-sqlite with npm Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/installation.md Use this command to install the op-sqlite library as a dependency in your React Native project. ```bash npm i -s @op-engineering/op-sqlite ``` -------------------------------- ### Get Database Path Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/debugging.md This snippet demonstrates how to open a database, create a table, retrieve the database file path, log it, and copy it to the clipboard. Use this to access the database file for debugging. ```tsx 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); ``` -------------------------------- ### Add optional web dependency Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/installation.md If you are using op-sqlite on the web, install the optional @sqlite.org/sqlite-wasm dependency using yarn. ```bash yarn add @sqlite.org/sqlite-wasm ``` -------------------------------- ### Create FTS5 Virtual Table with Custom Tokenizer Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/tokenizers.md After implementing and registering your tokenizer, create an FTS5 virtual table specifying your custom tokenizer using the `tokenize` parameter. This example demonstrates creating a table and inserting 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( ``` -------------------------------- ### iOS Post-Build Hook Example for Patching Pods Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/installation.md This Ruby snippet provides a template for a post-build hook in Xcode. It shows how to iterate through pod targets and potentially modify the configuration of a specific pod, like `expo-updates`, to resolve build issues. ```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 ``` -------------------------------- ### Get Database File Path Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Retrieve the absolute file path of the currently open SQLite database. This is helpful for debugging, logging, or providing file references. ```typescript const path = db.getDbPath(); ``` -------------------------------- ### Deploy Website (using SSH) Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/README.md Deploys the website using SSH. Assumes SSH is configured for deployment. ```bash USE_SSH=true yarn deploy ``` -------------------------------- ### Recompile Libsql for iOS Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/Libsql/updating.md Navigate to the `libsql/bindings/c` directory and run the `make ios` command to recompile the C binary for iOS. ```bash make ios ``` -------------------------------- ### Recompile Libsql for Android Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/Libsql/updating.md Navigate to the `libsql/bindings/c` directory and run the `make android` command to recompile the C binary for Android. ```bash make android ``` -------------------------------- ### Build Static Website Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/README.md Generates static website content into the build directory. ```bash yarn build ``` -------------------------------- ### Open and Use SQLite Database in Node.js Source: https://github.com/op-engineering/op-sqlite/blob/main/node/README.md Demonstrates how to open a database, execute synchronous and asynchronous queries, manage transactions, perform batch operations, and close the database connection using the op-sqlite-node adapter. ```typescript import { open } from '@op-engineering/op-sqlite-node'; // Open a database const db = open({ name: 'mydb.sqlite', location: './data' // optional, defaults to current directory }); // Execute queries synchronously const result = db.executeSync('SELECT * FROM users WHERE id = ?', [1]); console.log(result.rows); // Or asynchronously const asyncResult = await db.execute('SELECT * FROM users'); console.log(asyncResult.rows); // Use transactions await db.transaction(async (tx) => { await tx.execute('INSERT INTO users (name) VALUES (?)', ['John']); await tx.execute('INSERT INTO posts (user_id, title) VALUES (?, ?)', [1, 'Hello']); }); // Execute batch operations await db.executeBatch([ ['CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)'], ['INSERT INTO users (name) VALUES (?)', ['Alice']], ['INSERT INTO users (name) VALUES (?)', ['Bob']], ]); // Close the database db.close(); ``` -------------------------------- ### Open a Local Libsql Database with Sync Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/Libsql/start.md Use `openSync` to create a local database that syncs with a remote Turso database. Optional parameters include `syncInterval` and `encryptionKey`. ```typescript 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 }); ``` -------------------------------- ### Execute SQL with Tokenizer Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/tokenizers.md This snippet demonstrates how to execute a SQL query using a custom tokenizer. It prepares a statement, binds a tokenized search term, and logs the result. Ensure the tokenizer table and content are set up prior to execution. ```javascript const setup = async () => { const res = await db.all( 'SELECT content FROM tokenizer_table WHERE content MATCH ?', ['test'] ); console.warn(res); }; setup(); }, []); ``` -------------------------------- ### Remote and Sync Open (Libsql/Turso) Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Opens connections for remote or synchronized databases using Libsql or Turso backends. Requires URL and authentication token. ```APIDOC ### Remote and Sync Open (Libsql/Turso) For remote/sync scenarios, enable either the `libsql` or `turso` backend in your package configuration, then use `openRemote` or `openSync`. ```tsx 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(); ``` ``` -------------------------------- ### Enable Libsql in package.json Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/Libsql/start.md To use Libsql with op-sqlite, modify your package.json to include the `libsql: true` option. ```json { "op-sqlite": { "libsql": true } } ``` -------------------------------- ### Deploy Website (without SSH) Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/README.md Deploys the website without using SSH. Requires specifying the GitHub username. ```bash GIT_USER= yarn deploy ``` -------------------------------- ### Open Async (Web) Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Asynchronously opens a database connection, specifically for web environments utilizing OPFS. This is the required method for web usage. ```APIDOC ### Open Async (Web) On web, opening is async-only and requires OPFS. ```tsx import { openAsync } from '@op-engineering/op-sqlite'; export const db = await openAsync({ name: 'myDb.sqlite', }); ``` On web, `open()` intentionally throws. Use `openAsync()`. ``` -------------------------------- ### Open SQLite Database Connection Asynchronously (Web) Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md On web platforms, opening a database connection is asynchronous and requires the Origin Private File System (OPFS). Use `openAsync()` for web environments as `open()` intentionally throws. ```tsx import { openAsync } from '@op-engineering/op-sqlite'; export const db = await openAsync({ name: 'myDb.sqlite', }); ``` -------------------------------- ### Execute with Host Objects Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Executes a query and returns HostObjects. This is beneficial for performance when dealing with a large number of objects, as conversions from C++ to JavaScript values only occur when accessing properties. ```APIDOC ### Execute with Host Objects It’s possible to return HostObjects when using a query. The benefit is that HostObjects are only created in C++ and only when you try to access a value inside of them a C++ value → JS value conversion happens. This means creation is fast, property access is slow. The use case is clear if you are returning **massive** amount of objects but only displaying/accessing a few of them at the time. The example of querying 300k objects from a database uses this api. Just be careful with all the gotchas of HostObjects (no spread, no logging, etc.) ```tsx let res = await db.executeWithHostObjects('select * from USERS'); ``` ``` -------------------------------- ### Open Database with Fallback Path Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/configuration.md Configure a fallback database location for Android, prioritizing external storage but defaulting to the standard database path if external storage is unavailable or null. ```typescript const db = open({ name: 'myDB', location: Platform.OS === 'ios' ? IOS_LIBRARY_PATH : ANDROID_EXTERNAL_FILES_PATH ?? ANDROID_DATABASE_PATH, }); ``` -------------------------------- ### Key-Value Storage API Usage Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/key_value_storage.md Demonstrates how to initialize and use the OP-SQLite key-value storage. Supports synchronous and asynchronous operations for getItem, setItem, getAllKeys, and clear. The storage can be configured with a location and an optional encryption key. ```typescript 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(); ``` -------------------------------- ### Loading SQLite Extensions on Android and iOS Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Illustrates how to load runtime extensions for op-sqlite. On Android, extensions are automatically loaded from jniLibs. On iOS, you must first obtain the dylib path. ```tsx 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"); ``` -------------------------------- ### Open Remote or Synchronized Database Connection (Libsql/Turso) Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Enables remote or synchronized database access by configuring the `libsql` or `turso` backend. Use `openRemote` for remote access or `openSync` for synchronized access, providing the database URL and authentication token. ```tsx 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(); ``` -------------------------------- ### Move and Open Database from Assets Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/configuration.md Load a database file from the app's assets folder by first moving it to a writable location using `moveAssetsDatabase`. This function is idempotent and safe to call on app startup. ```typescript 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); }; ``` -------------------------------- ### Execute Query with Host Objects Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Allows returning HostObjects from queries, which defers the JavaScript conversion of values until they are accessed. This is beneficial for queries returning a massive number of objects where only a subset is accessed at a time, optimizing creation speed at the cost of property access speed. ```tsx let res = await db.executeWithHostObjects('select * from USERS'); ``` -------------------------------- ### Force Static Compilation for op-sqlite in Podfile Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/installation.md When using `use_frameworks`, this snippet modifies the `Podfile` to force `op-sqlite` to compile statically, preventing compilation issues. ```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 ``` -------------------------------- ### Open In-Memory Database Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/configuration.md Create a temporary, in-memory SQLite database by setting the location to ':memory:'. This is ideal for faster operations and synchronization-only workflows as it avoids disk I/O. ```typescript import { open } from '@op-engineering/op-sqlite'; const largeDb = open({ name: 'inMemoryDb', location: ':memory:', }); ``` -------------------------------- ### Expo Updates Workaround in AppDelegate.mm Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/installation.md This Objective-C++ code snippet demonstrates how to apply a workaround for using `expo-updates` and `libsql` together. It involves adding an import for `OPSQLite` and calling `[OPSQLite expoUpdatesWorkaround];` in the `didFinishLaunchingWithOptions` function of `AppDelegate.mm`. ```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]; } ``` -------------------------------- ### Enable iOS SQLite Flag in package.json Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/installation.md This JSON snippet shows how to enable the `iosSqlite` flag for the `op-sqlite` package within `package.json`. This uses the iOS embedded version of SQLite but may result in outdated versions and no extension loading support. ```json "op-sqlite": { "iosSqlite": true } ``` -------------------------------- ### Load SQL Dump File Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Efficiently load a large set of SQL statements or restore a database backup from a .sql file. This method is faster for large datasets as it minimizes JavaScript and C++ interaction. ```typescript const { rowsAffected, commands } = await db.loadFile( '/absolute/path/to/file.sql' ); ``` -------------------------------- ### Android Pick First Strategy for Duplicated Libraries Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/installation.md This text suggests using a `pickFirst` strategy on Android to resolve duplicated libraries, which can help mitigate compilation conflicts. ```text using a `pickFirst` strategy ``` -------------------------------- ### Open a Remote Libsql Database Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/Libsql/start.md Use `openRemote` to connect to a purely remote Turso database. Ensure you provide the correct URL and authentication token. ```typescript import { openRemote } from '@op-engineering/op-sqlite'; const remoteDb = openRemote({ url: 'url', authToken: 'token', }); ``` -------------------------------- ### Prepared Statements for Reusable Queries Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Use prepared statements to parse a query once and execute it multiple times with different arguments. This is beneficial for expensive queries that are reused. ```tsx 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(); ``` -------------------------------- ### Open Database with Absolute Paths Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/configuration.md Utilize exported absolute path constants for iOS and Android to specify custom database locations. Ensure to handle potential null values for paths like ANDROID_EXTERNAL_FILES_PATH. ```typescript 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, }); ``` -------------------------------- ### Inserting JSON Data as String or Blob Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Shows how to insert JavaScript objects into the database by either stringifying them or converting them to an ArrayBuffer. ```jsx let states = // some JS object await db.execute('INSERT INTO states VALUES (?)', [JSON.stringify(states)]); // or if using blobs function objectToArrayBuffer(obj) { // Step 1: Serialize the object to a JSON string const jsonString = JSON.stringify(obj); // Step 2: Encode the string to UTF-8 const encoder = new TextEncoder(); const uint8Array = encoder.encode(jsonString); // Step 3: Convert Uint8Array to ArrayBuffer return uint8Array.buffer; } await db.execute('INSERT INTO states VALUES (?)', [ objectToArrayBuffer(states), ]); ``` -------------------------------- ### TypeORM Driver for OP-SQLite Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/ORM_Libs.md Provides a custom driver implementation for TypeORM to use with OP-SQLite. It includes methods for opening databases, executing SQL, managing transactions, and closing connections. 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}`); } }, }; ``` -------------------------------- ### Configure op-sqlite in package.json Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/installation.md Customize op-sqlite by adding the 'op-sqlite' configuration object to your root package.json. This allows enabling features like SQLCipher, CR-SQLite, performance mode, and FTS5. ```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"] } } ``` -------------------------------- ### Declare Tokenizers in package.json Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/tokenizers.md Specify the tokenizers you intend to create within the `op-sqlite` configuration in your `package.json` file. 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 } } ``` -------------------------------- ### Using SQLite JSONB Functions Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Demonstrates inserting JSON data and querying it using SQLite's JSONB functions. Ensure your data is a string or ArrayBuffer. ```jsx 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;` ); ``` -------------------------------- ### Open a SQLite Database Connection Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Opens a connection to a SQLite database. It's recommended to open only one connection per App session and reuse it throughout the application lifecycle to avoid latency. ```jsx import { open } from '@op-engineering/op-sqlite'; export const db = open({ name: 'myDb.sqlite', }); ``` -------------------------------- ### Open Database with Relative Location Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/configuration.md Use a relative path to specify the database location, allowing navigation within the default directory structure. Note that iOS sandboxing restricts access outside app bundle directories. ```typescript import { open } from '@op-engineering/op-sqlite'; const db = open({ name: 'myDB', location: '../files/databases', }); ``` -------------------------------- ### Raw Execution for Scalar Results Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Use `executeRaw` for faster execution when only scalar results are needed, avoiding the overhead of creating objects with keys. ```tsx let result = await db.executeRaw('SELECT * FROM Users;'); // result = [[123, 'Katie', ...]] ``` -------------------------------- ### Batch Execution for Multiple Commands Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Execute a batch of commands efficiently, wrapped in a single transaction. If any statement fails, all are rolled back. This method is suitable for executing a large set of commands quickly. ```tsx 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`); ``` -------------------------------- ### Include SQLite Headers in Native Code Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/cpp_usage.md Conditionally include the necessary SQLite headers based on the target platform (Android or iOS). ```cpp #ifdef __ANDROID__ #include #include #else #include #include #endif ``` -------------------------------- ### Open Database Connection Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Opens a connection to a SQLite database. It's recommended to maintain a single connection per application session to avoid latency. ```APIDOC ## Open You start by opening a connection to your DB. It’s recommended you only open one connection **per App session**. There is no need to open and close it, as a matter of fact, it will add a lot of latency to your queries. ```jsx import { open } from '@op-engineering/op-sqlite'; export const db = open({ name: 'myDb.sqlite', }); ``` ``` -------------------------------- ### Link op-sqlite Library in CMakeLists.txt (Android) Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/cpp_usage.md Include this snippet in your Android CMakeLists.txt to link the op-sqlite library. Ensure the op-engineering_op-sqlite package is found. ```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 ) ``` -------------------------------- ### Set SQLite Journaling Mode Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/configuration.md Optimizes performance by setting the journaling mode to MEMORY or OFF. Modifying this setting is risky as it compromises rollback capabilities. ```typescript await db.execute('PRAGMA journal_mode = MEMORY;'); // or OFF ``` -------------------------------- ### Force Sync Libsql Database Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/Libsql/start.md Call the `sync()` method on a Libsql database instance to manually trigger a synchronization with the remote database. This is only available for Libsql databases. ```typescript remoteDb.sync(); ``` -------------------------------- ### SQLCipher Open Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Opens a database connection with SQLCipher encryption. Requires an `encryptionKey` parameter for encrypting and decrypting the database. ```APIDOC ### SQLCipher Open If you are using SQLCipher all the methods are the same with the exception of the open method which needs an extra `encryptionKey` to encrypt/decrypt the database. ```tsx 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 }); ``` If you want to read more about securely storing your encryption key, [read this article](https://ospfranco.com/react-native-security-guide/). Again: **DO NOT OPEN MORE THAN ONE CONNECTION PER DATABASE**. Just export one single db connection for your entire application and reuse it everywhere. ``` -------------------------------- ### Subscribe to Complex Query Results Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/reactive_queries.md This snippet demonstrates subscribing to the results of a complex SQL query involving joins and aggregations. The query re-runs on changes to specified tables, providing updated aggregated data. Remember to unsubscribe when necessary. ```tsx 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 }, }); ``` -------------------------------- ### Enable SQLite Memory Mapping Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/configuration.md Enables memory mapping for faster read/write operations. Set to 0 to disable. Use with caution as application crashes may occur if queries fail. ```typescript 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'); ``` -------------------------------- ### Open Database with Drilled Down Path Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/configuration.md Specify a nested directory within a base path for database storage, allowing for more organized file management on both iOS and Android. ```typescript const db = open({ name: 'myDB', location: Platform.OS === 'ios' ? IOS_LIBRARY_PATH : `${ANDROID_EXTERNAL_FILES_PATH}/databases/`, }); ``` -------------------------------- ### Configure Expo Updates to Use Third-Party SQLite Pod Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/installation.md This JavaScript code snippet demonstrates how to use an Expo config plugin to automatically add the `expo.updates.useThirdPartySQLitePod: 'true'` property to `ios/Podfile.properties.json`, resolving conflicts with `expo-updates`. ```javascript 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; ``` -------------------------------- ### Execute SQL Query Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Executes SQL commands. All execute calls run on a dedicated thread to avoid blocking the JavaScript thread. Transactions are recommended for all operations, including reads, to prevent data corruption. ```APIDOC ## Execute Base async query operation. All execute calls run on a (**single**) separate and dedicated thread, so the JS thread is not blocked. It’s recommended to ALWAYS use transactions since even read calls can corrupt a sqlite database. ```tsx 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); } ``` ### Web note On web, `execute()` runs the full SQL string passed to it. On native, `execute()` currently runs only the first prepared statement. If you need identical behavior across platforms, avoid multi-statement SQL strings. ``` -------------------------------- ### Blob Support with ArrayBuffer and Typed Arrays Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Store and retrieve binary data using `ArrayBuffer` or typed arrays like `Uint8Array`. Ensure correct type casting when retrieving blob content. ```tsx 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); ``` -------------------------------- ### Move Database from Assets Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Copy a SQLite database file from the application's bundled assets to the documents folder. This is necessary for databases that need to be modified or written to, especially on Android. ```typescript const copied = await moveAssetsDatabase({ filename: 'sample2.sqlite', path: 'sqlite', overwrite: true, }); expect(copied).to.equal(true); ``` -------------------------------- ### Register Commit and Rollback Hooks Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Implement hooks that trigger upon transaction commit or rollback. These are useful for reacting to the final state of a transaction. ```typescript // 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 } ``` -------------------------------- ### Creating New JS Objects from HostObjects Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/gotchas.md When assigning complex objects or to avoid potential issues with HostObjects, create a new pure JavaScript object by spreading the HostObject properties. ```tsx let newUser = { ...{}, ...results._array[0], newProp: { foo: 'bar' } }; ``` -------------------------------- ### Attach and Query Attached Database Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Attach a secondary SQLite database file to the main connection with an alias. You can then query across tables in both the main and attached databases. ```typescript db.attach({ secondaryDbFileName: 'statistics.sqlite', alias: 'stats', location: '../databases', }); 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' ); ``` -------------------------------- ### Open a SQLCipher Encrypted Database Connection Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md For SQLCipher databases, the `open` method requires an additional `encryptionKey` parameter to encrypt and decrypt the database. Ensure the encryption key is stored securely. ```tsx 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 }); ``` -------------------------------- ### Synchronous Execution Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Perform synchronous queries using `executeSync`. This method blocks the UI thread and is not available in transactions. On web, sync APIs intentionally throw; use async methods only. ```jsx let res = db.executeSync('SELECT 1'); ``` -------------------------------- ### Enabling Foreign Key Constraint Enforcement Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/gotchas.md Foreign key constraints are not enforced by default. Enable them using a PRAGMA statement after opening the connection. ```tsx db.executeSync('PRAGMA foreign_keys = true') ``` -------------------------------- ### Close SQLite Database Connection Asynchronously (Web) Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md On web platforms, use `await db.closeAsync()` to asynchronously close the database connection. ```tsx const db = open({ name: 'myDb.sqlite', }); await db.closeAsync(); ``` -------------------------------- ### Clear Gradle Cache to Fix Base Module Not Found Error Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/installation.md This text describes the process of clearing the Gradle cache to resolve the 'Base module not found' error. It involves removing the `caches/` directory within the Gradle user home directory. ```text Remove the `caches/` directory and rebuilding your app should be enough to fix the error. ``` -------------------------------- ### Executing Multiple Statements Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Execute multiple SQL statements in a single operation. Note that results and metadata may be mangled and are not supported in libsql. ```tsx // The result of this query will all be all mixed, no point in trying to read it let res = await db.execute( `CREATE TABLE T1 (id INT PRIMARY KEY) STRICT; CREATE TABLE T2 (id INT PRIMARY KEY) STRICT;` ); ``` -------------------------------- ### Subscribe to Table Changes Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/reactive_queries.md Use this snippet to subscribe to any changes within a specific table. The callback function will be executed whenever the table is modified. Remember to call the returned unsubscribe function when updates are no longer needed. ```tsx let unsubscribe = db.reactiveExecute({ query: 'SELECT * FROM Users', fireOn: [ { table: 'User', }, ], callback: (usersResponse) => { console.log(usersReponse.rows); // should print the entire list of users // You can pair this with your favourite state management // If you would do this with a mobx store runInAction(() => { this.users = usersReponse.rows; }); }, }); // If you later want to stop receiving updates or you eliminate the row you are watching unsubscribe(); // To trigger the reactive query you need to execute a transaction. The query will be re-run // at the end of the transaction await db.transaction(async () => { await db.execute('...'); // Do a query that mutates the table }); ``` -------------------------------- ### Delete Database Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Deletes the database file associated with the current connection. ```APIDOC ## Delete Deletes the database file represented by the current connection. ```tsx const db = open({ name: 'myDb.sqlite', }); db.close(); db.delete(); ``` ``` -------------------------------- ### Execute SQL Query Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Executes a SQL query asynchronously on a dedicated thread to prevent blocking the JavaScript thread. It is recommended to always use transactions, even for read operations, to ensure database integrity. ```tsx 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); } ``` -------------------------------- ### Register Update Hook Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Subscribe to database update operations (INSERT, UPDATE, DELETE) to perform actions or logging when data changes. The hook provides details about the affected row, table, and operation type. ```typescript // 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] ); ``` -------------------------------- ### Assigning Scalar Properties to HostObjects Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/gotchas.md Scalar properties can be assigned to HostObjects. This is useful for simple data updates. ```tsx let results = await db.executeWithHostObjects('SELECT * FROM USER;'); results._array[0].newProp = 'myNewProp'; ``` -------------------------------- ### Close Database Connection Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Closes the current database connection. Useful before runtime teardown, replacing the database file, or deleting the database. ```APIDOC ## Close Closes the current database connection. This is mainly useful before tearing down the runtime, replacing the file on disk, or deleting the database. ```tsx const db = open({ name: 'myDb.sqlite', }); db.close(); ``` On web, use `await db.closeAsync()` instead. ``` -------------------------------- ### Enforcing Type Strictness in SQLite Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/gotchas.md Use the STRICT keyword when creating tables to ensure type safety. Without it, SQLite may perform weak type casting, leading to unexpected behavior. ```tsx await db.execute('CREATE TABLE Test (id INT PRIMARY KEY, name TEXT) STRICT;') ``` -------------------------------- ### Handling Large Integers with BigInt Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/gotchas.md Use BigInt for numbers exceeding JavaScript's safe integer limit. Serialize to string for insertion and deserialize from string upon retrieval. ```typescript CREATE TABLE IF NOT EXISTS NumbersTable (myBigInt TEXT NOT NULL) STRICT ``` ```typescript INSERT INTO NumbersTable VALUES (?) ``` ```typescript SELECT * FROM NumbersTable ``` -------------------------------- ### Transaction Management Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Wrap code in a transaction to ensure atomicity. Errors thrown within the transaction body will cause a rollback. Manual commit and rollback are also possible. ```tsx 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 Specific Row Changes Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/reactive_queries.md Subscribe to updates for a particular row by first retrieving its row ID. The callback is invoked only when the specified row is modified. Ensure you call the unsubscribe function to stop receiving updates. ```tsx 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)); // should print the user whenever it updates }, }); ``` -------------------------------- ### Detach Attached Database Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Remove an attached database from the current connection using its alias. This is useful when the secondary database is no longer needed. ```typescript db.detach('stats'); ``` -------------------------------- ### Remove Database Hooks Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Remove previously registered update, commit, or rollback hooks by passing `null` to the respective hook function. This allows for dynamic management of event listeners. ```typescript db.updateHook(null); db.commitHook(null); db.rollbackHook(null); ``` -------------------------------- ### Delete a SQLite Database Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Deletes the database file associated with the current connection. Ensure the connection is closed before attempting to delete the database file. ```tsx const db = open({ name: 'myDb.sqlite', }); db.close(); db.delete(); ``` -------------------------------- ### Interrupt a Pending Query (Native) Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md On native platforms, `interrupt()` aborts any pending database operation on the current connection. This is safe to call from a different thread. The interrupted query will return `SQLITE_INTERRUPT`, and any in-flight transaction will be rolled back. This function is not available when using `libsql` or `turso` backends. ```tsx const query = db.execute(longRunningQuery); setTimeout(() => { db.interrupt(); }, 100); try { await query; } catch (error) { // SQLITE_INTERRUPT } ``` -------------------------------- ### Interrupt Query Source: https://github.com/op-engineering/op-sqlite/blob/main/docs/docs/api.md Aborts any pending database operation on the current connection. Safe to call from a different thread. The interrupted query returns `SQLITE_INTERRUPT` and rolls back any in-flight transaction. Not available with Libsql/Turso backends. ```APIDOC ## Interrupting a Query On native, `interrupt()` aborts any pending database operation on this connection. It is safe to call from a thread different from the one running the operation. The interrupted query returns `SQLITE_INTERRUPT`; any in-flight transaction is rolled back. This calls SQLite's native [`sqlite3_interrupt()`](https://sqlite.org/c3ref/interrupt.html). `interrupt()` is not available when op-sqlite is built with the `libsql` or `turso` backend. ```tsx const query = db.execute(longRunningQuery); setTimeout(() => { db.interrupt(); }, 100); try { await query; } catch (error) { // SQLITE_INTERRUPT } ``` On web, `interrupt()` is not supported. ```