Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 5 additions & 86 deletions docs/api-package-intros.mjs
Original file line number Diff line number Diff line change
@@ -1,94 +1,13 @@
const packageIntros = {
'react-native-nitro-sqlite': `Nitro SQLite provides local SQLite databases for React Native. This page lists the package's exported functions, objects, and types. For a guided introduction, start with [Getting Started](/docs).
'react-native-nitro-sqlite': `\`open({ name })\` returns a connection with query, transaction, and cleanup methods. This reference lists the package's exported functions, objects, and types.

## Install

Requires React Native 0.75 or newer, React 17 or newer, and \`react-native-nitro-modules\` 0.37.1 or newer.

\`\`\`sh
npm install react-native-nitro-sqlite react-native-nitro-modules
\`\`\`

Install CocoaPods for Apple platforms, then rebuild the native app. Android needs no additional setup for the default database location. Expo projects need a development build; Expo Go cannot load the native module.

\`\`\`sh
npx pod-install
\`\`\`

For a React Native macOS app, run \`pod install\` from its \`macos\` directory. See the [macOS configuration guide](/docs/configuration/macos) for the desktop example and database location.

## Open a database

\`\`\`ts
import { open } from 'react-native-nitro-sqlite'

const db = open({ name: 'app.sqlite' })
db.execute('CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)')
db.execute('INSERT INTO notes (body) VALUES (?)', ['Hello'])
const { rows } = db.execute('SELECT id, body FROM notes')
console.log(rows._array)
db.close()
\`\`\`

Use [Getting Started](/docs) for native setup and a fuller first query. The [concepts](/docs/concepts/databases-and-connections) and [guides](/docs/guides/parameters-and-results) explain database lifetime, query parameters, and results. For the Node-only test export and its API, see [Test SQLite in Node](/docs/guides/node-test-mock).
Start with [Getting Started](/docs) for installation and a first query. Use [Configuration](/docs/configuration) for shared \`package.json\` settings, [database lifecycle](/docs/guides/database-lifecycle) for opening and closing connections, and [parameters and results](/docs/guides/parameters-and-results) for queries and errors. For the Node-only test export, see [Test SQLite in Node](/docs/guides/node-test-mock).
`,
'react-native-nitro-sqlite/mock': `This Node-only export runs SQLite tests through \`better-sqlite3\` without loading the React Native module. Install \`better-sqlite3\` as a development dependency and import this module in your test setup. See [Test SQLite in Node](/docs/guides/node-test-mock) for Jest setup, shared connections, prepared statements, transactions, and cleanup.
'react-native-nitro-sqlite/mock': `Import \`react-native-nitro-sqlite/mock\` to run SQLite tests through \`better-sqlite3\` without loading the React Native module. See [Test SQLite in Node](/docs/guides/node-test-mock) for installation, Jest setup, shared connections, prepared statements, transactions, and cleanup.
`,
'react-native-nitro-sqlite-vec': `This optional package adds [sqlite-vec](https://github.com/asg017/sqlite-vec) vector search to Nitro SQLite. Its native code is compiled into the core package's SQLite build. The functions and types below cover availability checks, vector tables, and nearest-neighbor searches.

## Install

Install Nitro SQLite, Nitro Modules, and the vector companion. The core requires React Native 0.75 or newer, React 17 or newer, and Nitro Modules 0.37.1 or newer. Enable sqlite-vec for every platform you ship.

\`\`\`sh
npm install react-native-nitro-sqlite react-native-nitro-modules react-native-nitro-sqlite-vec
\`\`\`

On iOS and visionOS, install Pods with the build flag:

\`\`\`sh
NITRO_SQLITE_VEC=1 npx pod-install
\`\`\`

For macOS, run \`NITRO_SQLITE_VEC=1 pod install\` from the app's \`macos\` directory.

On Android, add this to \`android/gradle.properties\`:

\`\`\`properties
nitroSqliteVec=true
\`\`\`

Rebuild the native app after enabling the flag. See the [vector search guide](/docs/integrations/vector-search) for platform setup and usage.

## Create and search a vector table

\`\`\`ts
import { open } from 'react-native-nitro-sqlite'
import {
createVectorTable,
isVecAvailable,
knnSearch,
} from 'react-native-nitro-sqlite-vec'

const db = open({ name: 'vectors.sqlite' })

if (!isVecAvailable(db)) {
db.close()
throw new Error('Enable sqlite-vec in the native build')
}

createVectorTable(db, 'embeddings', { dimensions: 3 })
db.execute('INSERT INTO embeddings (rowid, embedding) VALUES (?, ?)', [
1,
'[0.1, 0.2, 0.3]',
])

const matches = knnSearch(db, 'embeddings', [0.1, 0.2, 0.25], 10)
console.log(matches)
db.close()
\`\`\`
'react-native-nitro-sqlite-vec': `\`createVectorTable()\` creates a vector table, \`knnSearch()\` finds nearby matches, and \`isVecAvailable()\` checks extension availability on an open connection. This optional package adds [sqlite-vec](https://github.com/asg017/sqlite-vec) to Nitro SQLite.

\`isVecAvailable()\` returns \`false\` when its version query fails. The helpers use table and column names as SQL identifiers, so only pass names you control. See the [vector search guide](/docs/integrations/vector-search) for storage types, distance metrics, and custom queries.
Follow the [vector search guide](/docs/integrations/vector-search) for installation, platform setup, and a complete example. The functions and types below describe the helper API.
`,
}

Expand Down
28 changes: 0 additions & 28 deletions docs/content/docs/concepts/connection-ordering.mdx

This file was deleted.

28 changes: 11 additions & 17 deletions docs/content/docs/concepts/databases-and-connections.mdx
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
---
title: Databases and connections
description: How SQLite files and Nitro SQLite connections relate.
description: SQLite database files, open connections, and persistent data.
---

SQLite is an embedded database. Your app calls the SQLite library directly, without a separate database server. A database usually lives in a file containing its tables, indexes, and other schema objects. A connection is an open handle to that database; closing the handle does not remove the file. See [SQLite's overview](https://www.sqlite.org/about.html) for more background.
SQLite is an embedded database engine. It runs inside an application and reads and writes local files without a separate database server. A database contains tables, indexes, and other schema objects. See [SQLite's overview](https://www.sqlite.org/about.html).

## Open and close a connection
## Connections

In Nitro SQLite, `open()` opens an existing file or creates one when it does not exist. Keep the returned connection while you need to query that database:
<span id="open-and-close-a-connection" />

```ts
import { open } from 'react-native-nitro-sqlite'
A connection is an open handle to a database. It holds the state needed to run SQL, including its transaction and connection settings. Several connections can access the same database file; each has its own transaction. [Isolation and concurrency](/docs/concepts/isolation-and-concurrency) explains when they see each other's changes.

const db = open({ name: 'notes.sqlite' })
const { rows } = await db.executeAsync(
'SELECT name FROM sqlite_master WHERE type = ?',
['table'],
)
console.log(rows._array)
db.close()
```
## File lifetimes

The optional `location` is a directory relative to the platform's database root. Only one default connection can be open for a database name at a time. Pass `connection: 'independent'` to `open()` for another handle to the same file. Finish pending async work before calling `close()`; it is synchronous and fails while that connection is busy. See [multiple connections](/docs/guides/multiple-connections) for a reader and writer example.
<span id="delete-a-database-file" />

## Delete a database file
Closing a connection releases its resources and leaves committed data in the file. Removing the database file removes that data.

Use `delete()` to remove the file when you no longer need its data. `close()` only releases the handle. For file locations, prepopulated databases, and cleanup, read the [database lifecycle guide](/docs/guides/database-lifecycle). The generated [`NitroSQLiteConnection` reference](/api/react-native-nitro-sqlite/interfaces/NitroSQLiteConnection) lists its methods.
SQLite can also create companion journal files. In [WAL mode](https://www.sqlite.org/wal.html#the_wal_file), committed changes can remain in the `-wal` file until checkpointing copies them into the main file. Copying only the main file while the database is active can lose data.

For Nitro SQLite's open, close, delete, and file-location APIs, see [database lifecycle](/docs/guides/database-lifecycle).
32 changes: 32 additions & 0 deletions docs/content/docs/concepts/isolation-and-concurrency.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: Isolation and concurrency
description: How SQLite connections share data and coordinate reads and writes.
---

A transaction sees its own writes before committing. Separate connections normally see only committed changes. See [SQLite's isolation guide](https://www.sqlite.org/isolation.html).

## Readers and writers

<span id="ordering-on-one-connection" />

SQLite supports multiple readers, but only one writer at a time for a database file. Competing writes can fail with `SQLITE_BUSY`. A bounded `busy_timeout` can wait for temporary lock contention; it does not guarantee success.

## Journal modes

<span id="transactions-and-independent-connections" />

Rollback journaling preserves original pages in a journal while changes are written to the database. Readers and writers can block each other.

Write-ahead logging, or WAL, writes changes to a separate log so readers can continue while a writer works:

```sql
PRAGMA journal_mode = WAL;
```

A WAL read transaction sees a snapshot of committed data from when it began. It must end before a new transaction can see later commits. Long-lived readers can delay checkpoints and let the WAL file grow. See [SQLite's WAL documentation](https://www.sqlite.org/wal.html#concurrency).

## Connection settings

WAL mode persists in the database file. Settings such as `busy_timeout` and `foreign_keys` belong to each connection and need to be set again when opening another one.

For Nitro SQLite APIs, see [multiple connections](/docs/guides/multiple-connections). For the order of calls on one connection, see [sync and async](/docs/guides/sync-and-async#connection-ordering).
8 changes: 7 additions & 1 deletion docs/content/docs/concepts/meta.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"title": "Concepts",
"pages": ["databases-and-connections", "tables-and-values", "queries-and-indexes", "transactions", "connection-ordering"]
"pages": [
"databases-and-connections",
"tables-and-values",
"queries-and-indexes",
"transactions",
"isolation-and-concurrency"
]
}
85 changes: 29 additions & 56 deletions docs/content/docs/concepts/queries-and-indexes.mdx
Original file line number Diff line number Diff line change
@@ -1,76 +1,49 @@
---
title: Queries and indexes
description: Read and change rows with SQL, then inspect query plans.
description: SQL queries, query plans, and spatial indexes.
---

SQL statements read or change the database. A `SELECT` can filter rows with `WHERE`, choose columns, and set an order with `ORDER BY`. Without an index that helps a query, SQLite may need to inspect many rows. An index is a separate data structure that SQLite's query planner can use to find matching rows or produce an order. It also takes storage and work to maintain when data changes. [SQLite's query planning guide](https://www.sqlite.org/queryplanner.html) explains how the planner chooses a path.
A `SELECT` reads rows, filters them with `WHERE`, and orders them with `ORDER BY`. An index helps SQLite find matching rows without inspecting every row. It also takes storage and adds work to writes.

## Create an index and inspect the plan

Nitro SQLite sends your SQL through `execute()` or `executeAsync()`. Assuming the `notes` table from [tables and values](/docs/concepts/tables-and-values) exists, use the same methods to create an index and inspect a query plan:
For the `notes` table in [tables and values](/docs/concepts/tables-and-values):

```ts
import { open } from 'react-native-nitro-sqlite'
```sql
CREATE INDEX notes_body_idx ON notes(body);

const db = open({ name: 'notes.sqlite' })

await db.executeAsync(
'CREATE INDEX IF NOT EXISTS notes_body_idx ON notes(body)',
)
SELECT id, body FROM notes WHERE body = 'Buy milk';
EXPLAIN QUERY PLAN SELECT id, body FROM notes WHERE body = 'Buy milk';
```

const { rows } = await db.executeAsync<{ id: number; body: string }>(
'SELECT id, body FROM notes WHERE body = ?',
['Buy milk'],
)
console.log(rows._array)
SQLite's query planner chooses whether to use an index. Inspect the plan with representative data before adding indexes for queries your application runs. See [SQLite's query planning guide](https://www.sqlite.org/queryplanner.html).

const plan = await db.executeAsync(
'EXPLAIN QUERY PLAN SELECT id, body FROM notes WHERE body = ?',
['Buy milk'],
)
console.log(plan.rows._array)
db.close()
```
## Parameters and prepared statements

Bind application values with `?`; placeholders cannot stand in for table or column names. Add an index for a query you actually run, then inspect its plan with representative data. [Parameters and results](/docs/guides/parameters-and-results) describes returned rows, and the [performance guide](/docs/guides/performance) covers keeping large reads bounded.
A prepared statement keeps SQL compiled for execution. Parameters such as `?` keep values separate from that SQL, so the same statement can run with different values. Parameters represent values, not table or column names. See [SQLite's expression documentation](https://www.sqlite.org/lang_expr.html).

## Query spatial ranges

An R\*Tree index stores bounding boxes and finds those that overlap a query box. Use it for queries such as finding places inside a map viewport or events that overlap a time interval. A normal B-tree index orders values by its columns; an R\*Tree can narrow a search across several independent coordinate ranges. Bounding boxes can include candidates outside an object's exact geometry, so apply an exact check when your query requires one. See [SQLite's R\*Tree documentation](https://www.sqlite.org/rtree.html).

Nitro SQLite's bundled build includes `rtree` and `rtree_i32` by default. You still create the virtual table and populate it explicitly. Enabling the module does not create indexes or change queries against ordinary tables.

```ts
import { open } from 'react-native-nitro-sqlite'

const db = open({ name: 'places.sqlite' })

await db.executeAsync(`
CREATE VIRTUAL TABLE IF NOT EXISTS place_bounds USING rtree(
id,
minLongitude, maxLongitude,
minLatitude, maxLatitude
)
`)
await db.executeAsync(
'INSERT OR REPLACE INTO place_bounds VALUES (?, ?, ?, ?, ?)',
[1, 16.36, 16.38, 48.2, 48.22],
)

const { rows } = await db.executeAsync<{ id: number }>(
`SELECT id FROM place_bounds
WHERE minLongitude <= ? AND maxLongitude >= ?
AND minLatitude <= ? AND maxLatitude >= ?`,
[16.4, 16.35, 48.23, 48.19],
)
console.log(rows._array) // [{ id: 1 }]
db.close()
An R\*Tree virtual table indexes bounding boxes for spatial or interval searches. A normal B-tree index orders values by its columns; an R\*Tree narrows searches across several coordinate ranges.

```sql
CREATE VIRTUAL TABLE place_bounds USING rtree(
id,
minLongitude, maxLongitude,
minLatitude, maxLatitude
);

INSERT INTO place_bounds VALUES (1, 16.36, 16.38, 48.2, 48.22);

SELECT id FROM place_bounds
WHERE minLongitude <= 16.4 AND maxLongitude >= 16.35
AND minLatitude <= 48.23 AND maxLatitude >= 48.19;
```

If you store the same objects in an ordinary table, keep their bounding boxes in sync through triggers or updates in the same transaction. R\*Tree data takes storage and adds work to writes. Its storage and index maintenance apply to the tables you create, rather than every database opened by an app that includes the module.
The query returns `1`. Bounding boxes can include points outside an object's exact geometry, so apply an exact check when needed. Keep boxes in sync with the source data through triggers or updates in the same transaction.

`rtree` stores coordinates as 32-bit floats and rounds lower bounds down and upper bounds up. Overlap queries can return extra candidates. Containment queries can miss an entry at an edge unless you expand the query box as described in [SQLite's roundoff guidance](https://www.sqlite.org/rtree.html#roundoff_error). Use `rtree_i32` for signed 32-bit integer coordinates. Both support one to five dimensions.
`rtree` rounds 32-bit floating-point bounds outward. Overlap queries can return extra candidates, while containment queries may need an expanded query box. `rtree_i32` uses signed 32-bit integer coordinates. Both support one to five dimensions. See [SQLite's R\*Tree documentation](https://www.sqlite.org/rtree.html#roundoff_error).

A write can restructure an active R\*Tree scan and fail with `SQLITE_LOCKED`. Nitro SQLite's execution methods, including prepared statement execution, read the complete result before returning, so a completed read does not leave that scan open.
Finish an R\*Tree scan before modifying its table; changes to the scan's index can fail with `SQLITE_LOCKED`.

For apps that do not need R\*Tree, set `nitroSQLite.enableRTree` to `false` in the app's `package.json` and rebuild the native app. See [Apple configuration](/docs/configuration/ios#rtree-support) or [Android configuration](/docs/configuration/android#rtree-support). Disabling it makes R\*Tree tables unavailable. Queries and schema migrations involving those tables can fail with `no such module: rtree`, including a rename of an ordinary table referenced by a view that also uses R\*Tree. System SQLite support depends on the operating system's build.
For Nitro SQLite, see [parameters and results](/docs/guides/parameters-and-results) to run queries, [performance](/docs/guides/performance) to bound database work, and [Configuration](/docs/configuration#rtree-support) for R\*Tree availability.
Loading