# SQLite
For an argument for using SQLite as an application file format see [[SQLite as an Application File Format]].
![[SQLite as an Application File Format#^ceunla]]
For full documentation of SQLite language syntax see [SQL As Understood By SQLite](https://sqlite.org/lang.html).
All queries and commands can use parameters. See [[SQLite Language Expressions#Parameters]] for information about parameter binding.
SQLite operates in two different journaling modes: [[Atomic Commit in SQLite|rollback journal]] and [[SQLite Write-Ahead Logging||write-ahead logging]] (WAL).
![[SQLite Write-Ahead Logging#^pey8go]]
## Using SQLite from Rust
The recommended low-level library for using SQLite inside rust is [rusqlite](https://docs.rs/rusqlite/). All Rust examples below are using rusqlite.
Opening a connection:
```rust
let connection = Connection::open("my_database.db")?;
```
Executing multiple commands in a single batch is done using `execute_batch`:
```rust
connection.execute_batch(indoc! { r#"
CREATE TABLE IF NOT EXISTS urls (
url,
expiration_date
);
CREATE TABLE IF NOT EXISTS chunks (
start INTEGER PRIMARY KEY,
length,
completed,
blob
);"#
})?;
```
Queries that return single rows can be executed directly on the connection, but for queries that return multiple rows, you need to prepare a statement first. For example:
```rust
let mut statement = connection.prepare(indoc! { r#"
INSERT INTO chunks (start, length, completed, blob)
VALUES (?, ?, 0, ZEROBLOB(?2))
"#})?; // Can fail if the SQL is invalid
statement.execute((start, length))?; //Can fail if parameter binding fails
```
Rusqlite also has the [named_params!](https://docs.rs/rusqlite/latest/rusqlite/macro.named_params.html) macro for named parameters, and the [params!](https://docs.rs/rusqlite/latest/rusqlite/macro.params.html) macro for positional parameters. These macros make working with parameters easier.
## Creating Tables
```sql
CREATE TABLE IF NOT EXISTS chunks (
start INTEGER PRIMARY KEY,
length,
completed,
blob
);
```
Notice that SQLite uses dynamic typing, so you can create a table without specifying column types. The only exception to this rule is the automatically created ID (`rowid`) and `INTEGER PRIMARY KEY`.
Columns can have various properties assigned to them. For a full list see [SQLite CREATE TABLE](https://sqlite.org/lang_createtable.html). Here are notable ones:
`INTEGER PRIMARY KEY` is used to indicate which column should be used as the primary key. If you don't specify a primary key, SQLite will create an internal `rowid` column that is used as a primary key.
`UNIQUE` ensures that all values in the column are distinct. Implemented as a unique index on the column.
See [[SQLite Foreign Key Support]] for info about foreign keys.
![[SQLite Foreign Key Support#^sll1kt]]
![[SQLite Foreign Key Support#^gmyorn]]
## Creating Rows
```sql
INSERT INTO chunks (start, length, completed, blob)
VALUES (?, ?, 0, ZEROBLOB(?2))
```
Upserting:
```sql
INSERT INTO files (path, hash)
VALUES (?, ?)
ON CONFLICT(path) DO UPDATE SET
hash=excluded.hash;
```
`excluded` refers to the values that would have been inserted if there was no conflict.
Conflict occurs if the `path` already exists in the table and is defined as a unique column.
## Read
```sql
SELECT rowid, published, last_modified, hash_last_modified, hash
FROM files
WHERE path = ?
ORDER BY last_modified DESC
LIMIT 10
```
## Update
```sql
UPDATE files
SET
published = ?,
last_modified = ?,
hash_last_modified = ?,
hash = ?
WHERE rowid = ?
```
## Delete
```sql
DELETE FROM files WHERE rowid = ?
```
## Transactions
![[SQLite Transactions#^l33yhl]]
![[SQLite Transactions#^0foyux]]
![[SQLite Transactions#^vm7v2b]]
![[SQLite Transactions#^yigjnr]]
![[SQLite Transactions#^uvyqsm]]
```sql
BEGIN; -- Starts a deferred transaction by default
-- statements...
COMMIT;
```
```sql
BEGIN EXCLUSIVE; -- Starts an exclusive transaction
-- statements...
COMMIT;
```
## Savepoints
![[SQLite Savepoints#^t3djkc]]
![[SQLite Savepoints#^s7oqgc]]
![[SQLite Savepoints#^mn014g]]
![[SQLite Savepoints#^c2smey]]
![[SQLite Savepoints#^pzqavu]]
![[SQLite Savepoints#^1rb5h2]]
## `VACUUM`
![[SQLite VACUUM#^ahlp1y]]
For more information see [[SQLite VACUUM]].
## Query Optimization
![[SQLite ANALYZE#^iri5gk]]
![[SQLite ANALYZE#^naxqbp]]
## Creating a Database Replica
There are several ways to create a database replica in SQLite: backup API, the `sqlite3_rsync` utility, and the `VACUUM INTO` command.
### Backup API
![[Using the SQLite Online Backup API#^aomsjt]]
### `sqlite3_rsync`
The [[Database Remote-Copy Tool for SQLite|sqlite3_rsync]] utility can be used to copy a database file to a remote location using SSH, using a protocol that minimizes the amount of data transferred across the network.
### `VACUUM INTO`
Is basically a `VACUUM` but into a new database file:
![[SQLite VACUUM#^zgss1s]]
![[SQLite VACUUM#^bjff9z]]
For more info see [[SQLite VACUUM]].