Skip to main content

Connection

Struct Connection 

Source
pub struct Connection { /* private fields */ }
Expand description

A handle to a SQLRite database. Opens a file or an in-memory DB; drop it to close. Every mutating statement auto-saves (except inside an explicit BEGIN/COMMIT block — see Transactions).

§Transactions

let mut conn = Connection::open("foo.sqlrite")?;
conn.execute("BEGIN")?;
conn.execute("INSERT INTO users (name) VALUES ('alice')")?;
conn.execute("INSERT INTO users (name) VALUES ('bob')")?;
conn.execute("COMMIT")?;

Connection is Send but not Sync — clone it (it’s currently unclonable) or share via a Mutex<Connection> if you need multi-threaded access.

Implementations§

Source§

impl Connection

Source

pub fn open<P: AsRef<Path>>(path: P) -> Result<Self>

Opens (or creates) a database file for read-write access.

If the file doesn’t exist, an empty one is materialized with the current format version. Takes an exclusive advisory lock on the file and its -wal sidecar; returns Err if either is already locked by another process.

Source

pub fn open_read_only<P: AsRef<Path>>(path: P) -> Result<Self>

Opens an existing database file for read-only access. Takes a shared advisory lock, so multiple read-only connections can coexist on the same file; any open writer excludes them. Mutating statements return cannot execute: database is opened read-only.

Source

pub fn open_in_memory() -> Result<Self>

Opens a transient in-memory database. No file is touched and no locks are taken; state lives for the lifetime of the Connection and is discarded on drop.

Examples found in repository?
examples/rust/quickstart.rs (line 15)
14fn main() -> Result<()> {
15    let mut conn = Connection::open_in_memory()?;
16
17    conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER);")?;
18    conn.execute("INSERT INTO users (name, age) VALUES ('alice', 30);")?;
19    conn.execute("INSERT INTO users (name, age) VALUES ('bob', 25);")?;
20    conn.execute("INSERT INTO users (name, age) VALUES ('charlie', 40);")?;
21
22    println!("All users:");
23    let stmt = conn.prepare("SELECT id, name, age FROM users;")?;
24    let mut rows = stmt.query()?;
25    while let Some(row) = rows.next()? {
26        let id: i64 = row.get_by_name("id")?;
27        let name: String = row.get_by_name("name")?;
28        // `Option<i64>` wraps NULL cleanly — `age` is declared
29        // nullable so the typed accessor surfaces None when absent.
30        let age: Option<i64> = row.get_by_name("age")?;
31        println!(
32            "  {} — {} ({})",
33            id,
34            name,
35            age.map(|a| a.to_string())
36                .unwrap_or_else(|| "NULL".to_string())
37        );
38    }
39
40    // Transactions: BEGIN + INSERT + ROLLBACK leaves the table untouched.
41    conn.execute("BEGIN;")?;
42    conn.execute("INSERT INTO users (name, age) VALUES ('will_vanish', 99);")?;
43    println!("\nMid-transaction row count: {}", count_users(&mut conn)?);
44    conn.execute("ROLLBACK;")?;
45    println!(
46        "Post-rollback row count:   {} (unchanged)",
47        count_users(&mut conn)?
48    );
49
50    Ok(())
51}
More examples
Hide additional examples
examples/hybrid-retrieval/hybrid_retrieval.rs (line 53)
52fn main() -> Result<()> {
53    let mut conn = Connection::open_in_memory()?;
54    conn.execute(
55        "CREATE TABLE docs (id INTEGER PRIMARY KEY, name TEXT, body TEXT, embedding VECTOR(4));",
56    )?;
57    for (name, body, vec) in CORPUS {
58        conn.execute(&format!(
59            "INSERT INTO docs (name, body, embedding) VALUES \
60             ('{name}', '{body}', [{}, {}, {}, {}]);",
61            vec[0], vec[1], vec[2], vec[3]
62        ))?;
63    }
64    conn.execute("CREATE INDEX docs_fts ON docs USING fts (body);")?;
65
66    // Same query, three rankings — see README for what each shape sees.
67    let body_query = "small embedded database";
68    let vector_query = [0.0, 0.0, 0.9, 0.2]; // semantic intent: "database, lightly web-ish"
69    let q_str = vec_lit(&vector_query);
70
71    println!("Corpus:");
72    for (name, body, vec) in CORPUS {
73        println!("  {name}: \"{body}\"  embedding={vec:?}");
74    }
75    println!("\nQuery body:   '{body_query}'");
76    println!("Query vector: {vector_query:?}\n");
77
78    println!("===  1. Pure BM25 (lexical) ===");
79    println!(
80        "WHERE  fts_match(body, '{body_query}')\n\
81         ORDER BY bm25_score(body, '{body_query}') DESC  LIMIT 3"
82    );
83    print_top(
84        &mut conn,
85        &format!(
86            "SELECT name, body FROM docs \
87             WHERE fts_match(body, '{body_query}') \
88             ORDER BY bm25_score(body, '{body_query}') DESC LIMIT 3;"
89        ),
90    )?;
91
92    println!("===  2. Pure vector (semantic) ===");
93    println!("ORDER BY vec_distance_cosine(embedding, {q_str}) ASC  LIMIT 3");
94    print_top(
95        &mut conn,
96        &format!(
97            "SELECT name, body FROM docs \
98             ORDER BY vec_distance_cosine(embedding, {q_str}) ASC LIMIT 3;"
99        ),
100    )?;
101
102    println!("===  3. Hybrid (50% BM25 + 50% inverted cosine) ===");
103    println!(
104        "WHERE  fts_match(body, '{body_query}')\n\
105         ORDER BY 0.5*bm25_score(...) + 0.5*(1.0 - vec_distance_cosine(...)) DESC  LIMIT 3"
106    );
107    print_top(
108        &mut conn,
109        &format!(
110            "SELECT name, body FROM docs \
111             WHERE fts_match(body, '{body_query}') \
112             ORDER BY 0.5 * bm25_score(body, '{body_query}') \
113                    + 0.5 * (1.0 - vec_distance_cosine(embedding, {q_str})) DESC \
114             LIMIT 3;"
115        ),
116    )?;
117    Ok(())
118}
Source

pub fn execute(&mut self, sql: &str) -> Result<String>

Parses and executes one SQL statement. For DDL (CREATE TABLE, CREATE INDEX), DML (INSERT, UPDATE, DELETE) and transaction control (BEGIN, COMMIT, ROLLBACK). Returns the status message the engine produced (e.g. "INSERT Statement executed.").

For SELECT, execute works but discards the row data and just returns the rendered status — use Connection::prepare and Statement::query to iterate typed rows.

Examples found in repository?
examples/rust/quickstart.rs (line 17)
14fn main() -> Result<()> {
15    let mut conn = Connection::open_in_memory()?;
16
17    conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER);")?;
18    conn.execute("INSERT INTO users (name, age) VALUES ('alice', 30);")?;
19    conn.execute("INSERT INTO users (name, age) VALUES ('bob', 25);")?;
20    conn.execute("INSERT INTO users (name, age) VALUES ('charlie', 40);")?;
21
22    println!("All users:");
23    let stmt = conn.prepare("SELECT id, name, age FROM users;")?;
24    let mut rows = stmt.query()?;
25    while let Some(row) = rows.next()? {
26        let id: i64 = row.get_by_name("id")?;
27        let name: String = row.get_by_name("name")?;
28        // `Option<i64>` wraps NULL cleanly — `age` is declared
29        // nullable so the typed accessor surfaces None when absent.
30        let age: Option<i64> = row.get_by_name("age")?;
31        println!(
32            "  {} — {} ({})",
33            id,
34            name,
35            age.map(|a| a.to_string())
36                .unwrap_or_else(|| "NULL".to_string())
37        );
38    }
39
40    // Transactions: BEGIN + INSERT + ROLLBACK leaves the table untouched.
41    conn.execute("BEGIN;")?;
42    conn.execute("INSERT INTO users (name, age) VALUES ('will_vanish', 99);")?;
43    println!("\nMid-transaction row count: {}", count_users(&mut conn)?);
44    conn.execute("ROLLBACK;")?;
45    println!(
46        "Post-rollback row count:   {} (unchanged)",
47        count_users(&mut conn)?
48    );
49
50    Ok(())
51}
More examples
Hide additional examples
examples/hybrid-retrieval/hybrid_retrieval.rs (lines 54-56)
52fn main() -> Result<()> {
53    let mut conn = Connection::open_in_memory()?;
54    conn.execute(
55        "CREATE TABLE docs (id INTEGER PRIMARY KEY, name TEXT, body TEXT, embedding VECTOR(4));",
56    )?;
57    for (name, body, vec) in CORPUS {
58        conn.execute(&format!(
59            "INSERT INTO docs (name, body, embedding) VALUES \
60             ('{name}', '{body}', [{}, {}, {}, {}]);",
61            vec[0], vec[1], vec[2], vec[3]
62        ))?;
63    }
64    conn.execute("CREATE INDEX docs_fts ON docs USING fts (body);")?;
65
66    // Same query, three rankings — see README for what each shape sees.
67    let body_query = "small embedded database";
68    let vector_query = [0.0, 0.0, 0.9, 0.2]; // semantic intent: "database, lightly web-ish"
69    let q_str = vec_lit(&vector_query);
70
71    println!("Corpus:");
72    for (name, body, vec) in CORPUS {
73        println!("  {name}: \"{body}\"  embedding={vec:?}");
74    }
75    println!("\nQuery body:   '{body_query}'");
76    println!("Query vector: {vector_query:?}\n");
77
78    println!("===  1. Pure BM25 (lexical) ===");
79    println!(
80        "WHERE  fts_match(body, '{body_query}')\n\
81         ORDER BY bm25_score(body, '{body_query}') DESC  LIMIT 3"
82    );
83    print_top(
84        &mut conn,
85        &format!(
86            "SELECT name, body FROM docs \
87             WHERE fts_match(body, '{body_query}') \
88             ORDER BY bm25_score(body, '{body_query}') DESC LIMIT 3;"
89        ),
90    )?;
91
92    println!("===  2. Pure vector (semantic) ===");
93    println!("ORDER BY vec_distance_cosine(embedding, {q_str}) ASC  LIMIT 3");
94    print_top(
95        &mut conn,
96        &format!(
97            "SELECT name, body FROM docs \
98             ORDER BY vec_distance_cosine(embedding, {q_str}) ASC LIMIT 3;"
99        ),
100    )?;
101
102    println!("===  3. Hybrid (50% BM25 + 50% inverted cosine) ===");
103    println!(
104        "WHERE  fts_match(body, '{body_query}')\n\
105         ORDER BY 0.5*bm25_score(...) + 0.5*(1.0 - vec_distance_cosine(...)) DESC  LIMIT 3"
106    );
107    print_top(
108        &mut conn,
109        &format!(
110            "SELECT name, body FROM docs \
111             WHERE fts_match(body, '{body_query}') \
112             ORDER BY 0.5 * bm25_score(body, '{body_query}') \
113                    + 0.5 * (1.0 - vec_distance_cosine(embedding, {q_str})) DESC \
114             LIMIT 3;"
115        ),
116    )?;
117    Ok(())
118}
Source

pub fn prepare<'c>(&'c mut self, sql: &str) -> Result<Statement<'c>>

Prepares a statement for repeated execution or row iteration. SQLR-23: the SQL is parsed once at prepare time (sqlparser walk plus placeholder rewriting), and the resulting AST is cached on the Statement for re-execution without further parsing.

Use Statement::query / Statement::run for unbound execution, or Statement::query_with_params / Statement::execute_with_params to substitute ? placeholders.

Examples found in repository?
examples/hybrid-retrieval/hybrid_retrieval.rs (line 126)
125fn print_top(conn: &mut Connection, sql: &str) -> Result<()> {
126    let stmt = conn.prepare(sql)?;
127    let mut rows = stmt.query()?;
128    let mut rank = 1;
129    while let Some(row) = rows.next()? {
130        let name: String = row.get_by_name("name")?;
131        let body: String = row.get_by_name("body")?;
132        println!("  {rank}. {name}  \"{body}\"");
133        rank += 1;
134    }
135    println!();
136    Ok(())
137}
More examples
Hide additional examples
examples/rust/quickstart.rs (line 23)
14fn main() -> Result<()> {
15    let mut conn = Connection::open_in_memory()?;
16
17    conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER);")?;
18    conn.execute("INSERT INTO users (name, age) VALUES ('alice', 30);")?;
19    conn.execute("INSERT INTO users (name, age) VALUES ('bob', 25);")?;
20    conn.execute("INSERT INTO users (name, age) VALUES ('charlie', 40);")?;
21
22    println!("All users:");
23    let stmt = conn.prepare("SELECT id, name, age FROM users;")?;
24    let mut rows = stmt.query()?;
25    while let Some(row) = rows.next()? {
26        let id: i64 = row.get_by_name("id")?;
27        let name: String = row.get_by_name("name")?;
28        // `Option<i64>` wraps NULL cleanly — `age` is declared
29        // nullable so the typed accessor surfaces None when absent.
30        let age: Option<i64> = row.get_by_name("age")?;
31        println!(
32            "  {} — {} ({})",
33            id,
34            name,
35            age.map(|a| a.to_string())
36                .unwrap_or_else(|| "NULL".to_string())
37        );
38    }
39
40    // Transactions: BEGIN + INSERT + ROLLBACK leaves the table untouched.
41    conn.execute("BEGIN;")?;
42    conn.execute("INSERT INTO users (name, age) VALUES ('will_vanish', 99);")?;
43    println!("\nMid-transaction row count: {}", count_users(&mut conn)?);
44    conn.execute("ROLLBACK;")?;
45    println!(
46        "Post-rollback row count:   {} (unchanged)",
47        count_users(&mut conn)?
48    );
49
50    Ok(())
51}
52
53fn count_users(conn: &mut Connection) -> Result<usize> {
54    let stmt = conn.prepare("SELECT id FROM users;")?;
55    let rows = stmt.query()?.collect_all()?;
56    Ok(rows.len())
57}
Source

pub fn prepare_cached<'c>(&'c mut self, sql: &str) -> Result<Statement<'c>>

Same as Connection::prepare, but consults a small per-connection LRU first. SQLR-23 — for hot statements (the body of an INSERT loop, a frequently-rerun lookup) the sqlparser walk is amortized to once across the connection’s lifetime, not once per prepare().

Default cache capacity is 16; tune with Connection::set_prepared_cache_capacity.

Source

pub fn set_prepared_cache_capacity(&mut self, cap: usize)

SQLR-23 — sets the maximum number of cached prepared plans (matches prepare_cached’s default 16). Reducing below the current size evicts the oldest entries; setting to 0 disables caching but prepare_cached still works (it just always re-parses).

Source

pub fn prepared_cache_len(&self) -> usize

SQLR-23 — current number of plans held by the prepared-statement cache. Useful for tests / introspection; not load-bearing for the public API.

Source

pub fn in_transaction(&self) -> bool

Returns true while a BEGIN … COMMIT/ROLLBACK block is open against this connection.

Source

pub fn auto_vacuum_threshold(&self) -> Option<f32>

Returns the current auto-VACUUM threshold (SQLR-10). After a page-releasing DDL (DROP TABLE / DROP INDEX / ALTER TABLE DROP COLUMN) commits, the engine compacts the file in place if the freelist exceeds this fraction of page_count. New connections default to Some(0.25) (SQLite parity); None means the trigger is disabled. See Connection::set_auto_vacuum_threshold.

Source

pub fn set_auto_vacuum_threshold( &mut self, threshold: Option<f32>, ) -> Result<()>

Sets the auto-VACUUM threshold (SQLR-10). Some(t) with t in 0.0..=1.0 arms the trigger; None disables it. Values outside 0.0..=1.0 (or NaN / infinite) return a typed error rather than silently saturating. The setting is per-connection runtime state — closing the connection drops it; new connections start at the default Some(0.25).

Calling this on an in-memory or read-only database is allowed (it just won’t fire — there’s nothing to compact / no writes will reach the trigger).

Source

pub fn is_read_only(&self) -> bool

Returns true if the connection was opened read-only. Mutating statements on a read-only connection return a typed error.

Trait Implementations§

Source§

impl ConnectionAskExt for Connection

Available on crate feature ask only.
Source§

fn ask( &self, question: &str, config: &AskConfig, ) -> Result<AskResponse, AskError>

Generate SQL from a natural-language question. Read more
Source§

impl Debug for Connection

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.