Skip to main content

duckdb/
lib.rs

1//! duckdb-rs is an ergonomic wrapper for using DuckDB from Rust. It attempts to
2//! expose an interface similar to [rusqlite](https://github.com/rusqlite/rusqlite).
3//!
4//! ```rust
5//! use duckdb::{params, Connection, Result};
6//! use duckdb::arrow::record_batch::RecordBatch;
7//! use duckdb::arrow::util::pretty::print_batches;
8//!
9//! #[derive(Debug)]
10//! struct Person {
11//!     id: i32,
12//!     name: String,
13//!     data: Option<Vec<u8>>,
14//! }
15//!
16//! fn main() -> Result<()> {
17//!     let conn = Connection::open_in_memory()?;
18//!
19//!     conn.execute_batch(
20//!         r"CREATE SEQUENCE seq;
21//!           CREATE TABLE person (
22//!                   id              INTEGER PRIMARY KEY DEFAULT NEXTVAL('seq'),
23//!                   name            TEXT NOT NULL,
24//!                   data            BLOB
25//!                   );
26//!          ")?;
27//!     let me = Person {
28//!         id: 0,
29//!         name: "Steven".to_string(),
30//!         data: None,
31//!     };
32//!     conn.execute(
33//!         "INSERT INTO person (name, data) VALUES (?, ?)",
34//!         params![me.name, me.data],
35//!     )?;
36//!
37//!     let mut stmt = conn.prepare("SELECT id, name, data FROM person")?;
38//!     let person_iter = stmt.query_map([], |row| {
39//!         Ok(Person {
40//!             id: row.get(0)?,
41//!             name: row.get(1)?,
42//!             data: row.get(2)?,
43//!         })
44//!     })?;
45//!
46//!     for person in person_iter {
47//!         println!("Found person {:?}", person.unwrap());
48//!     }
49//!
50//!     // query table by arrow
51//!     let rbs: Vec<RecordBatch> = stmt.query_arrow([])?.collect();
52//!     print_batches(&rbs);
53//!     Ok(())
54//! }
55//! ```
56#![warn(missing_docs)]
57
58pub use libduckdb_sys as ffi;
59
60use std::{
61    cell::RefCell,
62    convert,
63    ffi::CString,
64    fmt,
65    path::{Path, PathBuf},
66    result, str,
67};
68
69use crate::{cache::StatementCache, inner_connection::InnerConnection, raw_statement::RawStatement, types::ValueRef};
70
71#[cfg(feature = "r2d2")]
72pub use crate::r2d2::DuckdbConnectionManager;
73pub use crate::{
74    appender::Appender,
75    appender_params::{AppenderParams, AppenderParamsFromIter, appender_params_from_iter},
76    arrow_batch::{Arrow, ArrowStream},
77    cache::CachedStatement,
78    column::Column,
79    config::{AccessMode, Config, DefaultNullOrder, DefaultOrder},
80    error::Error,
81    ffi::ErrorCode,
82    inner_connection::InterruptHandle,
83    params::{Params, ParamsFromIter, params_from_iter},
84    row::{AndThenRows, Map, MappedRows, Row, RowIndex, Rows},
85    statement::Statement,
86    transaction::{DropBehavior, Transaction},
87    types::ToSql,
88};
89#[cfg(feature = "polars")]
90pub use polars_dataframe::Polars;
91
92// re-export dependencies to minimise version maintenance for crate users
93pub use arrow;
94#[cfg(feature = "loadable-extension")]
95pub use duckdb_loadable_macros::duckdb_entrypoint_c_api;
96#[cfg(feature = "polars")]
97pub use polars;
98
99/// The core module contains the main functionality of the DuckDB crate.
100pub mod core;
101
102#[macro_use]
103mod error;
104mod appender;
105mod appender_params;
106mod arrow_batch;
107#[cfg(feature = "vtab-arrow")]
108mod arrow_interop;
109mod cache;
110#[cfg(any(feature = "vscalar", feature = "vtab"))]
111mod callback;
112mod column;
113mod config;
114mod executed_result;
115mod inner_connection;
116mod params;
117
118#[cfg(feature = "polars")]
119mod polars_dataframe;
120mod pragma;
121#[cfg(feature = "r2d2")]
122mod r2d2;
123mod raw_statement;
124mod row;
125mod statement;
126mod transaction;
127
128#[cfg(any(
129    feature = "autocomplete",
130    feature = "icu",
131    feature = "json",
132    feature = "parquet",
133    feature = "tpcds",
134    feature = "tpch"
135))]
136mod extension;
137
138pub mod profiling;
139pub mod types;
140/// The duckdb table function interface
141#[cfg(feature = "vtab")]
142pub mod vtab;
143
144/// The duckdb scalar function interface
145#[cfg(feature = "vscalar")]
146pub mod vscalar;
147
148#[cfg(test)]
149mod test_all_types;
150
151// Number of cached prepared statements we'll hold on to.
152const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16;
153
154/// A macro making it more convenient to pass heterogeneous or long lists of
155/// parameters as a `&[&dyn ToSql]`.
156///
157/// # Example
158///
159/// ```rust,no_run
160/// # use duckdb::{Result, Connection, params};
161///
162/// struct Person {
163///     name: String,
164///     age_in_years: u8,
165///     data: Option<Vec<u8>>,
166/// }
167///
168/// fn add_person(conn: &Connection, person: &Person) -> Result<()> {
169///     conn.execute("INSERT INTO person (name, age_in_years, data)
170///                   VALUES (?1, ?2, ?3)",
171///                  params![person.name, person.age_in_years, person.data])?;
172///     Ok(())
173/// }
174/// ```
175#[macro_export]
176macro_rules! params {
177    () => {
178        &[] as &[&dyn $crate::ToSql]
179    };
180    ($($param:expr),+ $(,)?) => {
181        &[$(&$param as &dyn $crate::ToSql),+] as &[&dyn $crate::ToSql]
182    };
183}
184
185/// Convenience macro to build a named parameter slice.
186///
187/// This coerces each value to `&dyn ToSql`, which is useful when named
188/// parameters have different Rust types.
189/// Parameter names must be bare names without the `$` prefix. This differs
190/// from rusqlite-style named parameter slices, where keys commonly include the
191/// placeholder prefix.
192/// This macro must be used with named SQL placeholders such as `$name`,
193/// not positional placeholders such as `?` or `?1`.
194///
195/// # Example
196///
197/// ```rust,no_run
198/// # use duckdb::{Connection, Result, named_params};
199/// fn find(conn: &Connection, min_age: i32, name: &str) -> Result<bool> {
200///     conn.query_row(
201///         "SELECT $age >= 18 AND $name = 'Alice'",
202///         named_params! {
203///             "age": min_age,
204///             "name": name,
205///         },
206///         |row| row.get(0),
207///     )
208/// }
209/// ```
210#[macro_export]
211macro_rules! named_params {
212    ($($name:literal : $param:expr),* $(,)?) => {
213        &[$(($name, &$param as &dyn $crate::ToSql)),*] as &[(&str, &dyn $crate::ToSql)]
214    };
215}
216
217/// A typedef of the result returned by many methods.
218pub type Result<T, E = Error> = result::Result<T, E>;
219
220/// See the [method documentation](#tymethod.optional).
221pub trait OptionalExt<T> {
222    /// Converts a `Result<T>` into a `Result<Option<T>>`.
223    ///
224    /// By default, duckdb-rs treats 0 rows being returned from a query that is
225    /// expected to return 1 row as an error. This method will
226    /// handle that error, and give you back an `Option<T>` instead.
227    fn optional(self) -> Result<Option<T>>;
228}
229
230impl<T> OptionalExt<T> for Result<T> {
231    fn optional(self) -> Result<Option<T>> {
232        match self {
233            Ok(value) => Ok(Some(value)),
234            Err(Error::QueryReturnedNoRows) => Ok(None),
235            Err(e) => Err(e),
236        }
237    }
238}
239
240/// Name for a database within a DuckDB connection.
241#[derive(Copy, Clone, Debug)]
242pub enum DatabaseName<'a> {
243    /// The main database.
244    Main,
245
246    /// The temporary database (e.g., any "CREATE TEMPORARY TABLE" tables).
247    Temp,
248
249    /// A database that has been attached via "ATTACH DATABASE ...".
250    Attached(&'a str),
251}
252
253#[allow(clippy::needless_lifetimes)]
254impl<'a> fmt::Display for DatabaseName<'a> {
255    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256        match *self {
257            DatabaseName::Main => write!(f, "main"),
258            DatabaseName::Temp => write!(f, "temp"),
259            DatabaseName::Attached(s) => write!(f, "{s}"),
260        }
261    }
262}
263
264/// Shorthand for [`DatabaseName::Main`].
265pub const MAIN_DB: DatabaseName<'static> = DatabaseName::Main;
266
267/// Shorthand for [`DatabaseName::Temp`].
268pub const TEMP_DB: DatabaseName<'static> = DatabaseName::Temp;
269
270/// A connection to a DuckDB database.
271pub struct Connection {
272    db: RefCell<InnerConnection>,
273    cache: StatementCache,
274    path: Option<PathBuf>,
275}
276
277unsafe impl Send for Connection {}
278
279impl Connection {
280    /// Open a new connection to a DuckDB database.
281    ///
282    /// `Connection::open(path)` is equivalent to
283    /// `Connection::open_with_flags(path,
284    /// Config::default())`.
285    ///
286    /// ```rust,no_run
287    /// # use duckdb::{Connection, Result};
288    /// fn open_my_db() -> Result<()> {
289    ///     let path = "./my_db.db3";
290    ///     let db = Connection::open(&path)?;
291    ///     println!("{}", db.is_autocommit());
292    ///     Ok(())
293    /// }
294    /// ```
295    ///
296    /// # Failure
297    ///
298    /// Will return `Err` if `path` cannot be converted to a C-compatible
299    /// string or if the underlying DuckDB open call fails.
300    #[inline]
301    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
302        Self::open_with_flags(path, Config::default())
303    }
304
305    /// Open a new connection to an in-memory DuckDB database.
306    ///
307    /// # Failure
308    ///
309    /// Will return `Err` if the underlying DuckDB open call fails.
310    #[inline]
311    pub fn open_in_memory() -> Result<Self> {
312        Self::open_in_memory_with_flags(Config::default())
313    }
314
315    /// Open a new connection to an ffi database.
316    ///
317    /// # Failure
318    ///
319    /// Will return `Err` if the underlying DuckDB open call fails.
320    /// # Safety
321    ///
322    /// Need to pass in a valid db instance
323    #[inline]
324    pub unsafe fn open_from_raw(raw: ffi::duckdb_database) -> Result<Self> {
325        unsafe { InnerConnection::new_from_raw_db(raw, false) }.map(|db| Self {
326            db: RefCell::new(db),
327            cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
328            path: None, // Can we know the path from connection?
329        })
330    }
331
332    /// Open a new connection to a DuckDB database.
333    ///
334    /// # Failure
335    ///
336    /// Will return `Err` if `path` cannot be converted to a C-compatible
337    /// string or if the underlying DuckDB open call fails.
338    #[inline]
339    pub fn open_with_flags<P: AsRef<Path>>(path: P, config: Config) -> Result<Self> {
340        #[cfg(unix)]
341        fn path_to_cstring(p: &Path) -> Result<CString> {
342            use std::os::unix::ffi::OsStrExt;
343            Ok(CString::new(p.as_os_str().as_bytes())?)
344        }
345
346        #[cfg(not(unix))]
347        fn path_to_cstring(p: &Path) -> Result<CString> {
348            let s = p.to_str().ok_or_else(|| Error::InvalidPath(p.to_owned()))?;
349            Ok(CString::new(s)?)
350        }
351
352        let c_path = path_to_cstring(path.as_ref())?;
353        let config = config.with("duckdb_api", "rust").unwrap();
354        InnerConnection::open_with_flags(&c_path, config).map(|db| Self {
355            db: RefCell::new(db),
356            cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
357            path: Some(path.as_ref().to_path_buf()),
358        })
359    }
360
361    /// Open a new connection to an in-memory DuckDB database.
362    ///
363    /// # Failure
364    ///
365    /// Will return `Err` if the underlying DuckDB open call fails.
366    #[inline]
367    pub fn open_in_memory_with_flags(config: Config) -> Result<Self> {
368        Self::open_with_flags(":memory:", config)
369    }
370
371    /// Convenience method to run multiple SQL statements (that cannot take any
372    /// parameters).
373    ///
374    /// ## Example
375    ///
376    /// ```rust,no_run
377    /// # use duckdb::{Connection, Result};
378    /// fn create_tables(conn: &Connection) -> Result<()> {
379    ///     conn.execute_batch("BEGIN;
380    ///                         CREATE TABLE foo(x INTEGER);
381    ///                         CREATE TABLE bar(y TEXT);
382    ///                         COMMIT;",
383    ///     )
384    /// }
385    /// ```
386    ///
387    /// # Failure
388    ///
389    /// Will return `Err` if `sql` cannot be converted to a C-compatible string
390    /// or if the underlying DuckDB call fails.
391    pub fn execute_batch(&self, sql: &str) -> Result<()> {
392        self.db.borrow_mut().execute(sql)
393    }
394
395    /// Convenience method to prepare and execute a single SQL statement.
396    ///
397    /// On success, returns the number of rows that were changed or inserted or
398    /// deleted.
399    ///
400    /// ## Example
401    ///
402    /// ### With params
403    ///
404    /// ```rust,no_run
405    /// # use duckdb::{Connection};
406    /// fn update_rows(conn: &Connection) {
407    ///     match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?", [1i32]) {
408    ///         Ok(updated) => println!("{} rows were updated", updated),
409    ///         Err(err) => println!("update failed: {}", err),
410    ///     }
411    /// }
412    /// ```
413    ///
414    /// ### With params of varying types
415    ///
416    /// ```rust,no_run
417    /// # use duckdb::{Connection, params};
418    /// fn update_rows(conn: &Connection) {
419    ///     match conn.execute("UPDATE foo SET bar = ? WHERE qux = ?", params![&"baz", 1i32]) {
420    ///         Ok(updated) => println!("{} rows were updated", updated),
421    ///         Err(err) => println!("update failed: {}", err),
422    ///     }
423    /// }
424    /// ```
425    ///
426    /// # Failure
427    ///
428    /// Will return `Err` if `sql` cannot be converted to a C-compatible string
429    /// or if the underlying DuckDB call fails.
430    #[inline]
431    pub fn execute<P: Params>(&self, sql: &str, params: P) -> Result<usize> {
432        self.prepare(sql).and_then(|mut stmt| stmt.execute(params))
433    }
434
435    /// Returns the path to the database file, if one exists and is known.
436    #[inline]
437    pub fn path(&self) -> Option<&Path> {
438        self.path.as_deref()
439    }
440
441    /// Convenience method to execute a query that is expected to return a
442    /// single row.
443    ///
444    /// ## Example
445    ///
446    /// ```rust,no_run
447    /// # use duckdb::{Result, Connection};
448    /// fn preferred_locale(conn: &Connection) -> Result<String> {
449    ///     conn.query_row(
450    ///         "SELECT value FROM preferences WHERE name='locale'",
451    ///         [],
452    ///         |row| row.get(0),
453    ///     )
454    /// }
455    /// ```
456    ///
457    /// If the query returns more than one row, all rows except the first are
458    /// ignored.
459    ///
460    /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the
461    /// query truly is optional, you can call `.optional()` on the result of
462    /// this to get a `Result<Option<T>>`.
463    ///
464    /// # Failure
465    ///
466    /// Will return `Err` if `sql` cannot be converted to a C-compatible string
467    /// or if the underlying DuckDB call fails.
468    #[inline]
469    pub fn query_row<T, P, F>(&self, sql: &str, params: P, f: F) -> Result<T>
470    where
471        P: Params,
472        F: FnOnce(&Row<'_>) -> Result<T>,
473    {
474        self.prepare(sql)?.query_row(params, f)
475    }
476
477    /// Convenience method to execute a query that is expected to return a
478    /// single row, and execute a mapping via `f` on that returned row with
479    /// the possibility of failure. The `Result` type of `f` must implement
480    /// `std::convert::From<Error>`.
481    ///
482    /// ## Example
483    ///
484    /// ```rust,no_run
485    /// # use duckdb::{Result, Connection};
486    /// fn preferred_locale(conn: &Connection) -> Result<String> {
487    ///     conn.query_row_and_then(
488    ///         "SELECT value FROM preferences WHERE name='locale'",
489    ///         [],
490    ///         |row| row.get(0),
491    ///     )
492    /// }
493    /// ```
494    ///
495    /// If the query returns more than one row, all rows except the first are
496    /// ignored.
497    ///
498    /// # Failure
499    ///
500    /// Will return `Err` if `sql` cannot be converted to a C-compatible string
501    /// or if the underlying DuckDB call fails.
502    #[inline]
503    pub fn query_row_and_then<T, E, P, F>(&self, sql: &str, params: P, f: F) -> Result<T, E>
504    where
505        P: Params,
506        F: FnOnce(&Row<'_>) -> Result<T, E>,
507        E: convert::From<Error>,
508    {
509        self.prepare(sql)?
510            .query(params)?
511            .get_expected_row()
512            .map_err(E::from)
513            .and_then(f)
514    }
515
516    /// Prepare a SQL statement for execution.
517    ///
518    /// ## Example
519    ///
520    /// ```rust,no_run
521    /// # use duckdb::{Connection, Result};
522    /// fn insert_new_people(conn: &Connection) -> Result<()> {
523    ///     let mut stmt = conn.prepare("INSERT INTO People (name) VALUES (?)")?;
524    ///     stmt.execute(["Joe Smith"])?;
525    ///     stmt.execute(["Bob Jones"])?;
526    ///     Ok(())
527    /// }
528    /// ```
529    ///
530    /// # Failure
531    ///
532    /// Will return `Err` if `sql` cannot be converted to a C-compatible string
533    /// or if the underlying DuckDB call fails.
534    #[inline]
535    pub fn prepare(&self, sql: &str) -> Result<Statement<'_>> {
536        self.db.borrow_mut().prepare(self, sql)
537    }
538
539    /// Create an Appender for fast import data
540    /// default to use `DatabaseName::Main`
541    ///
542    /// ## Example
543    ///
544    /// ```rust,no_run
545    /// # use duckdb::{Connection, Result, params};
546    /// fn insert_rows(conn: &Connection) -> Result<()> {
547    ///     let mut app = conn.appender("foo")?;
548    ///     app.append_rows([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]])?;
549    ///     Ok(())
550    /// }
551    /// ```
552    ///
553    /// # Failure
554    ///
555    /// Will return `Err` if `table` not exists
556    pub fn appender(&self, table: &str) -> Result<Appender<'_>> {
557        self.appender_to_db(table, &DatabaseName::Main.to_string())
558    }
559
560    /// Create an Appender for fast import data
561    ///
562    /// ## Example
563    ///
564    /// ```rust,no_run
565    /// # use duckdb::{Connection, Result, params, DatabaseName};
566    /// fn insert_rows(conn: &Connection) -> Result<()> {
567    ///     let mut app = conn.appender_to_db("foo", &DatabaseName::Main.to_string())?;
568    ///     app.append_rows([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]])?;
569    ///     Ok(())
570    /// }
571    /// ```
572    ///
573    /// # Failure
574    ///
575    /// Will return `Err` if `table` not exists
576    pub fn appender_to_db(&self, table: &str, schema: &str) -> Result<Appender<'_>> {
577        self.db.borrow_mut().appender(self, None, schema, table)
578    }
579
580    /// Create an Appender for fast import data with provided catalog, schema and table
581    ///
582    /// ## Example
583    ///
584    /// ```rust,no_run
585    /// # use duckdb::{Connection, Result, params, DatabaseName};
586    /// fn insert_rows(conn: &Connection) -> Result<()> {
587    ///     let mut app = conn.appender_to_catalog_and_db("catalog", &DatabaseName::Main.to_string(), "foo")?;
588    ///     app.append_rows([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]])?;
589    ///     Ok(())
590    /// }
591    /// ```
592    ///
593    /// # Failure
594    ///
595    /// Will return `Err` if `catalog` or `schema` not exists
596    pub fn appender_to_catalog_and_db(&self, table: &str, catalog: &str, schema: &str) -> Result<Appender<'_>> {
597        self.db.borrow_mut().appender(self, Some(catalog), schema, table)
598    }
599
600    /// Create an Appender that only provides values for specific columns.
601    ///
602    /// Columns not in the list will use their DEFAULT value, or NULL if no default.
603    /// This supports all types of DEFAULT expressions including non-deterministic
604    /// ones like `random()`, `current_timestamp`, or sequences.
605    ///
606    /// ## Example
607    ///
608    /// ```rust,no_run
609    /// # use duckdb::{Connection, Result};
610    /// fn insert_partial(conn: &Connection) -> Result<()> {
611    ///     // Table: CREATE TABLE foo(id INT DEFAULT nextval('seq'), name TEXT, created TIMESTAMP DEFAULT current_timestamp)
612    ///     let mut app = conn.appender_with_columns("foo", &["name"])?;
613    ///     // Only provide name; id and created use their defaults
614    ///     app.append_row(["Alice"])?;
615    ///     app.append_row(["Bob"])?;
616    ///     Ok(())
617    /// }
618    /// ```
619    ///
620    /// # Failure
621    ///
622    /// Will return `Err` if `table` does not exist or a column name is invalid.
623    pub fn appender_with_columns(&self, table: &str, columns: &[&str]) -> Result<Appender<'_>> {
624        self.appender_with_columns_to_db(table, &DatabaseName::Main.to_string(), columns)
625    }
626
627    /// Create an Appender that only provides values for specific columns, with schema.
628    ///
629    /// See [`appender_with_columns`](Connection::appender_with_columns) for details.
630    pub fn appender_with_columns_to_db(&self, table: &str, schema: &str, columns: &[&str]) -> Result<Appender<'_>> {
631        self.db
632            .borrow_mut()
633            .appender_with_columns(self, None, schema, table, columns)
634    }
635
636    /// Create an Appender that only provides values for specific columns, with catalog and schema.
637    ///
638    /// See [`appender_with_columns`](Connection::appender_with_columns) for details.
639    pub fn appender_with_columns_to_catalog_and_db(
640        &self,
641        table: &str,
642        catalog: &str,
643        schema: &str,
644        columns: &[&str],
645    ) -> Result<Appender<'_>> {
646        self.db
647            .borrow_mut()
648            .appender_with_columns(self, Some(catalog), schema, table, columns)
649    }
650
651    /// Get a handle to interrupt long-running queries.
652    ///
653    /// ## Example
654    ///
655    /// ```rust,no_run
656    /// # use duckdb::{Connection, Result};
657    /// fn run_query(conn: Connection) -> Result<()> {
658    ///   let interrupt_handle = conn.interrupt_handle();
659    ///   let join_handle = std::thread::spawn(move || { conn.execute("expensive query", []) });
660    ///
661    ///   // Arbitrary wait for query to start
662    ///   std::thread::sleep(std::time::Duration::from_millis(100));
663    ///
664    ///   interrupt_handle.interrupt();
665    ///
666    ///   let query_result = join_handle.join().unwrap();
667    ///   assert!(query_result.is_err());
668    ///
669    ///   Ok(())
670    /// }
671    pub fn interrupt_handle(&self) -> std::sync::Arc<InterruptHandle> {
672        self.db.borrow().get_interrupt_handle()
673    }
674
675    /// Close the DuckDB connection.
676    ///
677    /// This is functionally equivalent to the `Drop` implementation for
678    /// `Connection` except that on failure, it returns an error and the
679    /// connection itself (presumably so closing can be attempted again).
680    ///
681    /// # Failure
682    ///
683    /// Will return `Err` if the underlying DuckDB call fails.
684    #[inline]
685    #[allow(clippy::result_large_err)]
686    pub fn close(self) -> Result<(), (Self, Error)> {
687        let r = self.db.borrow_mut().close();
688        r.map_err(move |err| (self, err))
689    }
690
691    /// Test for auto-commit mode.
692    /// Autocommit mode is on by default.
693    #[inline]
694    pub fn is_autocommit(&self) -> bool {
695        self.db.borrow().is_autocommit()
696    }
697
698    /// Creates a new connection to the already-opened database.
699    pub fn try_clone(&self) -> Result<Self> {
700        let inner = self.db.borrow().try_clone()?;
701        Ok(Self {
702            db: RefCell::new(inner),
703            cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
704            path: self.path.clone(),
705        })
706    }
707
708    /// Returns the version of the DuckDB library
709    pub fn version(&self) -> Result<String> {
710        self.query_row("PRAGMA version", [], |row| row.get(0))
711    }
712}
713
714impl fmt::Debug for Connection {
715    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
716        f.debug_struct("Connection").field("path", &self.path).finish()
717    }
718}
719
720#[cfg(doctest)]
721doc_comment::doctest!("../../../README.md");
722
723#[cfg(test)]
724mod test {
725    use crate::types::Value;
726
727    use super::*;
728    use std::{error::Error as StdError, fmt};
729
730    use arrow::{array::Int32Array, datatypes::DataType, record_batch::RecordBatch};
731    use fallible_iterator::FallibleIterator;
732
733    #[test]
734    fn connection_is_send() {
735        fn assert_send<T: Send>() {
736            let _ = std::marker::PhantomData::<T>;
737        }
738
739        assert_send::<Connection>();
740    }
741
742    pub fn checked_memory_handle() -> Connection {
743        Connection::open_in_memory().unwrap()
744    }
745
746    #[test]
747    fn test_params_of_vary_types() -> Result<()> {
748        let db = checked_memory_handle();
749        let sql = "BEGIN;
750                   CREATE TABLE foo(bar TEXT, qux INTEGER);
751                   INSERT INTO foo VALUES ('baz', 1), ('baz', 2), ('baz', 3);
752                   END;";
753        db.execute_batch(sql)?;
754
755        let changed = db.execute("UPDATE foo SET qux = ? WHERE bar = ?", params![1i32, &"baz"])?;
756        assert_eq!(changed, 3);
757        Ok(())
758    }
759
760    #[test]
761    #[cfg_attr(windows, ignore = "Windows doesn't allow concurrent writes to a file")]
762    fn test_concurrent_transactions_busy_commit() -> Result<()> {
763        let tmp = tempfile::tempdir().unwrap();
764        let path = tmp.path().join("transactions.db3");
765
766        Connection::open(&path)?.execute_batch(
767            "
768            BEGIN;
769            CREATE TABLE foo(x INTEGER);
770            INSERT INTO foo VALUES(42);
771            END;",
772        )?;
773
774        let mut db1 =
775            Connection::open_with_flags(&path, Config::default().access_mode(config::AccessMode::ReadWrite)?)?;
776        let mut db2 =
777            Connection::open_with_flags(&path, Config::default().access_mode(config::AccessMode::ReadWrite)?)?;
778
779        {
780            let tx1 = db1.transaction()?;
781            let tx2 = db2.transaction()?;
782
783            // SELECT first makes sqlite lock with a shared lock
784            tx1.query_row("SELECT x FROM foo LIMIT 1", [], |_| Ok(()))?;
785            tx2.query_row("SELECT x FROM foo LIMIT 1", [], |_| Ok(()))?;
786
787            tx1.execute("INSERT INTO foo VALUES(?1)", [1])?;
788            let _ = tx2.execute("INSERT INTO foo VALUES(?1)", [2]);
789
790            let _ = tx1.commit();
791            let _ = tx2.commit();
792        }
793
794        let _ = db1.transaction().expect("commit should have closed transaction");
795        let _ = db2.transaction().expect("commit should have closed transaction");
796        Ok(())
797    }
798
799    #[test]
800    fn test_persistence() -> Result<()> {
801        let temp_dir = tempfile::tempdir().unwrap();
802        let path = temp_dir.path().join("test.db3");
803
804        {
805            let db = Connection::open(&path)?;
806            let sql = "BEGIN;
807                   CREATE TABLE foo(x INTEGER);
808                   INSERT INTO foo VALUES(42);
809                   END;";
810            db.execute_batch(sql)?;
811        }
812
813        let path_string = path.to_str().unwrap();
814        let db = Connection::open(path_string)?;
815        let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", [], |r| r.get(0));
816
817        assert_eq!(42i64, the_answer?);
818        Ok(())
819    }
820
821    #[test]
822    fn test_open() {
823        let con = Connection::open_in_memory();
824        if let Err(e) = con {
825            panic!("open error {e}");
826        }
827        assert!(Connection::open_in_memory().is_ok());
828        let db = checked_memory_handle();
829        assert!(db.close().is_ok());
830        let _ = checked_memory_handle();
831        let _ = checked_memory_handle();
832    }
833
834    #[test]
835    fn test_open_from_raw() {
836        unsafe {
837            use std::{ffi::c_void, os::raw::c_char, ptr};
838
839            let mut db: ffi::duckdb_database = ptr::null_mut();
840            let mut c_err: *mut c_char = ptr::null_mut();
841            let r = ffi::duckdb_open_ext(
842                c":memory:".as_ptr(),
843                &mut db,
844                Config::default().duckdb_config(),
845                &mut c_err,
846            );
847            if r != ffi::DuckDBSuccess {
848                if !c_err.is_null() {
849                    ffi::duckdb_free(c_err as *mut c_void);
850                }
851                panic!("duckdb_open_ext failed: {r:?}");
852            }
853
854            let conn = Connection::open_from_raw(db).unwrap();
855            conn.execute_batch("SELECT 1").unwrap();
856            let cloned = conn.try_clone().unwrap();
857            drop(conn);
858            cloned.execute_batch("SELECT 2").unwrap();
859            cloned.close().unwrap();
860
861            ffi::duckdb_close(&mut db);
862        }
863    }
864
865    #[test]
866    fn test_open_failure() -> Result<()> {
867        let filename = "no_such_file.db";
868        let result =
869            Connection::open_with_flags(filename, Config::default().access_mode(config::AccessMode::ReadOnly)?);
870        assert!(result.is_err());
871        let err = result.err().unwrap();
872        if let Error::DuckDBFailure(_e, Some(msg)) = err {
873            // TODO: update error code
874            // assert_eq!(ErrorCode::CannotOpen, e.code);
875            assert!(
876                msg.contains(filename),
877                "error message '{msg}' does not contain '{filename}'"
878            );
879        } else {
880            panic!("DuckDBFailure expected");
881        }
882        Ok(())
883    }
884
885    #[cfg(unix)]
886    #[test]
887    fn test_invalid_unicode_file_names() -> Result<()> {
888        use std::{ffi::OsStr, fs::File, os::unix::ffi::OsStrExt};
889        let temp_dir = tempfile::tempdir().unwrap();
890
891        let path = temp_dir.path();
892        if File::create(path.join(OsStr::from_bytes(&[0xFE]))).is_err() {
893            // Skip test, filesystem doesn't support invalid Unicode
894            return Ok(());
895        }
896        let db_path = path.join(OsStr::from_bytes(&[0xFF]));
897        {
898            let db = Connection::open(&db_path)?;
899            let sql = "BEGIN;
900                   CREATE TABLE foo(x INTEGER);
901                   INSERT INTO foo VALUES(42);
902                   END;";
903            db.execute_batch(sql)?;
904        }
905
906        let db = Connection::open(&db_path)?;
907        let the_answer: Result<i64> = db.query_row("SELECT x FROM foo", [], |r| r.get(0));
908
909        assert_eq!(42i64, the_answer?);
910        Ok(())
911    }
912
913    #[test]
914    fn test_close_always_ok() -> Result<()> {
915        let db = checked_memory_handle();
916
917        // TODO: prepare a query but not execute it
918
919        db.close().unwrap();
920        Ok(())
921    }
922
923    #[test]
924    fn test_execute_batch() -> Result<()> {
925        let db = checked_memory_handle();
926        let sql = "BEGIN;
927                   CREATE TABLE foo(x INTEGER);
928                   INSERT INTO foo VALUES(1);
929                   INSERT INTO foo VALUES(2);
930                   INSERT INTO foo VALUES(3);
931                   INSERT INTO foo VALUES(4);
932                   END;";
933        db.execute_batch(sql)?;
934
935        db.execute_batch("UPDATE foo SET x = 3 WHERE x < 3")?;
936
937        assert!(db.execute_batch("INVALID SQL").is_err());
938        Ok(())
939    }
940
941    #[test]
942    fn test_execute_single() -> Result<()> {
943        let db = checked_memory_handle();
944        db.execute_batch("CREATE TABLE foo(x INTEGER)")?;
945
946        assert_eq!(
947            3,
948            db.execute("INSERT INTO foo(x) VALUES (?), (?), (?)", [1i32, 2i32, 3i32])?
949        );
950        assert_eq!(1, db.execute("INSERT INTO foo(x) VALUES (?)", [4i32])?);
951
952        assert_eq!(
953            10i32,
954            db.query_row::<i32, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?
955        );
956        Ok(())
957    }
958
959    #[test]
960    fn test_prepare_column_names() -> Result<()> {
961        let db = checked_memory_handle();
962        db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
963
964        let mut stmt = db.prepare("SELECT * FROM foo")?;
965        stmt.execute([])?;
966        assert_eq!(stmt.column_count(), 1);
967        assert_eq!(stmt.column_names(), vec!["x"]);
968
969        let mut stmt = db.prepare("SELECT x AS a, x AS b FROM foo")?;
970        stmt.execute([])?;
971        assert_eq!(stmt.column_count(), 2);
972        assert_eq!(stmt.column_names(), vec!["a", "b"]);
973        Ok(())
974    }
975
976    #[test]
977    fn test_prepare_execute() -> Result<()> {
978        let db = checked_memory_handle();
979        db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
980
981        let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)")?;
982        assert_eq!(insert_stmt.execute([1i32])?, 1);
983        assert_eq!(insert_stmt.execute([2i32])?, 1);
984        assert_eq!(insert_stmt.execute([3i32])?, 1);
985
986        assert!(insert_stmt.execute(["hello"]).is_err());
987        // NOTE: can't execute on errored stmt
988        // assert!(insert_stmt.execute(["goodbye"]).is_err());
989        // assert!(insert_stmt.execute([types::Null]).is_err());
990
991        let mut update_stmt = db.prepare("UPDATE foo SET x=? WHERE x<?")?;
992        assert_eq!(update_stmt.execute([3i32, 3i32])?, 2);
993        assert_eq!(update_stmt.execute([3i32, 3i32])?, 0);
994        assert_eq!(update_stmt.execute([8i32, 8i32])?, 3);
995        Ok(())
996    }
997
998    #[test]
999    fn test_prepare_query() -> Result<()> {
1000        let db = checked_memory_handle();
1001        db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
1002
1003        let mut insert_stmt = db.prepare("INSERT INTO foo(x) VALUES(?)")?;
1004        assert_eq!(insert_stmt.execute([1i32])?, 1);
1005        assert_eq!(insert_stmt.execute([2i32])?, 1);
1006        assert_eq!(insert_stmt.execute([3i32])?, 1);
1007
1008        let mut query = db.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC")?;
1009        {
1010            let mut rows = query.query([4i32])?;
1011            let mut v = Vec::<i32>::new();
1012
1013            while let Some(row) = rows.next()? {
1014                v.push(row.get(0)?);
1015            }
1016
1017            assert_eq!(v, [3i32, 2, 1]);
1018        }
1019
1020        {
1021            let mut rows = query.query([3i32])?;
1022            let mut v = Vec::<i32>::new();
1023
1024            while let Some(row) = rows.next()? {
1025                v.push(row.get(0)?);
1026            }
1027
1028            assert_eq!(v, [2i32, 1]);
1029        }
1030        Ok(())
1031    }
1032
1033    #[test]
1034    fn test_query_map() -> Result<()> {
1035        let db = checked_memory_handle();
1036        let sql = "BEGIN;
1037                   CREATE TABLE foo(x INTEGER, y TEXT);
1038                   INSERT INTO foo VALUES(4, 'hello');
1039                   INSERT INTO foo VALUES(3, ', ');
1040                   INSERT INTO foo VALUES(2, 'world');
1041                   INSERT INTO foo VALUES(1, '!');
1042                   END;";
1043        db.execute_batch(sql)?;
1044
1045        let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?;
1046        let results: Result<Vec<String>> = query.query([])?.map(|row| row.get(1)).collect();
1047
1048        assert_eq!(results?.concat(), "hello, world!");
1049        Ok(())
1050    }
1051
1052    #[test]
1053    fn test_query_row() -> Result<()> {
1054        let db = checked_memory_handle();
1055        let sql = "BEGIN;
1056                   CREATE TABLE foo(x INTEGER);
1057                   INSERT INTO foo VALUES(1);
1058                   INSERT INTO foo VALUES(2);
1059                   INSERT INTO foo VALUES(3);
1060                   INSERT INTO foo VALUES(4);
1061                   END;";
1062        db.execute_batch(sql)?;
1063
1064        assert_eq!(
1065            10i64,
1066            db.query_row::<i64, _, _>("SELECT SUM(x) FROM foo", [], |r| r.get(0))?
1067        );
1068
1069        let result: Result<i64> = db.query_row("SELECT x FROM foo WHERE x > 5", [], |r| r.get(0));
1070        match result.unwrap_err() {
1071            Error::QueryReturnedNoRows => (),
1072            err => panic!("Unexpected error {err}"),
1073        }
1074
1075        let bad_query_result = db.query_row("NOT A PROPER QUERY; test123", [], |_| Ok(()));
1076
1077        assert!(bad_query_result.is_err());
1078        Ok(())
1079    }
1080
1081    #[test]
1082    fn test_optional() -> Result<()> {
1083        let db = checked_memory_handle();
1084
1085        let result: Result<i64> = db.query_row("SELECT 1 WHERE 0 <> 0", [], |r| r.get(0));
1086        let result = result.optional();
1087        match result? {
1088            None => (),
1089            _ => panic!("Unexpected result"),
1090        }
1091
1092        let result: Result<i64> = db.query_row("SELECT 1 WHERE 0 == 0", [], |r| r.get(0));
1093        let result = result.optional();
1094        match result? {
1095            Some(1) => (),
1096            _ => panic!("Unexpected result"),
1097        }
1098
1099        let bad_query_result: Result<i64> = db.query_row("NOT A PROPER QUERY", [], |r| r.get(0));
1100        let bad_query_result = bad_query_result.optional();
1101        assert!(bad_query_result.is_err());
1102        Ok(())
1103    }
1104
1105    #[test]
1106    fn test_prepare_failures() -> Result<()> {
1107        let db = checked_memory_handle();
1108        db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
1109
1110        let _ = db.prepare("SELECT * FROM does_not_exist").unwrap_err();
1111        // assert!(format!("{}", err).contains("does_not_exist"));
1112        Ok(())
1113    }
1114
1115    #[test]
1116    fn test_is_autocommit() {
1117        let db = checked_memory_handle();
1118        assert!(db.is_autocommit(), "autocommit expected to be active by default");
1119    }
1120
1121    #[test]
1122    #[should_panic(expected = "not supported")]
1123    fn test_statement_debugging() {
1124        let db = checked_memory_handle();
1125        let query = "SELECT 12345";
1126        let stmt = db.prepare(query).unwrap();
1127
1128        assert!(format!("{stmt:?}").contains(query));
1129    }
1130
1131    #[test]
1132    fn test_notnull_constraint_error() -> Result<()> {
1133        let db = checked_memory_handle();
1134        db.execute_batch("CREATE TABLE foo(x TEXT NOT NULL)")?;
1135
1136        let result = db.execute("INSERT INTO foo (x) VALUES (NULL)", []);
1137        assert!(result.is_err());
1138
1139        match result.unwrap_err() {
1140            Error::DuckDBFailure(err, _) => {
1141                // TODO(wangfenjin): Update errorcode
1142                assert_eq!(err.code, ErrorCode::Unknown);
1143            }
1144            err => panic!("Unexpected error {err}"),
1145        }
1146        Ok(())
1147    }
1148
1149    #[test]
1150    fn test_clone() -> Result<()> {
1151        // 1. Drop the cloned connection first. The original connection should still be able to run queries.
1152        {
1153            let owned_con = checked_memory_handle();
1154            {
1155                let cloned_con = owned_con.try_clone().unwrap();
1156                cloned_con.execute_batch("create table test (c1 bigint)")?;
1157                cloned_con.close().unwrap();
1158            }
1159            owned_con.execute_batch("create table test2 (c1 bigint)")?;
1160            owned_con.close().unwrap();
1161        }
1162
1163        // 2. Close the original connection first. The cloned connection should still be able to run queries.
1164        {
1165            let cloned_con = {
1166                let owned_con = checked_memory_handle();
1167                let clone = owned_con.try_clone().unwrap();
1168                owned_con.execute_batch("create table test (c1 bigint)")?;
1169                owned_con.close().unwrap();
1170                clone
1171            };
1172            cloned_con.execute_batch("create table test2 (c1 bigint)")?;
1173            cloned_con.close().unwrap();
1174        }
1175        Ok(())
1176    }
1177
1178    #[test]
1179    fn test_try_clone_after_owner_drop() -> Result<()> {
1180        let clone1 = {
1181            let owned = checked_memory_handle();
1182            let clone1 = owned.try_clone()?;
1183            drop(owned);
1184            clone1
1185        };
1186
1187        let clone2 = clone1.try_clone()?;
1188        clone2.execute_batch("CREATE TABLE t312(i INTEGER); INSERT INTO t312 VALUES (1);")?;
1189        let value: i32 = clone1.query_row("SELECT i FROM t312", [], |r| r.get(0))?;
1190        assert_eq!(value, 1);
1191        Ok(())
1192    }
1193
1194    mod query_and_then_tests {
1195        use super::*;
1196
1197        #[derive(Debug)]
1198        enum CustomError {
1199            SomeError,
1200            Sqlite(Error),
1201        }
1202
1203        impl fmt::Display for CustomError {
1204            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1205                match *self {
1206                    Self::SomeError => write!(f, "my custom error"),
1207                    Self::Sqlite(ref se) => write!(f, "my custom error: {se}"),
1208                }
1209            }
1210        }
1211
1212        impl StdError for CustomError {
1213            fn description(&self) -> &str {
1214                "my custom error"
1215            }
1216
1217            fn cause(&self) -> Option<&dyn StdError> {
1218                match *self {
1219                    Self::SomeError => None,
1220                    Self::Sqlite(ref se) => Some(se),
1221                }
1222            }
1223        }
1224
1225        impl From<Error> for CustomError {
1226            fn from(se: Error) -> Self {
1227                Self::Sqlite(se)
1228            }
1229        }
1230
1231        type CustomResult<T> = Result<T, CustomError>;
1232
1233        #[test]
1234        fn test_query_and_then() -> Result<()> {
1235            let db = checked_memory_handle();
1236            let sql = "BEGIN;
1237                       CREATE TABLE foo(x INTEGER, y TEXT);
1238                       INSERT INTO foo VALUES(4, 'hello');
1239                       INSERT INTO foo VALUES(3, ', ');
1240                       INSERT INTO foo VALUES(2, 'world');
1241                       INSERT INTO foo VALUES(1, '!');
1242                       END;";
1243            db.execute_batch(sql)?;
1244
1245            let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?;
1246            let results: Result<Vec<String>> = query.query_and_then([], |row| row.get(1))?.collect();
1247
1248            assert_eq!(results?.concat(), "hello, world!");
1249            Ok(())
1250        }
1251
1252        #[test]
1253        fn test_query_and_then_fails() -> Result<()> {
1254            let db = checked_memory_handle();
1255            let sql = "BEGIN;
1256                       CREATE TABLE foo(x INTEGER, y TEXT);
1257                       INSERT INTO foo VALUES(4, 'hello');
1258                       INSERT INTO foo VALUES(3, ', ');
1259                       INSERT INTO foo VALUES(2, 'world');
1260                       INSERT INTO foo VALUES(1, '!');
1261                       END;";
1262            db.execute_batch(sql)?;
1263
1264            let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?;
1265            let bad_type: Result<Vec<f64>> = query.query_and_then([], |row| row.get(1))?.collect();
1266
1267            match bad_type.unwrap_err() {
1268                Error::InvalidColumnType(..) => (),
1269                err => panic!("Unexpected error {err}"),
1270            }
1271
1272            let bad_idx: Result<Vec<String>> = query.query_and_then([], |row| row.get(3))?.collect();
1273
1274            match bad_idx.unwrap_err() {
1275                Error::InvalidColumnIndex(_) => (),
1276                err => panic!("Unexpected error {err}"),
1277            }
1278            Ok(())
1279        }
1280
1281        #[test]
1282        fn test_query_and_then_custom_error() -> CustomResult<()> {
1283            let db = checked_memory_handle();
1284            let sql = "BEGIN;
1285                       CREATE TABLE foo(x INTEGER, y TEXT);
1286                       INSERT INTO foo VALUES(4, 'hello');
1287                       INSERT INTO foo VALUES(3, ', ');
1288                       INSERT INTO foo VALUES(2, 'world');
1289                       INSERT INTO foo VALUES(1, '!');
1290                       END;";
1291            db.execute_batch(sql)?;
1292
1293            let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?;
1294            let results: CustomResult<Vec<String>> = query
1295                .query_and_then([], |row| row.get(1).map_err(CustomError::Sqlite))?
1296                .collect();
1297
1298            assert_eq!(results?.concat(), "hello, world!");
1299            Ok(())
1300        }
1301
1302        #[test]
1303        fn test_query_and_then_custom_error_fails() -> Result<()> {
1304            let db = checked_memory_handle();
1305            let sql = "BEGIN;
1306                       CREATE TABLE foo(x INTEGER, y TEXT);
1307                       INSERT INTO foo VALUES(4, 'hello');
1308                       INSERT INTO foo VALUES(3, ', ');
1309                       INSERT INTO foo VALUES(2, 'world');
1310                       INSERT INTO foo VALUES(1, '!');
1311                       END;";
1312            db.execute_batch(sql)?;
1313
1314            let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC")?;
1315            let bad_type: CustomResult<Vec<f64>> = query
1316                .query_and_then([], |row| row.get(1).map_err(CustomError::Sqlite))?
1317                .collect();
1318
1319            match bad_type.unwrap_err() {
1320                CustomError::Sqlite(Error::InvalidColumnType(..)) => (),
1321                err => panic!("Unexpected error {err}"),
1322            }
1323
1324            let bad_idx: CustomResult<Vec<String>> = query
1325                .query_and_then([], |row| row.get(3).map_err(CustomError::Sqlite))?
1326                .collect();
1327
1328            match bad_idx.unwrap_err() {
1329                CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (),
1330                err => panic!("Unexpected error {err}"),
1331            }
1332
1333            let non_sqlite_err: CustomResult<Vec<String>> =
1334                query.query_and_then([], |_| Err(CustomError::SomeError))?.collect();
1335
1336            match non_sqlite_err.unwrap_err() {
1337                CustomError::SomeError => (),
1338                err => panic!("Unexpected error {err}"),
1339            }
1340            Ok(())
1341        }
1342
1343        #[test]
1344        fn test_query_row_and_then_custom_error() -> CustomResult<()> {
1345            let db = checked_memory_handle();
1346            let sql = "BEGIN;
1347                       CREATE TABLE foo(x INTEGER, y TEXT);
1348                       INSERT INTO foo VALUES(4, 'hello');
1349                       END;";
1350            db.execute_batch(sql)?;
1351
1352            let query = "SELECT x, y FROM foo ORDER BY x DESC";
1353            let results: CustomResult<String> =
1354                db.query_row_and_then(query, [], |row| row.get(1).map_err(CustomError::Sqlite));
1355
1356            assert_eq!(results?, "hello");
1357            Ok(())
1358        }
1359
1360        #[test]
1361        fn test_query_row_and_then_custom_error_fails() -> Result<()> {
1362            let db = checked_memory_handle();
1363            let sql = "BEGIN;
1364                       CREATE TABLE foo(x INTEGER, y TEXT);
1365                       INSERT INTO foo VALUES(4, 'hello');
1366                       END;";
1367            db.execute_batch(sql)?;
1368
1369            let query = "SELECT x, y FROM foo ORDER BY x DESC";
1370            let bad_type: CustomResult<f64> =
1371                db.query_row_and_then(query, [], |row| row.get(1).map_err(CustomError::Sqlite));
1372
1373            match bad_type.unwrap_err() {
1374                CustomError::Sqlite(Error::InvalidColumnType(..)) => (),
1375                err => panic!("Unexpected error {err}"),
1376            }
1377
1378            let bad_idx: CustomResult<String> =
1379                db.query_row_and_then(query, [], |row| row.get(3).map_err(CustomError::Sqlite));
1380
1381            match bad_idx.unwrap_err() {
1382                CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (),
1383                err => panic!("Unexpected error {err}"),
1384            }
1385
1386            let non_sqlite_err: CustomResult<String> =
1387                db.query_row_and_then(query, [], |_| Err(CustomError::SomeError));
1388
1389            match non_sqlite_err.unwrap_err() {
1390                CustomError::SomeError => (),
1391                err => panic!("Unexpected error {err}"),
1392            }
1393            Ok(())
1394        }
1395
1396        #[test]
1397        fn test_rows_and_then_with_custom_error() -> Result<()> {
1398            let db = checked_memory_handle();
1399            db.execute_batch("CREATE TABLE test (value INTEGER)")?;
1400            db.execute_batch("INSERT INTO test VALUES (1), (3), (5)")?;
1401
1402            let mut stmt = db.prepare("SELECT value FROM test ORDER BY value")?;
1403            let rows = stmt.query([])?;
1404
1405            // Use and_then to apply custom validation with custom error type
1406            let results: Vec<i32> = rows
1407                .and_then(|row| -> CustomResult<i32> {
1408                    let val: i32 = row.get(0)?; // duckdb::Error automatically converted via From trait
1409                    if val > 10 {
1410                        Err(CustomError::SomeError) // Custom application-specific error
1411                    } else {
1412                        Ok(val)
1413                    }
1414                })
1415                .collect::<CustomResult<Vec<_>>>()
1416                .unwrap();
1417
1418            assert_eq!(results, vec![1, 3, 5]);
1419            Ok(())
1420        }
1421    }
1422
1423    #[test]
1424    fn test_dynamic() -> Result<()> {
1425        let db = checked_memory_handle();
1426        let sql = "BEGIN;
1427                       CREATE TABLE foo(x INTEGER, y TEXT);
1428                       INSERT INTO foo VALUES(4, 'hello');
1429                       END;";
1430        db.execute_batch(sql)?;
1431
1432        db.query_row("SELECT * FROM foo", [], |r| {
1433            assert_eq!(2, r.as_ref().column_count());
1434            Ok(())
1435        })
1436    }
1437    #[test]
1438    fn test_dyn_box() -> Result<()> {
1439        let db = checked_memory_handle();
1440        db.execute_batch("CREATE TABLE foo(x INTEGER);")?;
1441        let b: Box<dyn ToSql> = Box::new(5);
1442        db.execute("INSERT INTO foo VALUES(?)", [b])?;
1443        db.query_row("SELECT x FROM foo", [], |r| {
1444            assert_eq!(5, r.get_unwrap::<_, i32>(0));
1445            Ok(())
1446        })
1447    }
1448
1449    #[test]
1450    fn test_alter_table() -> Result<()> {
1451        let db = checked_memory_handle();
1452        db.execute_batch("CREATE TABLE x(t INTEGER);")?;
1453        // `execute_batch` should be used but `execute` should also work
1454        db.execute("ALTER TABLE x RENAME TO y;", [])?;
1455        Ok(())
1456    }
1457
1458    #[test]
1459    fn test_query_arrow_record_batch_small() -> Result<()> {
1460        let db = checked_memory_handle();
1461        let sql = "BEGIN TRANSACTION;
1462                   CREATE TABLE test(t INTEGER);
1463                   INSERT INTO test VALUES (1); INSERT INTO test VALUES (2); INSERT INTO test VALUES (3); INSERT INTO test VALUES (4); INSERT INTO test VALUES (5);
1464                   END TRANSACTION;";
1465        db.execute_batch(sql)?;
1466        let mut stmt = db.prepare("select t from test order by t desc")?;
1467        let mut arr = stmt.query_arrow([])?;
1468
1469        let schema = arr.get_schema();
1470        assert_eq!(schema.fields().len(), 1);
1471        assert_eq!(schema.field(0).name(), "t");
1472        assert_eq!(schema.field(0).data_type(), &DataType::Int32);
1473
1474        let rb = arr.next().unwrap();
1475        let column = rb.column(0).as_any().downcast_ref::<Int32Array>().unwrap();
1476        assert_eq!(column.len(), 5);
1477        assert_eq!(column.value(0), 5);
1478        assert_eq!(column.value(1), 4);
1479        assert_eq!(column.value(2), 3);
1480        assert_eq!(column.value(3), 2);
1481        assert_eq!(column.value(4), 1);
1482
1483        assert!(arr.next().is_none());
1484        Ok(())
1485    }
1486
1487    #[test]
1488    fn test_query_arrow_record_batch_large() -> Result<()> {
1489        let db = checked_memory_handle();
1490        db.execute_batch("BEGIN TRANSACTION")?;
1491        db.execute_batch("CREATE TABLE test(t INTEGER);")?;
1492        for _ in 0..600 {
1493            db.execute_batch("INSERT INTO test VALUES (1); INSERT INTO test VALUES (2); INSERT INTO test VALUES (3); INSERT INTO test VALUES (4); INSERT INTO test VALUES (5);")?;
1494        }
1495        db.execute_batch("END TRANSACTION")?;
1496        let rbs: Vec<RecordBatch> = db.prepare("select t from test order by t")?.query_arrow([])?.collect();
1497        // batch size is not stable
1498        // assert_eq!(rbs.len(), 3);
1499        assert_eq!(rbs.iter().map(|rb| rb.num_rows()).sum::<usize>(), 3000);
1500        assert_eq!(
1501            rbs.iter()
1502                .map(|rb| rb
1503                    .column(0)
1504                    .as_any()
1505                    .downcast_ref::<Int32Array>()
1506                    .unwrap()
1507                    .iter()
1508                    .map(|i| i.unwrap())
1509                    .sum::<i32>())
1510                .sum::<i32>(),
1511            9000
1512        );
1513        Ok(())
1514    }
1515
1516    #[test]
1517    fn test_stream_arrow_with_call() -> Result<()> {
1518        let db = checked_memory_handle();
1519
1520        db.execute_batch(
1521            "CREATE TABLE test_data(id INTEGER, name VARCHAR);
1522             INSERT INTO test_data VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie');",
1523        )?;
1524
1525        db.execute_batch("CREATE MACRO test_func() AS TABLE SELECT * FROM test_data;")?;
1526
1527        let mut stmt = db.prepare("CALL test_func()")?;
1528        let rbs: Vec<RecordBatch> = stmt.stream_arrow([])?.collect();
1529
1530        // Verify we got results
1531        assert!(!rbs.is_empty(), "Expected at least one record batch");
1532        let total_rows: usize = rbs.iter().map(|rb| rb.num_rows()).sum();
1533        assert_eq!(total_rows, 3);
1534
1535        let id_column = rbs[0].column(0).as_any().downcast_ref::<Int32Array>().unwrap();
1536        assert_eq!(id_column.value(0), 1);
1537
1538        Ok(())
1539    }
1540
1541    #[test]
1542    fn round_trip_interval() -> Result<()> {
1543        let db = checked_memory_handle();
1544        db.execute_batch("CREATE TABLE foo (t INTERVAL);")?;
1545
1546        let d = Value::Interval {
1547            months: 1,
1548            days: 2,
1549            nanos: 3,
1550        };
1551        db.execute("INSERT INTO foo VALUES (?)", [d])?;
1552
1553        let mut stmt = db.prepare("SELECT t FROM foo")?;
1554        let mut rows = stmt.query([])?;
1555        let row = rows.next()?.unwrap();
1556        let d: Value = row.get_unwrap(0);
1557        assert_eq!(d, d);
1558        Ok(())
1559    }
1560
1561    #[test]
1562    fn test_database_name_to_string() -> Result<()> {
1563        assert_eq!(DatabaseName::Main.to_string(), "main");
1564        assert_eq!(DatabaseName::Temp.to_string(), "temp");
1565        assert_eq!(DatabaseName::Attached("abc").to_string(), "abc");
1566        Ok(())
1567    }
1568
1569    #[test]
1570    fn test_interrupt() -> Result<()> {
1571        let db = checked_memory_handle();
1572        let db_interrupt = db.interrupt_handle();
1573
1574        let (tx, rx) = std::sync::mpsc::channel();
1575        std::thread::spawn(move || {
1576            let mut stmt = db
1577                .prepare("select count(*) from range(10000000) t1, range(1000000) t2")
1578                .unwrap();
1579            tx.send(stmt.execute([])).unwrap();
1580        });
1581
1582        std::thread::sleep(std::time::Duration::from_millis(100));
1583        db_interrupt.interrupt();
1584
1585        let result = rx.recv_timeout(std::time::Duration::from_secs(5)).unwrap();
1586        assert!(result.is_err_and(|err| err.to_string().contains("INTERRUPT")));
1587        Ok(())
1588    }
1589
1590    #[test]
1591    fn test_interrupt_on_dropped_db() {
1592        let db = checked_memory_handle();
1593        let db_interrupt = db.interrupt_handle();
1594
1595        drop(db);
1596        db_interrupt.interrupt();
1597    }
1598
1599    #[test]
1600    fn test_arrow_string_view_setting() -> Result<()> {
1601        // Test that only one setting doesn't work (missing arrow_output_version)
1602        {
1603            let config = Config::default().with("produce_arrow_string_view", "true")?;
1604            let conn = Connection::open_in_memory_with_flags(config)?;
1605
1606            let mut query = conn.prepare("SELECT 'test'::varchar AS str")?;
1607            let arrow = query.query_arrow([])?;
1608
1609            let batch = arrow.into_iter().next().expect("Expected at least one batch");
1610            assert_eq!(batch.schema().field(0).data_type(), &DataType::Utf8);
1611        }
1612
1613        {
1614            let config = Config::default()
1615                .with("produce_arrow_string_view", "true")?
1616                .with("arrow_output_version", "1.4")?;
1617            let conn = Connection::open_in_memory_with_flags(config)?;
1618
1619            let mut query = conn.prepare("SELECT 'test'::varchar AS str")?;
1620            let arrow = query.query_arrow([])?;
1621
1622            let batch = arrow.into_iter().next().expect("Expected at least one batch");
1623            assert_eq!(batch.schema().field(0).data_type(), &DataType::Utf8View);
1624        }
1625
1626        Ok(())
1627    }
1628
1629    #[test]
1630    fn test_prepare_multi_statement() -> Result<()> {
1631        let db = checked_memory_handle();
1632
1633        {
1634            let mut stmt =
1635                db.prepare("CREATE TABLE test(x INTEGER); INSERT INTO test VALUES (42); SELECT x FROM test;")?;
1636            let result: i32 = stmt.query_row([], |row| row.get(0))?;
1637            assert_eq!(result, 42);
1638        }
1639
1640        {
1641            let mut stmt = db.prepare(
1642                "CREATE TEMP TABLE temp_data(id INTEGER, value TEXT);
1643                INSERT INTO temp_data VALUES (1, 'first'), (2, 'second');
1644                SELECT COUNT(*) FROM temp_data;",
1645            )?;
1646            let count: i32 = stmt.query_row([], |row| row.get(0))?;
1647            assert_eq!(count, 2);
1648        }
1649
1650        Ok(())
1651    }
1652
1653    #[test]
1654    fn test_pivot_query() -> Result<()> {
1655        let db = checked_memory_handle();
1656
1657        db.execute_batch(
1658            "CREATE TABLE cities(city VARCHAR, year INTEGER, population INTEGER);
1659             INSERT INTO cities VALUES
1660               ('Amsterdam', 2000, 1005),
1661               ('Amsterdam', 2010, 1065),
1662               ('Amsterdam', 2020, 1158),
1663               ('Berlin', 2000, 3382),
1664               ('Berlin', 2010, 3460),
1665               ('Berlin', 2020, 3576);",
1666        )?;
1667
1668        // PIVOT queries internally expand to multiple statements
1669        let mut stmt = db.prepare("PIVOT cities ON year USING sum(population);")?;
1670        let mut rows = stmt.query([])?;
1671
1672        let mut row_count = 0;
1673        while let Some(_row) = rows.next()? {
1674            row_count += 1;
1675        }
1676        assert_eq!(row_count, 2);
1677
1678        Ok(())
1679    }
1680
1681    #[test]
1682    fn test_multiple_memory_databases() -> Result<()> {
1683        // Unnamed :memory: connections are isolated
1684        {
1685            let mem1 = Connection::open_in_memory()?;
1686            let mem2 = Connection::open_in_memory()?;
1687
1688            mem1.execute_batch("CREATE TABLE test (id INTEGER)")?;
1689            mem1.execute("INSERT INTO test VALUES (1)", [])?;
1690
1691            mem2.execute_batch("CREATE TABLE test (id INTEGER)")?;
1692            mem2.execute("INSERT INTO test VALUES (2)", [])?;
1693
1694            let value1: i32 = mem1.query_row("SELECT id FROM test", [], |r| r.get(0))?;
1695            assert_eq!(value1, 1);
1696
1697            let value2: i32 = mem2.query_row("SELECT id FROM test", [], |r| r.get(0))?;
1698            assert_eq!(value2, 2);
1699        }
1700
1701        // try_clone() shares the same database
1702        {
1703            let shared = Connection::open_in_memory()?;
1704
1705            shared.execute_batch("CREATE TABLE shared_table (id INTEGER)")?;
1706            shared.execute("INSERT INTO shared_table VALUES (123)", [])?;
1707
1708            let cloned = shared.try_clone()?;
1709
1710            // Cloned connection can see the original's tables
1711            let value: i32 = cloned.query_row("SELECT id FROM shared_table", [], |r| r.get(0))?;
1712            assert_eq!(value, 123);
1713
1714            cloned.execute("INSERT INTO shared_table VALUES (456)", [])?;
1715
1716            // Original connection can see cloned's insert
1717            let count: i64 = shared.query_row("SELECT COUNT(*) FROM shared_table", [], |r| r.get(0))?;
1718            assert_eq!(count, 2);
1719        }
1720
1721        Ok(())
1722    }
1723
1724    #[test]
1725    fn test_appender_with_catalog() -> Result<()> {
1726        let db = checked_memory_handle();
1727
1728        // Attach a new database to use as a catalog
1729        let temp_dir = tempfile::tempdir().unwrap();
1730        let attached_path = temp_dir.path().join("attached.db");
1731        db.execute_batch(&format!("ATTACH '{}' AS attached_db", attached_path.display()))?;
1732
1733        // Create a table in the attached database
1734        db.execute_batch("CREATE TABLE attached_db.main.test_table (id INTEGER, name TEXT)")?;
1735
1736        // Use appender with catalog
1737        {
1738            let mut app = db.appender_to_catalog_and_db("test_table", "attached_db", "main")?;
1739            app.append_row(params![1, "Alice"])?;
1740            app.append_row(params![2, "Bob"])?;
1741            app.append_row(params![3, "Charlie"])?;
1742        }
1743
1744        // Verify data was inserted into the correct table
1745        let count: i64 = db.query_row("SELECT COUNT(*) FROM attached_db.main.test_table", [], |r| r.get(0))?;
1746        assert_eq!(count, 3);
1747
1748        let name: String = db.query_row("SELECT name FROM attached_db.main.test_table WHERE id = ?", [2], |r| {
1749            r.get(0)
1750        })?;
1751        assert_eq!(name, "Bob");
1752
1753        Ok(())
1754    }
1755
1756    #[test]
1757    fn test_appender_with_catalog_multiple_schemas() -> Result<()> {
1758        let db = checked_memory_handle();
1759
1760        // Attach a new database
1761        let temp_dir = tempfile::tempdir().unwrap();
1762        let attached_path = temp_dir.path().join("multi_schema.db");
1763        db.execute_batch(&format!("ATTACH '{}' AS my_catalog", attached_path.display()))?;
1764
1765        // Create multiple schemas and tables
1766        db.execute_batch("CREATE SCHEMA my_catalog.schema1")?;
1767        db.execute_batch("CREATE SCHEMA my_catalog.schema2")?;
1768        db.execute_batch("CREATE TABLE my_catalog.schema1.data (value INTEGER)")?;
1769        db.execute_batch("CREATE TABLE my_catalog.schema2.data (value INTEGER)")?;
1770
1771        // Append to schema1
1772        {
1773            let mut app = db.appender_to_catalog_and_db("data", "my_catalog", "schema1")?;
1774            app.append_rows([[10], [20], [30]])?;
1775        }
1776
1777        // Append to schema2
1778        {
1779            let mut app = db.appender_to_catalog_and_db("data", "my_catalog", "schema2")?;
1780            app.append_rows([[100], [200]])?;
1781        }
1782
1783        // Verify data in schema1
1784        let sum1: i64 = db.query_row("SELECT SUM(value) FROM my_catalog.schema1.data", [], |r| r.get(0))?;
1785        assert_eq!(sum1, 60);
1786
1787        // Verify data in schema2
1788        let sum2: i64 = db.query_row("SELECT SUM(value) FROM my_catalog.schema2.data", [], |r| r.get(0))?;
1789        assert_eq!(sum2, 300);
1790
1791        Ok(())
1792    }
1793
1794    #[test]
1795    fn test_appender_with_catalog_main_vs_attached() -> Result<()> {
1796        let db = checked_memory_handle();
1797
1798        // Create table in main database
1799        db.execute_batch("CREATE TABLE test (id INTEGER)")?;
1800
1801        // Attach another database
1802        let temp_dir = tempfile::tempdir().unwrap();
1803        let attached_path = temp_dir.path().join("other.db");
1804        db.execute_batch(&format!("ATTACH '{}' AS other_db", attached_path.display()))?;
1805        db.execute_batch("CREATE TABLE other_db.main.test (id INTEGER)")?;
1806
1807        // Append to main catalog (memory)
1808        {
1809            let mut app = db.appender_to_catalog_and_db("test", "memory", "main")?;
1810            app.append_rows([[1], [2]])?;
1811        }
1812
1813        // Append to attached catalog
1814        {
1815            let mut app = db.appender_to_catalog_and_db("test", "other_db", "main")?;
1816            app.append_rows([[100], [200]])?;
1817        }
1818
1819        // Verify main database
1820        let count_main: i64 = db.query_row("SELECT COUNT(*) FROM test", [], |r| r.get(0))?;
1821        assert_eq!(count_main, 2);
1822
1823        // Verify attached database
1824        let count_attached: i64 = db.query_row("SELECT COUNT(*) FROM other_db.main.test", [], |r| r.get(0))?;
1825        assert_eq!(count_attached, 2);
1826
1827        Ok(())
1828    }
1829
1830    #[test]
1831    fn test_appender_with_catalog_error_invalid_catalog() -> Result<()> {
1832        let db = checked_memory_handle();
1833
1834        // Try to create appender with non-existent catalog
1835        let result = db.appender_to_catalog_and_db("test", "nonexistent_catalog", "main");
1836        assert!(result.is_err());
1837
1838        Ok(())
1839    }
1840
1841    #[test]
1842    fn test_appender_with_catalog_error_invalid_schema() -> Result<()> {
1843        let db = checked_memory_handle();
1844
1845        // Attach a database
1846        let temp_dir = tempfile::tempdir().unwrap();
1847        let attached_path = temp_dir.path().join("test.db");
1848        db.execute_batch(&format!("ATTACH '{}' AS my_db", attached_path.display()))?;
1849
1850        db.execute_batch("CREATE TABLE my_db.main.test (id INTEGER)")?;
1851
1852        // Try to create appender with non-existent schema
1853        let result = db.appender_to_catalog_and_db("test", "my_db", "nonexistent_schema");
1854        assert!(result.is_err());
1855
1856        Ok(())
1857    }
1858
1859    #[test]
1860    fn test_appender_with_catalog_flush() -> Result<()> {
1861        let db = checked_memory_handle();
1862
1863        // Attach database
1864        let temp_dir = tempfile::tempdir().unwrap();
1865        let attached_path = temp_dir.path().join("flush_test.db");
1866        db.execute_batch(&format!("ATTACH '{}' AS flush_db", attached_path.display()))?;
1867
1868        db.execute_batch("CREATE TABLE flush_db.main.test (id INTEGER)")?;
1869
1870        // Use appender with explicit flush
1871        {
1872            let mut app = db.appender_to_catalog_and_db("test", "flush_db", "main")?;
1873            app.append_row([1])?;
1874            app.append_row([2])?;
1875            app.flush()?;
1876            app.append_row([3])?;
1877            app.flush()?;
1878        }
1879
1880        // Verify all rows were flushed
1881        let count: i64 = db.query_row("SELECT COUNT(*) FROM flush_db.main.test", [], |r| r.get(0))?;
1882        assert_eq!(count, 3);
1883
1884        Ok(())
1885    }
1886
1887    /// Enum values should reflect actual row data, not just iterate over enum variants.
1888    #[test]
1889    fn test_enum_read() -> Result<()> {
1890        let conn = Connection::open_in_memory()?;
1891        conn.execute_batch(
1892            r#"
1893            CREATE TABLE stats (
1894                name ENUM('CA', 'NY'),
1895                value INTEGER,
1896            );
1897            INSERT INTO stats VALUES ('CA', 10), ('CA', 20), ('NY', 4);
1898            "#,
1899        )?;
1900
1901        let mut stmt = conn.prepare("SELECT * FROM stats")?;
1902        let results: Vec<(String, i32)> = stmt
1903            .query_map([], |row| {
1904                let name: String = row.get(0)?;
1905                let value: i32 = row.get(1)?;
1906                Ok((name, value))
1907            })?
1908            .map(|r| r.unwrap())
1909            .collect();
1910
1911        assert_eq!(results.len(), 3);
1912        assert_eq!(results[0], ("CA".to_string(), 10));
1913        assert_eq!(results[1], ("CA".to_string(), 20));
1914        assert_eq!(results[2], ("NY".to_string(), 4));
1915        Ok(())
1916    }
1917
1918    #[test]
1919    fn test_enum_read_nullable() -> Result<()> {
1920        let conn = Connection::open_in_memory()?;
1921        conn.execute_batch(
1922            r#"
1923            CREATE TABLE stats (name ENUM('CA', 'NY'));
1924            INSERT INTO stats VALUES ('CA'), (NULL), ('NY');
1925            "#,
1926        )?;
1927
1928        let mut stmt = conn.prepare("SELECT name FROM stats")?;
1929        let results: Vec<Option<String>> = stmt.query_map([], |row| row.get(0))?.map(|r| r.unwrap()).collect();
1930
1931        assert_eq!(results, vec![Some("CA".into()), None, Some("NY".into())]);
1932        Ok(())
1933    }
1934}