Skip to main content

duckdb/
statement.rs

1use std::{convert, ffi::c_void, fmt, mem, os::raw::c_char, ptr, str};
2
3use arrow::{array::StructArray, datatypes::SchemaRef};
4
5use super::{AndThenRows, Connection, Error, MappedRows, Params, RawStatement, Result, Row, Rows, ValueRef, ffi};
6use crate::core::LogicalTypeId;
7#[cfg(feature = "polars")]
8use crate::polars_dataframe::Polars;
9use crate::{
10    arrow_batch::{Arrow, ArrowStream},
11    error::result_from_duckdb_prepare,
12    types::{
13        ToSql, ToSqlOutput, binding_unsupported_value, to_duckdb_hugeint, to_duckdb_uhugeint, value_ref_from_value,
14    },
15};
16#[cfg(feature = "polars")]
17use polars_core::utils::arrow as polars_arrow;
18
19/// A prepared statement.
20///
21/// # Thread Safety
22///
23/// `Statement` is neither `Send` nor `Sync`:
24/// - Not `Send` because it holds a reference to `Connection`, which is `!Sync`
25/// - Not `Sync` because DuckDB prepared statements don't support concurrent access
26///
27/// See the [DuckDB concurrency documentation](https://duckdb.org/docs/stable/connect/concurrency.html) for more details.
28pub struct Statement<'conn> {
29    conn: &'conn Connection,
30    pub(crate) stmt: RawStatement,
31}
32
33impl Statement<'_> {
34    /// Execute the prepared statement.
35    ///
36    /// On success, returns the number of rows that were changed or inserted or
37    /// deleted.
38    ///
39    /// ## Example
40    ///
41    /// ### Use with positional parameters
42    ///
43    /// ```rust,no_run
44    /// # use duckdb::{Connection, Result, params};
45    /// fn update_rows(conn: &Connection) -> Result<()> {
46    ///     let mut stmt = conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?")?;
47    ///     // The `duckdb::params!` macro is mostly useful when the parameters do not
48    ///     // all have the same type, or if there are more than 32 parameters
49    ///     // at once.
50    ///     stmt.execute(params![1i32])?;
51    ///     // However, it's not required, many cases are fine as:
52    ///     stmt.execute(&[&2i32])?;
53    ///     // Or even:
54    ///     stmt.execute([2i32])?;
55    ///     Ok(())
56    /// }
57    /// ```
58    ///
59    /// ### Use without parameters
60    ///
61    /// ```rust,no_run
62    /// # use duckdb::{Connection, Result, params};
63    /// fn delete_all(conn: &Connection) -> Result<()> {
64    ///     let mut stmt = conn.prepare("DELETE FROM users")?;
65    ///     stmt.execute([])?;
66    ///     Ok(())
67    /// }
68    /// ```
69    ///
70    /// # Failure
71    ///
72    /// Will return `Err` if binding parameters fails, the executed statement
73    /// returns rows (in which case `query` should be used instead), or the
74    /// underlying DuckDB call fails.
75    #[inline]
76    pub fn execute<P: Params>(&mut self, params: P) -> Result<usize> {
77        params.__bind_in(self)?;
78        self.execute_with_bound_parameters()
79    }
80
81    /// Execute an INSERT.
82    ///
83    /// # Note
84    ///
85    /// This function is a convenience wrapper around
86    /// [`execute()`](Statement::execute) intended for queries that insert a
87    /// single item. It is possible to misuse this function in a way that it
88    /// cannot detect, such as by calling it on a statement which _updates_
89    /// a single item rather than inserting one. Please don't do that.
90    ///
91    /// # Failure
92    ///
93    /// Will return `Err` if no row is inserted or many rows are inserted.
94    #[inline]
95    pub fn insert<P: Params>(&mut self, params: P) -> Result<()> {
96        let changes = self.execute(params)?;
97        match changes {
98            1 => Ok(()),
99            _ => Err(Error::StatementChangedRows(changes)),
100        }
101    }
102
103    /// Execute the prepared statement, returning a handle to the resulting
104    /// vector of arrow RecordBatch
105    ///
106    /// The result is fully materialized on execution, so memory usage is
107    /// proportional to the size of the result set. For large results, see
108    /// [`stream_arrow`](Self::stream_arrow).
109    ///
110    /// ## Example
111    ///
112    /// ```rust,no_run
113    /// # use duckdb::{Result, Connection};
114    /// # use arrow::record_batch::RecordBatch;
115    /// fn get_arrow_data(conn: &Connection) -> Result<Vec<RecordBatch>> {
116    ///     Ok(conn.prepare("SELECT * FROM test")?.query_arrow([])?.collect())
117    /// }
118    /// ```
119    ///
120    /// # Failure
121    ///
122    /// Will return `Err` if binding parameters fails, execution fails, or
123    /// DuckDB reports unsupported executed-result metadata.
124    #[inline]
125    pub fn query_arrow<P: Params>(&mut self, params: P) -> Result<Arrow<'_>> {
126        self.execute(params)?;
127        Ok(Arrow::new(self))
128    }
129
130    /// Execute the prepared statement, returning a handle to the resulting
131    /// vector of arrow RecordBatch in streaming way
132    ///
133    /// Unlike [`query_arrow`](Self::query_arrow), chunks are fetched lazily
134    /// as the iterator is consumed instead of buffering the full result on
135    /// the client side. Note that DuckDB may still materialize the result
136    /// internally for some statements (e.g. CALL). The schema is available
137    /// via [`Arrow::get_schema`].
138    ///
139    /// ## Example
140    ///
141    /// ```rust,no_run
142    /// # use duckdb::{Result, Connection};
143    /// # use arrow::record_batch::RecordBatch;
144    /// fn get_arrow_data(conn: &Connection) -> Result<Vec<RecordBatch>> {
145    ///     Ok(conn.prepare("SELECT * FROM test")?.stream_arrow([])?.collect())
146    /// }
147    /// ```
148    ///
149    /// # Failure
150    ///
151    /// Same failure modes as [`query_arrow`](Self::query_arrow). In addition,
152    /// the returned iterator panics if fetching or Arrow conversion fails
153    /// after execution has started.
154    #[inline]
155    pub fn stream_arrow<P: Params>(&mut self, params: P) -> Result<ArrowStream<'_>> {
156        params.__bind_in(self)?;
157        self.stmt.execute_streaming()?;
158        Ok(ArrowStream::new(self))
159    }
160
161    /// Execute the prepared statement, returning a handle to the resulting
162    /// vector of polars DataFrame.
163    ///
164    /// ## Example
165    ///
166    /// ```rust,no_run
167    /// # use duckdb::{Result, Connection};
168    /// # use polars::prelude::DataFrame;
169    ///
170    /// fn get_polars_dfs(conn: &Connection) -> Result<Vec<DataFrame>> {
171    ///     let dfs: Vec<DataFrame> = conn
172    ///         .prepare("SELECT * FROM test")?
173    ///         .query_polars([])?
174    ///         .collect();
175    ///
176    ///     Ok(dfs)
177    /// }
178    /// ```
179    ///
180    /// To derive a DataFrame from Vec\<DataFrame>, we can use function
181    /// [polars_core::utils::accumulate_dataframes_vertical_unchecked](https://docs.rs/polars-core/latest/polars_core/utils/fn.accumulate_dataframes_vertical_unchecked.html).
182    ///
183    /// ```rust,no_run
184    /// # use duckdb::{Result, Connection};
185    /// # use polars::prelude::DataFrame;
186    /// # use polars_core::utils::accumulate_dataframes_vertical_unchecked;
187    ///
188    /// fn get_polars_df(conn: &Connection) -> Result<DataFrame> {
189    ///     let mut stmt = conn.prepare("SELECT * FROM test")?;
190    ///     let pl = stmt.query_polars([])?;
191    ///     let df = accumulate_dataframes_vertical_unchecked(pl);
192    ///
193    ///    Ok(df)
194    /// }
195    /// ```
196    ///
197    ///
198    #[cfg(feature = "polars")]
199    #[inline]
200    pub fn query_polars<P: Params>(&mut self, params: P) -> Result<Polars<'_>> {
201        self.execute(params)?;
202        Ok(Polars::new(self))
203    }
204
205    /// Execute the prepared statement, returning a handle to the resulting
206    /// rows.
207    ///
208    /// Due to lifetime restricts, the rows handle returned by `query` does not
209    /// implement the `Iterator` trait. Consider using
210    /// [`query_map`](Statement::query_map) or
211    /// [`query_and_then`](Statement::query_and_then) instead, which do.
212    ///
213    /// ## Example
214    ///
215    /// ### Use without parameters
216    ///
217    /// ```rust,no_run
218    /// # use duckdb::{Connection, Result};
219    /// fn get_names(conn: &Connection) -> Result<Vec<String>> {
220    ///     let mut stmt = conn.prepare("SELECT name FROM people")?;
221    ///     let mut rows = stmt.query([])?;
222    ///
223    ///     let mut names = Vec::new();
224    ///     while let Some(row) = rows.next()? {
225    ///         names.push(row.get(0)?);
226    ///     }
227    ///
228    ///     Ok(names)
229    /// }
230    /// ```
231    ///
232    /// ### Use with positional parameters
233    ///
234    /// ```rust,no_run
235    /// # use duckdb::{Connection, Result};
236    /// fn query(conn: &Connection, name: &str) -> Result<()> {
237    ///     let mut stmt = conn.prepare("SELECT * FROM test where name = ?")?;
238    ///     let mut rows = stmt.query(duckdb::params![name])?;
239    ///     while let Some(row) = rows.next()? {
240    ///         // ...
241    ///     }
242    ///     Ok(())
243    /// }
244    /// ```
245    ///
246    /// Or, equivalently (but without the [`duckdb::params!`](crate::params!)
247    /// macro).
248    ///
249    /// ```rust,no_run
250    /// # use duckdb::{Connection, Result};
251    /// fn query(conn: &Connection, name: &str) -> Result<()> {
252    ///     let mut stmt = conn.prepare("SELECT * FROM test where name = ?")?;
253    ///     let mut rows = stmt.query([name])?;
254    ///     while let Some(row) = rows.next()? {
255    ///         // ...
256    ///     }
257    ///     Ok(())
258    /// }
259    /// ```
260    ///
261    /// ## Failure
262    ///
263    /// Will return `Err` if binding parameters fails.
264    #[inline]
265    pub fn query<P: Params>(&mut self, params: P) -> Result<Rows<'_>> {
266        self.execute(params)?;
267        Ok(Rows::new(self))
268    }
269
270    /// Executes the prepared statement and maps a function over the resulting
271    /// rows, returning an iterator over the mapped function results.
272    ///
273    /// `f` is used to transform the _streaming_ iterator into a _standard_
274    /// iterator.
275    ///
276    /// This is equivalent to `stmt.query(params)?.mapped(f)`.
277    ///
278    /// ## Example
279    ///
280    /// ### Use with positional params
281    ///
282    /// ```rust,no_run
283    /// # use duckdb::{Connection, Result};
284    /// fn get_names(conn: &Connection) -> Result<Vec<String>> {
285    ///     let mut stmt = conn.prepare("SELECT name FROM people")?;
286    ///     let rows = stmt.query_map([], |row| row.get(0))?;
287    ///
288    ///     let mut names = Vec::new();
289    ///     for name_result in rows {
290    ///         names.push(name_result?);
291    ///     }
292    ///
293    ///     Ok(names)
294    /// }
295    /// ```
296    ///
297    /// ## Failure
298    ///
299    /// Will return `Err` if binding parameters fails.
300    pub fn query_map<T, P, F>(&mut self, params: P, f: F) -> Result<MappedRows<'_, F>>
301    where
302        P: Params,
303        F: FnMut(&Row<'_>) -> Result<T>,
304    {
305        self.query(params).map(|rows| rows.mapped(f))
306    }
307
308    /// Executes the prepared statement and maps a function over the resulting
309    /// rows, where the function returns a `Result` with `Error` type
310    /// implementing `std::convert::From<Error>` (so errors can be unified).
311    ///
312    /// This is equivalent to `stmt.query(params)?.and_then(f)`.
313    ///
314    /// ## Example
315    ///
316    /// ### Use with positional params
317    ///
318    /// ```no_run
319    /// # use duckdb::{Connection, Result};
320    /// fn get_names(conn: &Connection) -> Result<Vec<String>> {
321    ///     let mut stmt = conn.prepare("SELECT name FROM people WHERE id = ?")?;
322    ///     let rows = stmt.query_and_then(["one"], |row| row.get::<_, String>(0))?;
323    ///
324    ///     let mut persons = Vec::new();
325    ///     for person_result in rows {
326    ///         persons.push(person_result?);
327    ///     }
328    ///
329    ///     Ok(persons)
330    /// }
331    /// ```
332    ///
333    /// # Failure
334    ///
335    /// Will return `Err` if binding parameters fails.
336    #[inline]
337    pub fn query_and_then<T, E, P, F>(&mut self, params: P, f: F) -> Result<AndThenRows<'_, F>>
338    where
339        P: Params,
340        E: convert::From<Error>,
341        F: FnMut(&Row<'_>) -> Result<T, E>,
342    {
343        self.query(params).map(|rows| rows.and_then(f))
344    }
345
346    /// Return `true` if a query in the SQL statement it executes returns one
347    /// or more rows and `false` if the SQL returns an empty set.
348    #[inline]
349    pub fn exists<P: Params>(&mut self, params: P) -> Result<bool> {
350        let mut rows = self.query(params)?;
351        let exists = rows.next()?.is_some();
352        Ok(exists)
353    }
354
355    /// Convenience method to execute a query that is expected to return a
356    /// single row.
357    ///
358    /// If the query returns more than one row, all rows except the first are
359    /// ignored.
360    ///
361    /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the
362    /// query truly is optional, you can call
363    /// [`.optional()`](crate::OptionalExt::optional) on the result of
364    /// this to get a `Result<Option<T>>` (requires that the trait
365    /// `duckdb::OptionalExt` is imported).
366    ///
367    /// # Failure
368    ///
369    /// Will return `Err` if the underlying DuckDB call fails.
370    pub fn query_row<T, P, F>(&mut self, params: P, f: F) -> Result<T>
371    where
372        P: Params,
373        F: FnOnce(&Row<'_>) -> Result<T>,
374    {
375        self.query(params)?.get_expected_row().and_then(f)
376    }
377
378    /// Convenience method to execute a query that is expected to return exactly
379    /// one row.
380    ///
381    /// Returns `Err(QueryReturnedMoreThanOneRow)` if the query returns more than one row.
382    ///
383    /// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the
384    /// query truly is optional, you can call
385    /// [`.optional()`](crate::OptionalExt::optional) on the result of
386    /// this to get a `Result<Option<T>>` (requires that the trait
387    /// `duckdb::OptionalExt` is imported).
388    ///
389    /// # Failure
390    ///
391    /// Will return `Err` if the underlying DuckDB call fails.
392    pub fn query_one<T, P, F>(&mut self, params: P, f: F) -> Result<T>
393    where
394        P: Params,
395        F: FnOnce(&Row<'_>) -> Result<T>,
396    {
397        let mut rows = self.query(params)?;
398        let row = rows.get_expected_row().and_then(f)?;
399        if rows.next()?.is_some() {
400            return Err(Error::QueryReturnedMoreThanOneRow);
401        }
402        Ok(row)
403    }
404
405    /// Return the row count
406    #[inline]
407    pub fn row_count(&self) -> usize {
408        self.stmt.row_count()
409    }
410
411    /// Get next batch records in arrow-rs.
412    ///
413    /// Returns `Err` if DuckDB cannot fetch the next result chunk or convert
414    /// it to Arrow.
415    #[inline]
416    pub fn step(&self) -> Result<Option<StructArray>> {
417        self.stmt.step()
418    }
419
420    #[cfg(feature = "polars")]
421    #[inline]
422    pub(crate) fn step_polars(&self) -> Result<Option<polars_arrow::array::StructArray>> {
423        self.stmt.step_polars()
424    }
425
426    #[inline]
427    pub(crate) fn bind_parameters<P>(&mut self, params: P) -> Result<()>
428    where
429        P: IntoIterator,
430        P::Item: ToSql,
431    {
432        let result = self.try_bind_parameters(params);
433        if result.is_err() {
434            let _ = self.stmt.clear_bindings();
435        }
436        result
437    }
438
439    fn try_bind_parameters<P>(&mut self, params: P) -> Result<()>
440    where
441        P: IntoIterator,
442        P::Item: ToSql,
443    {
444        let expected = self.stmt.bind_parameter_count();
445        let mut index = 0;
446        for p in params.into_iter() {
447            index += 1; // The leftmost SQL parameter has an index of 1.
448            if index > expected {
449                break;
450            }
451            self.bind_parameter(&p, index)?;
452        }
453        if index != expected {
454            Err(Error::InvalidParameterCount(index, expected))
455        } else {
456            Ok(())
457        }
458    }
459
460    /// Return the number of parameters that can be bound to this statement.
461    #[inline]
462    pub fn parameter_count(&self) -> usize {
463        self.stmt.bind_parameter_count()
464    }
465
466    /// Returns the name of the parameter at the given index.
467    ///
468    /// This can be used to query the names of named parameters (e.g., `$param_name`)
469    /// in a prepared statement.
470    ///
471    /// # Arguments
472    ///
473    /// * `one_based_col_index` - One-based parameter index (1 to [`Statement::parameter_count`])
474    ///
475    /// # Returns
476    ///
477    /// * `Ok(String)` - The parameter name (without the `$` prefix for named params, or the numeric index for positional params)
478    /// * `Err(InvalidParameterIndex)` - If the index is out of range
479    ///
480    /// # Example
481    ///
482    /// ```rust,no_run
483    /// # use duckdb::{Connection, Result};
484    /// fn query_parameter_names(conn: &Connection) -> Result<()> {
485    ///     let stmt = conn.prepare("SELECT $foo, $bar")?;
486    ///
487    ///     assert_eq!(stmt.parameter_count(), 2);
488    ///     assert_eq!(stmt.parameter_name(1)?, "foo");
489    ///     assert_eq!(stmt.parameter_name(2)?, "bar");
490    ///
491    ///     Ok(())
492    /// }
493    /// ```
494    #[inline]
495    pub fn parameter_name(&self, idx: usize) -> Result<String> {
496        self.stmt.parameter_name(idx)
497    }
498
499    /// Low level API to directly bind a parameter to a given index.
500    ///
501    /// Note that the index is one-based, that is, the first parameter index is
502    /// 1 and not 0. This is consistent with the DuckDB API and the values given
503    /// to parameters bound as `?NNN`.
504    ///
505    /// The valid values for `one_based_col_index` begin at `1`, and end at
506    /// [`Statement::parameter_count`], inclusive.
507    ///
508    /// # Caveats
509    ///
510    /// This should not generally be used, but is available for special cases
511    /// such as:
512    ///
513    /// - binding parameters where a gap exists.
514    /// - binding named and positional parameters in the same query.
515    /// - separating parameter binding from query execution.
516    ///
517    /// Statements that have had their parameters bound this way should be
518    /// queried or executed by [`Statement::raw_query`] or
519    /// [`Statement::raw_execute`]. Other functions are not guaranteed to work.
520    ///
521    /// # Example
522    ///
523    /// ```rust,no_run
524    /// # use duckdb::{Connection, Result};
525    /// fn query(conn: &Connection) -> Result<()> {
526    ///     let mut stmt = conn.prepare("SELECT * FROM test WHERE name = ? AND value > ?2")?;
527    ///     stmt.raw_bind_parameter(1, "foo")?;
528    ///     stmt.raw_bind_parameter(2, 100)?;
529    ///     let mut rows = stmt.raw_query();
530    ///     while let Some(row) = rows.next()? {
531    ///         // ...
532    ///     }
533    ///     Ok(())
534    /// }
535    /// ```
536    #[inline]
537    pub fn raw_bind_parameter<T: ToSql>(&mut self, one_based_col_index: usize, param: T) -> Result<()> {
538        // This is the same as `bind_parameter` but slightly more ergonomic and
539        // correctly takes `&mut self`.
540        self.bind_parameter(&param, one_based_col_index)
541    }
542
543    /// Low level API to execute a statement given that all parameters were
544    /// bound explicitly with the [`Statement::raw_bind_parameter`] API.
545    ///
546    /// # Caveats
547    ///
548    /// Any unbound parameters will have `NULL` as their value.
549    ///
550    /// This should not generally be used outside of special cases, and
551    /// functions in the [`Statement::execute`] family should be preferred.
552    ///
553    /// # Failure
554    ///
555    /// Will return `Err` if the executed statement returns rows (in which case
556    /// `query` should be used instead), or the underlying DuckDB call fails.
557    #[inline]
558    pub fn raw_execute(&mut self) -> Result<usize> {
559        self.execute_with_bound_parameters()
560    }
561
562    /// Low level API to get `Rows` for this query given that all parameters
563    /// were bound explicitly with the [`Statement::raw_bind_parameter`] API.
564    ///
565    /// # Caveats
566    ///
567    /// Any unbound parameters will have `NULL` as their value.
568    ///
569    /// This should not generally be used outside of special cases, and
570    /// functions in the [`Statement::query`] family should be preferred.
571    ///
572    /// Note that if the SQL does not return results, [`Statement::raw_execute`]
573    /// should be used instead.
574    #[inline]
575    pub fn raw_query(&self) -> Rows<'_> {
576        Rows::new(self)
577    }
578
579    /// Returns the underlying schema of the prepared statement.
580    ///
581    /// # Caveats
582    /// Panics if the query has not been [`execute`](Statement::execute)d yet.
583    #[inline]
584    pub fn schema(&self) -> SchemaRef {
585        self.stmt.schema()
586    }
587
588    #[inline]
589    pub(crate) fn result_column_logical_id(&self, idx: usize) -> Option<LogicalTypeId> {
590        self.stmt.result_column_logical_id(idx)
591    }
592
593    // generic because many of these branches can constant fold away.
594    fn bind_parameter<P: ?Sized + ToSql>(&self, param: &P, col: usize) -> Result<()> {
595        let value = param.to_sql()?;
596
597        let ptr = unsafe { self.stmt.ptr() };
598        let value = match value {
599            ToSqlOutput::Borrowed(v) => v,
600            ToSqlOutput::Owned(ref v) => value_ref_from_value(v, binding_unsupported_value)?,
601        };
602        // TODO: bind more
603        let rc = match value {
604            ValueRef::Null => unsafe { ffi::duckdb_bind_null(ptr, col as u64) },
605            ValueRef::Boolean(i) => unsafe { ffi::duckdb_bind_boolean(ptr, col as u64, i) },
606            ValueRef::TinyInt(i) => unsafe { ffi::duckdb_bind_int8(ptr, col as u64, i) },
607            ValueRef::SmallInt(i) => unsafe { ffi::duckdb_bind_int16(ptr, col as u64, i) },
608            ValueRef::Int(i) => unsafe { ffi::duckdb_bind_int32(ptr, col as u64, i) },
609            ValueRef::BigInt(i) => unsafe { ffi::duckdb_bind_int64(ptr, col as u64, i) },
610            ValueRef::HugeInt(i) => unsafe { ffi::duckdb_bind_hugeint(ptr, col as u64, to_duckdb_hugeint(i)) },
611            ValueRef::UHugeInt(i) => unsafe { ffi::duckdb_bind_uhugeint(ptr, col as u64, to_duckdb_uhugeint(i)) },
612            ValueRef::UTinyInt(i) => unsafe { ffi::duckdb_bind_uint8(ptr, col as u64, i) },
613            ValueRef::USmallInt(i) => unsafe { ffi::duckdb_bind_uint16(ptr, col as u64, i) },
614            ValueRef::UInt(i) => unsafe { ffi::duckdb_bind_uint32(ptr, col as u64, i) },
615            ValueRef::UBigInt(i) => unsafe { ffi::duckdb_bind_uint64(ptr, col as u64, i) },
616            ValueRef::Float(r) => unsafe { ffi::duckdb_bind_float(ptr, col as u64, r) },
617            ValueRef::Double(r) => unsafe { ffi::duckdb_bind_double(ptr, col as u64, r) },
618            ValueRef::Text(s) => unsafe {
619                ffi::duckdb_bind_varchar_length(ptr, col as u64, s.as_ptr() as *const c_char, s.len() as u64)
620            },
621            ValueRef::Blob(b) | ValueRef::Geometry(b) => unsafe {
622                ffi::duckdb_bind_blob(ptr, col as u64, b.as_ptr() as *const c_void, b.len() as u64)
623            },
624            ValueRef::Timestamp(u, i) => unsafe {
625                ffi::duckdb_bind_timestamp(ptr, col as u64, ffi::duckdb_timestamp { micros: u.to_micros(i) })
626            },
627            ValueRef::Interval { months, days, nanos } => unsafe {
628                let micros = nanos / 1_000;
629                ffi::duckdb_bind_interval(ptr, col as u64, ffi::duckdb_interval { months, days, micros })
630            },
631            ValueRef::Date32(days) => unsafe { ffi::duckdb_bind_date(ptr, col as u64, ffi::duckdb_date { days }) },
632            ValueRef::Time64(u, i) => unsafe {
633                ffi::duckdb_bind_time(ptr, col as u64, ffi::duckdb_time { micros: u.to_micros(i) })
634            },
635            ValueRef::Decimal(d) => unsafe {
636                let decimal = crate::types::to_duckdb_decimal(d);
637                ffi::duckdb_bind_decimal(ptr, col as u64, decimal)
638            },
639            _ => {
640                return Err(Error::ToSqlConversionFailure(
641                    binding_unsupported_value(value.data_type()).into(),
642                ));
643            }
644        };
645        result_from_duckdb_prepare(rc, ptr)
646    }
647
648    #[inline]
649    fn execute_with_bound_parameters(&mut self) -> Result<usize> {
650        self.stmt.execute()
651    }
652
653    /// Safety: This is unsafe, because using `sqlite3_stmt` after the
654    /// connection has closed is illegal, but `RawStatement` does not enforce
655    /// this, as it loses our protective `'conn` lifetime bound.
656    #[inline]
657    pub(crate) unsafe fn into_raw(mut self) -> RawStatement {
658        let mut stmt = unsafe { RawStatement::new(ptr::null_mut()) };
659        mem::swap(&mut stmt, &mut self.stmt);
660        stmt
661    }
662}
663
664impl fmt::Debug for Statement<'_> {
665    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
666        let sql = if self.stmt.is_null() {
667            Ok("")
668        } else {
669            str::from_utf8(self.stmt.sql().unwrap().to_bytes())
670        };
671        f.debug_struct("Statement")
672            .field("conn", self.conn)
673            .field("stmt", &self.stmt)
674            .field("sql", &sql)
675            .finish()
676    }
677}
678
679impl Statement<'_> {
680    #[inline]
681    pub(super) fn new(conn: &Connection, stmt: RawStatement) -> Statement<'_> {
682        Statement { conn, stmt }
683    }
684}
685
686#[cfg(test)]
687mod test {
688    use arrow::{
689        array::ListArray,
690        datatypes::{DataType, Int32Type, Schema, SchemaRef},
691        record_batch::RecordBatch,
692    };
693
694    use crate::{
695        Connection, Error, Result, Statement,
696        core::LogicalTypeId,
697        params_from_iter,
698        types::{Decimal, ListType, ToSql, ToSqlOutput, Type, ValueRef},
699    };
700
701    struct BorrowedList(ListArray);
702    impl BorrowedList {
703        fn new() -> Self {
704            Self(ListArray::from_iter_primitive::<Int32Type, _, _>(vec![Some(vec![
705                Some(1),
706                Some(2),
707            ])]))
708        }
709    }
710    impl ToSql for BorrowedList {
711        fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
712            Ok(ToSqlOutput::Borrowed(ValueRef::List(ListType::Regular(&self.0), 0)))
713        }
714    }
715
716    fn assert_binding_list_error(err: Error) {
717        match err {
718            Error::ToSqlConversionFailure(e) => {
719                assert!(
720                    e.to_string().contains("binding List parameters is not yet supported"),
721                    "unexpected message: {e}"
722                );
723            }
724            other => panic!("expected ToSqlConversionFailure, got {other:?}"),
725        }
726    }
727
728    fn assert_variant_decode_error(err: Error, expected_idx: usize) {
729        match err {
730            Error::FromSqlConversionFailure(idx, Type::Variant, e) => {
731                assert_eq!(idx, expected_idx);
732                assert!(
733                    e.to_string().contains("decoding Variant columns is not supported"),
734                    "unexpected message: {e}"
735                );
736            }
737            other => panic!("expected FromSqlConversionFailure for Variant, got {other:?}"),
738        }
739    }
740
741    #[test]
742    fn test_execute() -> Result<()> {
743        let db = Connection::open_in_memory()?;
744        db.execute_batch("CREATE TABLE foo(x INTEGER)")?;
745
746        assert_eq!(db.execute("INSERT INTO foo(x) VALUES (?)", [&2i32])?, 1);
747        assert_eq!(db.execute("INSERT INTO foo(x) VALUES (?)", [&3i32])?, 1);
748
749        // TODO(wangfenjin): No column type for SUM(x)?
750        assert_eq!(
751            5i32,
752            db.query_row::<i32, _, _>("SELECT SUM(x) FROM foo WHERE x > ?", [&0i32], |r| r.get(0))?
753        );
754        assert_eq!(
755            3i32,
756            db.query_row::<i32, _, _>("SELECT SUM(x) FROM foo WHERE x > ?", [&2i32], |r| r.get(0))?
757        );
758        Ok(())
759    }
760
761    #[test]
762    fn test_stmt_execute() -> Result<()> {
763        let db = Connection::open_in_memory()?;
764        let sql = r#"
765        CREATE SEQUENCE seq;
766        CREATE TABLE test (id INTEGER DEFAULT NEXTVAL('seq'), name TEXT NOT NULL, flag INTEGER);
767        "#;
768        db.execute_batch(sql)?;
769
770        let mut stmt = db.prepare("INSERT INTO test (name) VALUES (?)")?;
771        stmt.execute([&"one"])?;
772
773        let mut stmt = db.prepare("SELECT COUNT(*) FROM test WHERE name = ?")?;
774        assert_eq!(1i32, stmt.query_row::<i32, _, _>([&"one"], |r| r.get(0))?);
775        Ok(())
776    }
777
778    #[test]
779    fn test_query() -> Result<()> {
780        let db = Connection::open_in_memory()?;
781        let sql = r#"
782        CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
783        INSERT INTO test(id, name) VALUES (1, 'one');
784        "#;
785        db.execute_batch(sql)?;
786
787        let mut stmt = db.prepare("SELECT id FROM test where name = ?")?;
788        {
789            let id: i32 = stmt.query_one([&"one"], |r| r.get(0))?;
790            assert_eq!(id, 1);
791        }
792        Ok(())
793    }
794
795    #[test]
796    fn test_bind_u128_as_uhugeint() -> Result<()> {
797        const U128_MAX_SQL: &str = "340282366920938463463374607431768211455";
798
799        let db = Connection::open_in_memory()?;
800        let max = u128::MAX;
801
802        let typ: String = db.query_row("SELECT typeof(?)", [&max], |row| row.get(0))?;
803        assert_eq!(typ, "UHUGEINT");
804
805        let equals_max: bool = db.query_row(&format!("SELECT ? = ({U128_MAX_SQL})::UHUGEINT"), [&max], |row| {
806            row.get(0)
807        })?;
808        assert!(equals_max);
809
810        Ok(())
811    }
812
813    #[test]
814    fn test_query_and_then() -> Result<()> {
815        let db = Connection::open_in_memory()?;
816        let sql = r#"
817        CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
818        INSERT INTO test(id, name) VALUES (1, 'one');
819        INSERT INTO test(id, name) VALUES (2, 'one');
820        "#;
821        db.execute_batch(sql)?;
822
823        let mut stmt = db.prepare("SELECT id FROM test where name = ? ORDER BY id ASC")?;
824        let mut rows = stmt.query_and_then([&"one"], |row| {
825            let id: i32 = row.get(0)?;
826            if id == 1 {
827                Ok(id)
828            } else {
829                Err(Error::ExecuteReturnedResults)
830            }
831        })?;
832
833        // first row should be Ok
834        let doubled_id: i32 = rows.next().unwrap()?;
835        assert_eq!(1, doubled_id);
836
837        // second row should be Err
838        #[allow(clippy::match_wild_err_arm)]
839        match rows.next().unwrap() {
840            Ok(_) => panic!("invalid Ok"),
841            Err(Error::ExecuteReturnedResults) => (),
842            Err(_) => panic!("invalid Err"),
843        }
844        Ok(())
845    }
846
847    #[test]
848    fn test_unbound_parameters_are_error() -> Result<()> {
849        let db = Connection::open_in_memory()?;
850        let sql = "CREATE TABLE test (x TEXT, y TEXT)";
851        db.execute_batch(sql)?;
852
853        let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (?, ?)")?;
854        assert!(stmt.execute([&"one"]).is_err());
855        Ok(())
856    }
857
858    #[test]
859    fn test_insert_empty_text_is_none() -> Result<()> {
860        let db = Connection::open_in_memory()?;
861        let sql = "CREATE TABLE test (x TEXT, y TEXT)";
862        db.execute_batch(sql)?;
863
864        let mut stmt = db.prepare("INSERT INTO test (x) VALUES (?)")?;
865        stmt.execute([&"one"])?;
866
867        let result: Option<String> = db.query_row("SELECT y FROM test WHERE x = 'one'", [], |row| row.get(0))?;
868        assert!(result.is_none());
869        Ok(())
870    }
871
872    #[test]
873    fn test_raw_binding() -> Result<()> {
874        let db = Connection::open_in_memory()?;
875        db.execute_batch("CREATE TABLE test (name TEXT, value INTEGER)")?;
876        {
877            let mut stmt = db.prepare("INSERT INTO test (name, value) VALUES (?, ?)")?;
878
879            stmt.raw_bind_parameter(2, 50i32)?;
880            stmt.raw_bind_parameter(1, "example")?;
881            let n = stmt.raw_execute()?;
882            assert_eq!(n, 1);
883        }
884
885        {
886            let mut stmt = db.prepare("SELECT name, value FROM test WHERE value = ?")?;
887            stmt.raw_bind_parameter(1, 50)?;
888            stmt.raw_execute()?;
889            let mut rows = stmt.raw_query();
890            {
891                let row = rows.next()?.unwrap();
892                let name: String = row.get(0)?;
893                assert_eq!(name, "example");
894                let value: i32 = row.get(1)?;
895                assert_eq!(value, 50);
896            }
897            assert!(rows.next()?.is_none());
898        }
899
900        {
901            let db = Connection::open_in_memory()?;
902            db.execute_batch("CREATE TABLE test (name TEXT, value UINTEGER)")?;
903            let mut stmt = db.prepare("INSERT INTO test(name, value) VALUES (?, ?)")?;
904            stmt.raw_bind_parameter(1, "negative")?;
905            stmt.raw_bind_parameter(2, u32::MAX)?;
906            let n = stmt.raw_execute()?;
907            assert_eq!(n, 1);
908            assert_eq!(
909                u32::MAX,
910                db.query_row::<u32, _, _>("SELECT value FROM test", [], |r| r.get(0))?
911            );
912        }
913
914        {
915            let db = Connection::open_in_memory()?;
916            db.execute_batch("CREATE TABLE test (name TEXT, value UBIGINT)")?;
917            let mut stmt = db.prepare("INSERT INTO test(name, value) VALUES (?, ?)")?;
918            stmt.raw_bind_parameter(1, "negative")?;
919            stmt.raw_bind_parameter(2, u64::MAX)?;
920            let n = stmt.raw_execute()?;
921            assert_eq!(n, 1);
922            assert_eq!(
923                u64::MAX,
924                db.query_row::<u64, _, _>("SELECT value FROM test", [], |r| r.get(0))?
925            );
926        }
927
928        Ok(())
929    }
930
931    #[test]
932    #[cfg_attr(windows, ignore = "Windows doesn't allow concurrent writes to a file")]
933    fn test_insert_duplicate() -> Result<()> {
934        let db = Connection::open_in_memory()?;
935        db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")?;
936        let mut stmt = db.prepare("INSERT INTO foo (x) VALUES (?)")?;
937        // TODO(wangfenjin): currently always 1
938        stmt.insert([1i32])?;
939        stmt.insert([2i32])?;
940        assert!(stmt.insert([1i32]).is_err());
941        let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")?;
942        match multi.insert([]).unwrap_err() {
943            Error::StatementChangedRows(2) => (),
944            err => panic!("Unexpected error {err}"),
945        }
946        Ok(())
947    }
948
949    #[test]
950    fn test_insert_different_tables() -> Result<()> {
951        // Test for https://github.com/duckdb/duckdb/issues/171
952        let db = Connection::open_in_memory()?;
953        db.execute_batch(
954            r"
955            CREATE TABLE foo(x INTEGER);
956            CREATE TABLE bar(x INTEGER);
957        ",
958        )?;
959
960        db.prepare("INSERT INTO foo VALUES (10)")?.insert([])?;
961        db.prepare("INSERT INTO bar VALUES (10)")?.insert([])?;
962        Ok(())
963    }
964
965    // When using RETURNING clauses, DuckDB core treats the statement as a query result instead of a modification
966    // statement. This causes execute() to return 0 changed rows and insert() to fail with an error.
967    // This test demonstrates current behavior and proper usage patterns for RETURNING clauses.
968    #[test]
969    fn test_insert_with_returning_clause() -> Result<()> {
970        let db = Connection::open_in_memory()?;
971        db.execute_batch(
972            "CREATE SEQUENCE location_id_seq START WITH 1 INCREMENT BY 1;
973             CREATE TABLE location (
974                 id INTEGER PRIMARY KEY DEFAULT nextval('location_id_seq'),
975                 name TEXT NOT NULL
976             )",
977        )?;
978
979        // INSERT without RETURNING using execute
980        let changes = db.execute("INSERT INTO location (name) VALUES (?)", ["test1"])?;
981        assert_eq!(changes, 1);
982
983        // INSERT with RETURNING using execute - returns 0 (known limitation)
984        let changes = db.execute("INSERT INTO location (name) VALUES (?) RETURNING id", ["test2"])?;
985        assert_eq!(changes, 0);
986
987        // Verify the row was actually inserted despite returning 0
988        let count: i64 = db.query_row("SELECT COUNT(*) FROM location", [], |r| r.get(0))?;
989        assert_eq!(count, 2);
990
991        // INSERT without RETURNING using insert
992        let mut stmt = db.prepare("INSERT INTO location (name) VALUES (?)")?;
993        stmt.insert(["test3"])?;
994
995        // INSERT with RETURNING using insert - fails (known limitation)
996        let mut stmt = db.prepare("INSERT INTO location (name) VALUES (?) RETURNING id")?;
997        let result = stmt.insert(["test4"]);
998        assert!(matches!(result, Err(Error::StatementChangedRows(0))));
999
1000        // Verify the row was still inserted despite the error
1001        let count: i64 = db.query_row("SELECT COUNT(*) FROM location", [], |r| r.get(0))?;
1002        assert_eq!(count, 4);
1003
1004        // Proper way to use RETURNING - with query_row
1005        let id: i64 = db.query_row("INSERT INTO location (name) VALUES (?) RETURNING id", ["test5"], |r| {
1006            r.get(0)
1007        })?;
1008        assert_eq!(id, 5);
1009
1010        // Proper way to use RETURNING - with query_map
1011        let mut stmt = db.prepare("INSERT INTO location (name) VALUES (?) RETURNING id")?;
1012        let ids: Vec<i64> = stmt
1013            .query_map(["test6"], |row| row.get(0))?
1014            .collect::<Result<Vec<_>>>()?;
1015        assert_eq!(ids.len(), 1);
1016        assert_eq!(ids[0], 6);
1017
1018        // Proper way to use RETURNING - with query_one
1019        let id: i64 = db
1020            .prepare("INSERT INTO location (name) VALUES (?) RETURNING id")?
1021            .query_one(["test7"], |r| r.get(0))?;
1022        assert_eq!(id, 7);
1023
1024        // Multiple RETURNING columns
1025        let (id, name): (i64, String) = db.query_row(
1026            "INSERT INTO location (name) VALUES (?) RETURNING id, name",
1027            ["test8"],
1028            |r| Ok((r.get(0)?, r.get(1)?)),
1029        )?;
1030        assert_eq!(id, 8);
1031        assert_eq!(name, "test8");
1032
1033        Ok(())
1034    }
1035
1036    #[test]
1037    fn test_exists() -> Result<()> {
1038        let db = Connection::open_in_memory()?;
1039        let sql = "BEGIN;
1040                   CREATE TABLE foo(x INTEGER);
1041                   INSERT INTO foo VALUES(1);
1042                   INSERT INTO foo VALUES(2);
1043                   END;";
1044        db.execute_batch(sql)?;
1045        let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?")?;
1046        assert!(stmt.exists([1i32])?);
1047        assert!(stmt.exists([2i32])?);
1048        assert!(!stmt.exists([0i32])?);
1049        Ok(())
1050    }
1051
1052    #[test]
1053    fn test_query_row() -> Result<()> {
1054        let db = Connection::open_in_memory()?;
1055        let sql = "BEGIN;
1056                   CREATE TABLE foo(x INTEGER, y INTEGER);
1057                   INSERT INTO foo VALUES(1, 3);
1058                   INSERT INTO foo VALUES(2, 4);
1059                   END;";
1060        db.execute_batch(sql)?;
1061        let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?")?;
1062        let y: Result<i32> = stmt.query_row([1i32], |r| r.get(0));
1063        assert_eq!(3i32, y?);
1064        Ok(())
1065    }
1066
1067    #[test]
1068    fn test_query_one() -> Result<()> {
1069        let db = Connection::open_in_memory()?;
1070        let sql = "BEGIN;
1071                   CREATE TABLE foo(x INTEGER, y INTEGER);
1072                   INSERT INTO foo VALUES(1, 3);
1073                   INSERT INTO foo VALUES(2, 4);
1074                   END;";
1075        db.execute_batch(sql)?;
1076
1077        // Exactly one row
1078        let y: i32 = db
1079            .prepare("SELECT y FROM foo WHERE x = ?")?
1080            .query_one([1], |r| r.get(0))?;
1081        assert_eq!(y, 3);
1082
1083        // No rows
1084        let res: Result<i32> = db
1085            .prepare("SELECT y FROM foo WHERE x = ?")?
1086            .query_one([99], |r| r.get(0));
1087        assert_eq!(res.unwrap_err(), Error::QueryReturnedNoRows);
1088
1089        // Multiple rows
1090        let res: Result<i32> = db.prepare("SELECT y FROM foo")?.query_one([], |r| r.get(0));
1091        assert_eq!(res.unwrap_err(), Error::QueryReturnedMoreThanOneRow);
1092
1093        Ok(())
1094    }
1095
1096    #[test]
1097    fn test_query_one_optional() -> Result<()> {
1098        use crate::OptionalExt;
1099
1100        let db = Connection::open_in_memory()?;
1101        let sql = "BEGIN;
1102                   CREATE TABLE foo(x INTEGER, y INTEGER);
1103                   INSERT INTO foo VALUES(1, 3);
1104                   INSERT INTO foo VALUES(2, 4);
1105                   END;";
1106        db.execute_batch(sql)?;
1107
1108        // Exactly one row
1109        let y: Option<i32> = db
1110            .prepare("SELECT y FROM foo WHERE x = ?")?
1111            .query_one([1], |r| r.get(0))
1112            .optional()?;
1113        assert_eq!(y, Some(3));
1114
1115        // No rows
1116        let y: Option<i32> = db
1117            .prepare("SELECT y FROM foo WHERE x = ?")?
1118            .query_one([99], |r| r.get(0))
1119            .optional()?;
1120        assert_eq!(y, None);
1121
1122        // Multiple rows - should still return error (not converted by optional)
1123        let res = db
1124            .prepare("SELECT y FROM foo")?
1125            .query_one([], |r| r.get::<_, i32>(0))
1126            .optional();
1127        assert_eq!(res.unwrap_err(), Error::QueryReturnedMoreThanOneRow);
1128
1129        Ok(())
1130    }
1131
1132    #[test]
1133    fn test_query_by_column_name() -> Result<()> {
1134        let db = Connection::open_in_memory()?;
1135        let sql = "BEGIN;
1136                   CREATE TABLE foo(x INTEGER, y INTEGER);
1137                   INSERT INTO foo VALUES(1, 3);
1138                   END;";
1139        db.execute_batch(sql)?;
1140        let mut stmt = db.prepare("SELECT y FROM foo")?;
1141        let y: Result<i64> = stmt.query_row([], |r| r.get("y"));
1142        assert_eq!(3i64, y?);
1143        Ok(())
1144    }
1145
1146    #[test]
1147    fn test_get_schema_of_executed_result() -> Result<()> {
1148        use arrow::datatypes::{DataType, Field, Schema};
1149        let db = Connection::open_in_memory()?;
1150        let sql = "BEGIN;
1151                   CREATE TABLE foo(x STRING, y INTEGER);
1152                   INSERT INTO foo VALUES('hello', 3);
1153                   END;";
1154        db.execute_batch(sql)?;
1155        let mut stmt = db.prepare("SELECT x, y FROM foo")?;
1156        let _ = stmt.execute([]);
1157        let schema = stmt.schema();
1158        assert_eq!(
1159            *schema,
1160            Schema::new(vec![
1161                Field::new("x", DataType::Utf8, true),
1162                Field::new("y", DataType::Int32, true)
1163            ])
1164        );
1165        Ok(())
1166    }
1167
1168    #[test]
1169    #[should_panic(expected = "The statement was not executed yet")]
1170    fn test_unexecuted_schema_panics() {
1171        let db = Connection::open_in_memory().unwrap();
1172        let sql = "BEGIN;
1173                   CREATE TABLE foo(x STRING, y INTEGER);
1174                   INSERT INTO foo VALUES('hello', 3);
1175                   END;";
1176        db.execute_batch(sql).unwrap();
1177        let stmt = db.prepare("SELECT x, y FROM foo").unwrap();
1178        let _ = stmt.schema();
1179    }
1180
1181    #[test]
1182    fn test_query_by_column_name_ignore_case() -> Result<()> {
1183        let db = Connection::open_in_memory()?;
1184        let sql = "BEGIN;
1185                   CREATE TABLE foo(x INTEGER, y INTEGER);
1186                   INSERT INTO foo VALUES(1, 3);
1187                   END;";
1188        db.execute_batch(sql)?;
1189        let mut stmt = db.prepare("SELECT y as Y FROM foo")?;
1190        let y: Result<i64> = stmt.query_row([], |r| r.get("y"));
1191        assert_eq!(3i64, y?);
1192        Ok(())
1193    }
1194
1195    #[test]
1196    fn test_bind_parameters() -> Result<()> {
1197        let db = Connection::open_in_memory()?;
1198        // dynamic slice:
1199        db.query_row("SELECT ?1, ?2, ?3", [&1u8 as &dyn ToSql, &"one", &Some("one")], |row| {
1200            row.get::<_, u8>(0)
1201        })?;
1202        // existing collection:
1203        let data = vec![1, 2, 3];
1204        db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| row.get::<_, u8>(0))?;
1205        db.query_row("SELECT ?1, ?2, ?3", params_from_iter(data.as_slice()), |row| {
1206            row.get::<_, u8>(0)
1207        })?;
1208        db.query_row("SELECT ?1, ?2, ?3", params_from_iter(data), |row| row.get::<_, u8>(0))?;
1209
1210        let data: std::collections::BTreeSet<String> =
1211            ["one", "two", "three"].iter().map(|s| (*s).to_string()).collect();
1212        db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| {
1213            row.get::<_, String>(0)
1214        })?;
1215
1216        let data = [0; 3];
1217        db.query_row("SELECT ?1, ?2, ?3", params_from_iter(&data), |row| row.get::<_, u8>(0))?;
1218        db.query_row("SELECT ?1, ?2, ?3", params_from_iter(data.iter()), |row| {
1219            row.get::<_, u8>(0)
1220        })?;
1221        Ok(())
1222    }
1223
1224    #[test]
1225    fn test_named_parameters() -> Result<()> {
1226        use std::collections::HashMap;
1227
1228        let named_params = HashMap::from([("foo", 42), ("bar", 23)]);
1229
1230        let db = Connection::open_in_memory()?;
1231        let sql = r#"SELECT $foo > $bar"#;
1232        let result: bool = db.query_row(sql, &named_params, |row| row.get(0))?;
1233        assert!(result);
1234        Ok(())
1235    }
1236
1237    #[test]
1238    fn test_named_parameters_macro() -> Result<()> {
1239        let db = Connection::open_in_memory()?;
1240        let name = "alice";
1241        let params = crate::named_params! {
1242            "foo": 42,
1243            "name": name,
1244        };
1245        let result: bool = db.query_row("SELECT $foo > 40 AND $name = 'alice'", params, |row| row.get(0))?;
1246        assert!(result);
1247        Ok(())
1248    }
1249
1250    #[test]
1251    fn test_empty_named_parameters_macro() -> Result<()> {
1252        let db = Connection::open_in_memory()?;
1253        let result: i32 = db.query_row("SELECT 1", crate::named_params! {}, |row| row.get(0))?;
1254        assert_eq!(result, 1);
1255        Ok(())
1256    }
1257
1258    #[test]
1259    fn test_named_parameters_repeated_placeholder() -> Result<()> {
1260        use std::collections::HashMap;
1261
1262        let db = Connection::open_in_memory()?;
1263        let stmt = db.prepare("SELECT $foo + $foo")?;
1264        assert_eq!(stmt.parameter_count(), 1);
1265        assert_eq!(stmt.parameter_name(1)?, "foo");
1266
1267        let slice_result: i32 = db.query_row(
1268            "SELECT $foo + $foo",
1269            crate::named_params! {
1270                "foo": 21,
1271            },
1272            |row| row.get(0),
1273        )?;
1274        assert_eq!(slice_result, 42);
1275
1276        let named_params = HashMap::from([("foo", 21)]);
1277        let hashmap_result: i32 = db.query_row("SELECT $foo + $foo", &named_params, |row| row.get(0))?;
1278        assert_eq!(hashmap_result, 42);
1279
1280        Ok(())
1281    }
1282
1283    #[test]
1284    fn test_named_parameters_reject_extra_keys() -> Result<()> {
1285        use std::collections::HashMap;
1286
1287        let named_params = HashMap::from([("foo", 42), ("bar", 23), ("extra", 1)]);
1288
1289        let db = Connection::open_in_memory()?;
1290        let err = db
1291            .query_row("SELECT $foo > $bar", &named_params, |row| row.get::<_, bool>(0))
1292            .unwrap_err();
1293        assert_eq!(err, Error::InvalidParameterName("extra".to_string()));
1294        Ok(())
1295    }
1296
1297    #[test]
1298    fn test_named_parameters_reject_extra_hashmap_key_deterministically() -> Result<()> {
1299        use std::collections::HashMap;
1300
1301        let named_params = HashMap::from([("foo", 42), ("z_extra", 1), ("a_extra", 2)]);
1302
1303        let db = Connection::open_in_memory()?;
1304        let err = db
1305            .query_row("SELECT $foo", &named_params, |row| row.get::<_, i32>(0))
1306            .unwrap_err();
1307        assert_eq!(err, Error::InvalidParameterName("a_extra".to_string()));
1308        Ok(())
1309    }
1310
1311    #[test]
1312    fn test_named_parameters_reject_extra_slice_key() -> Result<()> {
1313        let db = Connection::open_in_memory()?;
1314        let bar = 23;
1315        let params = crate::named_params! {
1316            "foo": 42,
1317            "middle_extra": 0,
1318            "bar": bar,
1319            "first_extra": 1,
1320            "second_extra": 2,
1321        };
1322
1323        let err = db
1324            .query_row("SELECT $foo + $bar", params, |row| row.get::<_, i32>(0))
1325            .unwrap_err();
1326        assert_eq!(err, Error::InvalidParameterName("middle_extra".to_string()));
1327        Ok(())
1328    }
1329
1330    #[test]
1331    fn test_named_parameters_reject_missing_hashmap_key() -> Result<()> {
1332        use std::collections::HashMap;
1333
1334        let named_params = HashMap::from([("foo", 42)]);
1335
1336        let db = Connection::open_in_memory()?;
1337        let err = db
1338            .query_row("SELECT $foo > $bar", &named_params, |row| row.get::<_, bool>(0))
1339            .unwrap_err();
1340        assert_eq!(err, Error::InvalidParameterName("bar".to_string()));
1341        Ok(())
1342    }
1343
1344    #[test]
1345    fn test_named_parameters_reject_missing_slice_key() -> Result<()> {
1346        let db = Connection::open_in_memory()?;
1347        let params = crate::named_params! {
1348            "foo": 42,
1349        };
1350
1351        let err = db
1352            .query_row("SELECT $foo > $bar", params, |row| row.get::<_, bool>(0))
1353            .unwrap_err();
1354        assert_eq!(err, Error::InvalidParameterName("bar".to_string()));
1355        Ok(())
1356    }
1357
1358    #[test]
1359    fn test_named_parameters_reject_duplicate_slice_keys() -> Result<()> {
1360        let db = Connection::open_in_memory()?;
1361        let first = 42;
1362        let second = 23;
1363        let params = &[("foo", &first as &dyn ToSql), ("foo", &second as &dyn ToSql)] as &[(&str, &dyn ToSql)];
1364
1365        let err = db
1366            .query_row("SELECT $foo", params, |row| row.get::<_, i32>(0))
1367            .unwrap_err();
1368        assert_eq!(
1369            err,
1370            Error::InvalidParameterName("duplicate parameter name: foo".to_string())
1371        );
1372        Ok(())
1373    }
1374
1375    #[test]
1376    fn test_named_parameters_reject_positional_placeholders() -> Result<()> {
1377        use std::collections::HashMap;
1378
1379        let named_params = HashMap::from([("1", 42), ("2", 23)]);
1380
1381        let db = Connection::open_in_memory()?;
1382        let err = db
1383            .query_row("SELECT ? > ?", &named_params, |row| row.get::<_, bool>(0))
1384            .unwrap_err();
1385        assert_eq!(
1386            err,
1387            Error::InvalidParameterName("positional parameter 1 cannot be used with named parameters".to_string())
1388        );
1389        Ok(())
1390    }
1391
1392    #[test]
1393    fn test_named_parameters_reject_dollar_number_placeholders() -> Result<()> {
1394        use std::collections::HashMap;
1395
1396        let named_params = HashMap::from([("1", 42), ("2", 23)]);
1397        let db = Connection::open_in_memory()?;
1398
1399        let hashmap_err = db
1400            .query_row("SELECT $1 + $2", &named_params, |row| row.get::<_, i32>(0))
1401            .unwrap_err();
1402        assert_eq!(
1403            hashmap_err,
1404            Error::InvalidParameterName("positional parameter 1 cannot be used with named parameters".to_string())
1405        );
1406
1407        let slice_err = db
1408            .query_row(
1409                "SELECT $1 + $2",
1410                crate::named_params! {
1411                    "1": 42,
1412                    "2": 23,
1413                },
1414                |row| row.get::<_, i32>(0),
1415            )
1416            .unwrap_err();
1417        assert_eq!(
1418            slice_err,
1419            Error::InvalidParameterName("positional parameter 1 cannot be used with named parameters".to_string())
1420        );
1421        Ok(())
1422    }
1423
1424    #[test]
1425    fn test_named_parameters_reject_mixed_placeholders() -> Result<()> {
1426        let db = Connection::open_in_memory()?;
1427        let err = db.prepare("SELECT $foo + ?").unwrap_err();
1428        assert!(err.to_string().contains("Mixing named and positional parameters"));
1429        Ok(())
1430    }
1431
1432    #[test]
1433    fn test_named_parameters_bind_null_values() -> Result<()> {
1434        use std::collections::HashMap;
1435
1436        let db = Connection::open_in_memory()?;
1437        let named_params = HashMap::from([("x", None::<i32>)]);
1438
1439        let hashmap_result: bool = db.query_row("SELECT $x IS NULL", &named_params, |row| row.get(0))?;
1440        assert!(hashmap_result);
1441
1442        let slice_result: bool = db.query_row(
1443            "SELECT $x IS NULL",
1444            crate::named_params! {
1445                "x": Option::<i32>::None,
1446            },
1447            |row| row.get(0),
1448        )?;
1449        assert!(slice_result);
1450
1451        Ok(())
1452    }
1453
1454    #[test]
1455    fn test_named_parameters_string_keys_query_map() -> Result<()> {
1456        use std::collections::HashMap;
1457
1458        let db = Connection::open_in_memory()?;
1459        let params = HashMap::from([("min".to_string(), 2i64), ("max".to_string(), 3i64)]);
1460        let mut stmt = db.prepare("SELECT i FROM range(5) tbl(i) WHERE i BETWEEN $min AND $max ORDER BY i")?;
1461        let rows = stmt.query_map(&params, |row| row.get::<_, i64>(0))?;
1462        let values = rows.collect::<Result<Vec<_>>>()?;
1463
1464        assert_eq!(values, [2, 3]);
1465        Ok(())
1466    }
1467
1468    #[test]
1469    fn test_named_parameters_cow_keys_custom_hasher() -> Result<()> {
1470        use std::{
1471            borrow::Cow,
1472            collections::{HashMap, hash_map::DefaultHasher},
1473            hash::BuildHasherDefault,
1474        };
1475
1476        let db = Connection::open_in_memory()?;
1477        let mut params: HashMap<Cow<'static, str>, i64, BuildHasherDefault<DefaultHasher>> = HashMap::default();
1478        params.insert(Cow::Borrowed("min"), 2);
1479        params.insert(Cow::Owned("max".to_string()), 3);
1480
1481        let mut stmt = db.prepare("SELECT i FROM range(5) tbl(i) WHERE i BETWEEN $min AND $max ORDER BY i")?;
1482        let rows = stmt.query_map(&params, |row| row.get::<_, i64>(0))?;
1483        let values = rows.collect::<Result<Vec<_>>>()?;
1484
1485        assert_eq!(values, [2, 3]);
1486        Ok(())
1487    }
1488
1489    #[test]
1490    fn test_empty_stmt() -> Result<()> {
1491        let conn = Connection::open_in_memory()?;
1492        let stmt = conn.prepare("");
1493        assert!(stmt.is_err());
1494
1495        Ok(())
1496    }
1497
1498    #[test]
1499    fn test_comment_empty_stmt() -> Result<()> {
1500        let conn = Connection::open_in_memory()?;
1501        assert!(conn.prepare("/*SELECT 1;*/").is_err());
1502        Ok(())
1503    }
1504
1505    #[test]
1506    fn test_comment_and_sql_stmt() -> Result<()> {
1507        let conn = Connection::open_in_memory()?;
1508        let mut stmt = conn.prepare("/*...*/ SELECT 1;")?;
1509        stmt.execute([])?;
1510        assert_eq!(1, stmt.column_count());
1511        Ok(())
1512    }
1513
1514    #[test]
1515    fn test_nul_byte() -> Result<()> {
1516        let db = Connection::open_in_memory()?;
1517        let expected = "a\x00b";
1518        let actual: String = db.query_row("SELECT CAST(? AS VARCHAR)", [expected], |row| row.get(0))?;
1519        assert_eq!(expected, actual);
1520        Ok(())
1521    }
1522
1523    #[test]
1524    fn test_parameter_name() -> Result<()> {
1525        let db = Connection::open_in_memory()?;
1526
1527        {
1528            let stmt = db.prepare("SELECT $foo, $bar")?;
1529
1530            assert_eq!(stmt.parameter_count(), 2);
1531            assert_eq!(stmt.parameter_name(1)?, "foo");
1532            assert_eq!(stmt.parameter_name(2)?, "bar");
1533
1534            assert!(matches!(stmt.parameter_name(0), Err(Error::InvalidParameterIndex(0))));
1535            assert!(matches!(
1536                stmt.parameter_name(100),
1537                Err(Error::InvalidParameterIndex(100))
1538            ));
1539        }
1540
1541        // Positional parameters return their index number as the name
1542        {
1543            let stmt = db.prepare("SELECT ?, ?")?;
1544            assert_eq!(stmt.parameter_count(), 2);
1545            assert_eq!(stmt.parameter_name(1)?, "1");
1546            assert_eq!(stmt.parameter_name(2)?, "2");
1547        }
1548
1549        // Numbered positional parameters also return their number
1550        {
1551            let stmt = db.prepare("SELECT ?1, ?2")?;
1552            assert_eq!(stmt.parameter_count(), 2);
1553            assert_eq!(stmt.parameter_name(1)?, "1");
1554            assert_eq!(stmt.parameter_name(2)?, "2");
1555        }
1556
1557        Ok(())
1558    }
1559
1560    #[test]
1561    fn test_bind_named_parameters_manually() -> Result<()> {
1562        use std::collections::HashMap;
1563
1564        let db = Connection::open_in_memory()?;
1565        let mut stmt = db.prepare("SELECT $foo > $bar")?;
1566
1567        let mut params: HashMap<String, i32> = HashMap::new();
1568        params.insert("foo".to_string(), 42);
1569        params.insert("bar".to_string(), 23);
1570
1571        for idx in 1..=stmt.parameter_count() {
1572            let name = stmt.parameter_name(idx)?;
1573            if let Some(value) = params.get(&name) {
1574                stmt.raw_bind_parameter(idx, value)?;
1575            }
1576        }
1577
1578        stmt.raw_execute()?;
1579
1580        let mut rows = stmt.raw_query();
1581        let row = rows.next()?.unwrap();
1582        let result: bool = row.get(0)?;
1583        assert!(result);
1584
1585        Ok(())
1586    }
1587
1588    #[test]
1589    fn test_execute_streaming_error_message() -> Result<()> {
1590        let db = Connection::open_in_memory()?;
1591
1592        // Trigger a conversion error - should fail with a descriptive message
1593        let mut stmt = db.prepare("SELECT CAST('not-a-number' AS INTEGER)")?;
1594        let result = stmt.stmt.execute_streaming();
1595
1596        assert!(result.is_err());
1597        let err = result.unwrap_err();
1598
1599        let error_string = format!("{}", err);
1600        assert!(
1601            error_string.contains("Conversion Error"),
1602            "Expected descriptive error, got: {}",
1603            error_string
1604        );
1605
1606        Ok(())
1607    }
1608
1609    #[test]
1610    fn test_step_after_streaming_materialized_result_fetches_data() -> Result<()> {
1611        use arrow::array::Int32Array;
1612
1613        let db = Connection::open_in_memory()?;
1614        db.execute_batch(
1615            "CREATE TABLE test_data(id INTEGER);
1616             INSERT INTO test_data VALUES (1), (2);
1617             CREATE MACRO test_func() AS TABLE SELECT * FROM test_data;",
1618        )?;
1619
1620        let mut stmt = db.prepare("CALL test_func()")?;
1621        stmt.stmt.execute_streaming()?;
1622
1623        let batch = stmt.stmt.step()?.expect("expected result batch");
1624        let values = batch.column(0).as_any().downcast_ref::<Int32Array>().unwrap();
1625        assert_eq!(1, values.value(0));
1626        Ok(())
1627    }
1628
1629    #[test]
1630    fn test_row_count_after_query_execution() -> Result<()> {
1631        let db = Connection::open_in_memory()?;
1632        let mut stmt = db.prepare("SELECT i FROM range(3000) AS t(i)")?;
1633        {
1634            let rows = stmt.query([])?;
1635            drop(rows);
1636        }
1637
1638        assert_eq!(3000, stmt.row_count());
1639        Ok(())
1640    }
1641
1642    #[test]
1643    fn test_stream_arrow_large_result() -> Result<()> {
1644        use arrow::array::Int64Array;
1645
1646        let db = Connection::open_in_memory()?;
1647        let mut stmt = db.prepare("SELECT i FROM range(3000) AS t(i) ORDER BY i")?;
1648        let mut stream = stmt.stream_arrow([])?;
1649        let batches = stream.by_ref().collect::<Vec<_>>();
1650
1651        assert!(batches.len() > 1);
1652        assert_eq!(3000, batches.iter().map(|batch| batch.num_rows()).sum::<usize>());
1653
1654        let values = batches
1655            .iter()
1656            .flat_map(|batch| {
1657                batch
1658                    .column(0)
1659                    .as_any()
1660                    .downcast_ref::<Int64Array>()
1661                    .unwrap()
1662                    .iter()
1663                    .map(Option::unwrap)
1664            })
1665            .collect::<Vec<_>>();
1666        assert_eq!(0, values[0]);
1667        assert_eq!(2048, values[2048]);
1668        assert_eq!(2999, values[2999]);
1669        assert!(stream.next().is_none());
1670        assert!(stream.next().is_none());
1671
1672        Ok(())
1673    }
1674
1675    #[test]
1676    fn test_stream_arrow_with_params() -> Result<()> {
1677        let db = Connection::open_in_memory()?;
1678        let mut stmt = db.prepare("SELECT i FROM range(3000) AS t(i) WHERE i < ? ORDER BY i")?;
1679        let batches = stmt.stream_arrow([2500_i64])?.collect::<Vec<_>>();
1680
1681        assert!(batches.len() > 1);
1682        assert_eq!(2500, batches.iter().map(|batch| batch.num_rows()).sum::<usize>());
1683        Ok(())
1684    }
1685
1686    const ARROW_EXTENSION_NAME_KEY: &str = "ARROW:extension:name";
1687    const UUID_EXTENSION_NAME: &str = "arrow.uuid";
1688    const UUID_BYTE_WIDTH: i32 = 16;
1689
1690    fn assert_default_uuid_arrow_schema(schema: &Schema) {
1691        let field = schema.field(0);
1692        assert_eq!(field.name(), "id");
1693        assert_eq!(field.data_type(), &DataType::Utf8);
1694        assert!(
1695            !field.metadata().contains_key(ARROW_EXTENSION_NAME_KEY),
1696            "default UUID Arrow export should not carry extension metadata"
1697        );
1698    }
1699
1700    fn assert_lossless_uuid_arrow_schema(schema: &Schema) {
1701        let field = schema.field(0);
1702        assert_eq!(field.name(), "id");
1703        assert_eq!(field.data_type(), &DataType::FixedSizeBinary(UUID_BYTE_WIDTH));
1704        assert_eq!(
1705            field.metadata().get(ARROW_EXTENSION_NAME_KEY).map(String::as_str),
1706            Some(UUID_EXTENSION_NAME)
1707        );
1708    }
1709
1710    fn check_uuid_arrow_schemas(query: impl Fn(&mut Statement<'_>) -> Result<(SchemaRef, RecordBatch)>) -> Result<()> {
1711        for lossless in [false, true] {
1712            let db = Connection::open_in_memory()?;
1713            if lossless {
1714                db.execute_batch("SET arrow_lossless_conversion = true")?;
1715            }
1716            let assert_schema: fn(&Schema) = if lossless {
1717                assert_lossless_uuid_arrow_schema
1718            } else {
1719                assert_default_uuid_arrow_schema
1720            };
1721
1722            let mut stmt = db.prepare("SELECT 'a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8'::UUID AS id")?;
1723            let (schema, batch) = query(&mut stmt)?;
1724            assert_schema(schema.as_ref());
1725            assert_schema(batch.schema().as_ref());
1726        }
1727
1728        Ok(())
1729    }
1730
1731    #[test]
1732    fn test_query_arrow_uuid_schema_with_and_without_lossless_conversion() -> Result<()> {
1733        check_uuid_arrow_schemas(|stmt| {
1734            let arrow = stmt.query_arrow([])?;
1735            let schema = arrow.get_schema();
1736            let batch = arrow.into_iter().next().expect("expected one batch");
1737            Ok((schema, batch))
1738        })
1739    }
1740
1741    #[test]
1742    fn test_stream_arrow_uuid_schema_with_and_without_lossless_conversion() -> Result<()> {
1743        check_uuid_arrow_schemas(|stmt| {
1744            let stream = stmt.stream_arrow([])?;
1745            let schema = stream.get_schema();
1746            let batch = stream.into_iter().next().expect("expected one batch");
1747            Ok((schema, batch))
1748        })
1749    }
1750
1751    #[test]
1752    fn test_streaming_step_surfaces_fetch_error_after_interrupt() -> Result<()> {
1753        let db = Connection::open_in_memory()?;
1754        let interrupt = db.interrupt_handle();
1755        let mut stmt = db.prepare("SELECT i FROM range(100000000) AS t(i)")?;
1756
1757        stmt.stmt.execute_streaming()?;
1758        assert!(stmt.stmt.step()?.is_some());
1759        interrupt.interrupt();
1760
1761        let err = stmt.stmt.step().unwrap_err();
1762        let message = err.to_string();
1763        assert!(
1764            message.contains("Interrupted"),
1765            "expected interrupted fetch error, got: {message}"
1766        );
1767        Ok(())
1768    }
1769
1770    #[test]
1771    fn test_stream_arrow_empty_result_reaches_clean_eof() -> Result<()> {
1772        let db = Connection::open_in_memory()?;
1773        let mut stmt = db.prepare("SELECT 1 AS x WHERE false")?;
1774        let mut stream = stmt.stream_arrow([])?;
1775
1776        let schema = stream.get_schema();
1777        assert_eq!(1, schema.fields().len());
1778        assert_eq!("x", schema.field(0).name());
1779        assert_eq!(&arrow::datatypes::DataType::Int32, schema.field(0).data_type());
1780        assert!(stream.next().is_none());
1781        assert!(stream.next().is_none());
1782        Ok(())
1783    }
1784
1785    #[test]
1786    fn test_stream_arrow_then_query_arrow_replaces_result() -> Result<()> {
1787        use arrow::array::Int64Array;
1788
1789        fn assert_range_batches(batches: &[arrow::record_batch::RecordBatch]) {
1790            assert!(batches.len() > 1);
1791            assert_eq!(3000, batches.iter().map(|batch| batch.num_rows()).sum::<usize>());
1792
1793            let values = batches
1794                .iter()
1795                .flat_map(|batch| {
1796                    batch
1797                        .column(0)
1798                        .as_any()
1799                        .downcast_ref::<Int64Array>()
1800                        .unwrap()
1801                        .iter()
1802                        .map(Option::unwrap)
1803                })
1804                .collect::<Vec<_>>();
1805            assert_eq!(0, values[0]);
1806            assert_eq!(2048, values[2048]);
1807            assert_eq!(2999, values[2999]);
1808        }
1809
1810        let db = Connection::open_in_memory()?;
1811        let mut stmt = db.prepare("SELECT i FROM range(3000) AS t(i) ORDER BY i")?;
1812
1813        {
1814            let batches = stmt.stream_arrow([])?.collect::<Vec<_>>();
1815            assert_range_batches(&batches);
1816        }
1817
1818        {
1819            let batches = stmt.query_arrow([])?.collect::<Vec<_>>();
1820            assert_range_batches(&batches);
1821        }
1822
1823        {
1824            let batches = stmt.stream_arrow([])?.collect::<Vec<_>>();
1825            assert_range_batches(&batches);
1826        }
1827
1828        Ok(())
1829    }
1830
1831    #[test]
1832    fn test_bind_date32() -> Result<()> {
1833        use crate::types::Value;
1834
1835        let db = Connection::open_in_memory()?;
1836        // 19130 days since epoch = 2022-05-18
1837        let result: bool = db.query_row("SELECT ? = DATE '2022-05-18'", [Value::Date32(19130)], |row| row.get(0))?;
1838        assert!(result);
1839        Ok(())
1840    }
1841
1842    #[test]
1843    fn test_bind_time64() -> Result<()> {
1844        use crate::types::{TimeUnit, Value};
1845
1846        let db = Connection::open_in_memory()?;
1847        // 45_045_123_456 micros = 12:30:45.123456
1848        let micros = 45_045_123_456i64;
1849        let result: bool = db.query_row(
1850            "SELECT ? = TIME '12:30:45.123456'",
1851            [Value::Time64(TimeUnit::Microsecond, micros)],
1852            |row| row.get(0),
1853        )?;
1854        assert!(result);
1855        Ok(())
1856    }
1857
1858    #[test]
1859    fn test_execute_tuple() -> Result<()> {
1860        let db = Connection::open_in_memory()?;
1861        db.execute_batch("CREATE TABLE test (id INTEGER, name TEXT, score DOUBLE)")?;
1862
1863        // Heterogeneous tuple
1864        let mut stmt = db.prepare("INSERT INTO test VALUES (?, ?, ?)")?;
1865        stmt.execute((1i32, "alice", 95.5f64))?;
1866        stmt.execute((2i32, "bob", 87.0f64))?;
1867
1868        let mut stmt = db.prepare("SELECT id, name, score FROM test ORDER BY id")?;
1869        let mut rows = stmt.query([])?;
1870
1871        let row = rows.next()?.unwrap();
1872        assert_eq!(row.get::<_, i32>(0)?, 1);
1873        assert_eq!(row.get::<_, String>(1)?, "alice");
1874        assert_eq!(row.get::<_, f64>(2)?, 95.5);
1875
1876        let row = rows.next()?.unwrap();
1877        assert_eq!(row.get::<_, i32>(0)?, 2);
1878        assert_eq!(row.get::<_, String>(1)?, "bob");
1879        assert_eq!(row.get::<_, f64>(2)?, 87.0);
1880
1881        Ok(())
1882    }
1883
1884    #[test]
1885    fn test_query_row_tuple() -> Result<()> {
1886        let db = Connection::open_in_memory()?;
1887        db.execute_batch("CREATE TABLE test (id INTEGER, name TEXT)")?;
1888        db.execute("INSERT INTO test VALUES (1, 'alice')", [])?;
1889
1890        let name: String = db.query_row("SELECT name FROM test WHERE id = ?", (1i32,), |r| r.get(0))?;
1891        assert_eq!(name, "alice");
1892
1893        Ok(())
1894    }
1895
1896    #[test]
1897    fn test_execute_tuple_single_element() -> Result<()> {
1898        let db = Connection::open_in_memory()?;
1899        db.execute_batch("CREATE TABLE test (id INTEGER)")?;
1900
1901        db.execute("INSERT INTO test VALUES (?)", (42i32,))?;
1902
1903        let val: i32 = db.query_row("SELECT id FROM test", [], |r| r.get(0))?;
1904        assert_eq!(val, 42);
1905        Ok(())
1906    }
1907
1908    #[test]
1909    fn test_execute_tuple_many_columns() -> Result<()> {
1910        let db = Connection::open_in_memory()?;
1911        db.execute_batch(
1912            "CREATE TABLE test (a INT, b TEXT, c DOUBLE, d INT, e TEXT, f DOUBLE, g INT, h TEXT, i DOUBLE, j INT, k TEXT, l DOUBLE, m INT, n TEXT, o DOUBLE, p INT)",
1913        )?;
1914
1915        // Use arity 16 with heterogeneous types to exercise the max tuple impl
1916        db.execute(
1917            "INSERT INTO test VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
1918            (
1919                1i32, "a", 1.0f64, 2i32, "b", 2.0f64, 3i32, "c", 3.0f64, 4i32, "d", 4.0f64, 5i32, "e", 5.0f64, 6i32,
1920            ),
1921        )?;
1922
1923        let (a, p): (i32, i32) = db.query_row("SELECT a, p FROM test", [], |r| Ok((r.get(0)?, r.get(1)?)))?;
1924        assert_eq!(a, 1);
1925        assert_eq!(p, 6);
1926        Ok(())
1927    }
1928
1929    #[test]
1930    fn test_execute_tuple_with_option() -> Result<()> {
1931        let db = Connection::open_in_memory()?;
1932        db.execute_batch("CREATE TABLE test (id INTEGER, name TEXT)")?;
1933
1934        db.execute("INSERT INTO test VALUES (?, ?)", (1i32, None::<String>))?;
1935
1936        let name: Option<String> = db.query_row("SELECT name FROM test WHERE id = ?", (1i32,), |r| r.get(0))?;
1937        assert_eq!(name, None);
1938        Ok(())
1939    }
1940
1941    #[test]
1942    fn test_execute_empty_tuple() -> Result<()> {
1943        let db = Connection::open_in_memory()?;
1944        db.execute_batch("CREATE TABLE test (id INTEGER DEFAULT 1)")?;
1945
1946        db.execute("INSERT INTO test DEFAULT VALUES", ())?;
1947
1948        let val: i32 = db.query_row("SELECT id FROM test", (), |r| r.get(0))?;
1949        assert_eq!(val, 1);
1950        Ok(())
1951    }
1952
1953    #[test]
1954    fn test_with_decimal() -> Result<()> {
1955        let db = Connection::open_in_memory()?;
1956        db.execute_batch(
1957            "BEGIN; \
1958            CREATE TABLE foo(x DECIMAL(18, 4)); \
1959            CREATE TABLE bar(y DECIMAL(18, 2)); \
1960            COMMIT;",
1961        )?;
1962
1963        // If DuckDB's scale matches the value scale, the scaled integer is preserved.
1964        let value = Decimal::new(18, 4, 12345)?;
1965        db.execute("INSERT INTO foo(x) VALUES (?)", [&value])?;
1966        let row: Decimal = db.query_row("SELECT x FROM foo", [], |r| r.get::<_, Decimal>(0))?;
1967        assert_eq!(row, value);
1968
1969        // If DuckDB's scale is smaller than the value scale, DuckDB coerces
1970        // 1.2345 to the target DECIMAL(18,2) value.
1971        let value = Decimal::new(18, 4, 12345)?;
1972        db.execute("INSERT INTO bar(y) VALUES (?)", [&value])?;
1973        let row: Decimal = db.query_row("SELECT y FROM bar", [], |r| r.get::<_, Decimal>(0))?;
1974        assert_eq!(row, Decimal::new(18, 2, 123)?);
1975
1976        Ok(())
1977    }
1978
1979    #[test]
1980    fn test_with_native_decimal() -> Result<()> {
1981        let db = Connection::open_in_memory()?;
1982        db.execute_batch("CREATE TABLE foo(x DECIMAL(38, 2));")?;
1983
1984        let value = Decimal::new(38, 2, 12_345_678_901_234_567_890_123_456_789_012)?;
1985        db.execute("INSERT INTO foo(x) VALUES (?)", [&value])?;
1986
1987        let row: Decimal = db.query_row("SELECT x FROM foo", [], |r| r.get(0))?;
1988        assert_eq!(row, value);
1989
1990        Ok(())
1991    }
1992
1993    #[test]
1994    fn test_decimal_from_sql_integer() -> Result<()> {
1995        let db = Connection::open_in_memory()?;
1996
1997        // FromSql: INTEGER -> Decimal
1998        let row: Decimal = db.query_row("SELECT 42", [], |r| r.get(0))?;
1999        assert_eq!(row, Decimal::new(2, 0, 42)?);
2000
2001        // FromSql: BIGINT -> Decimal
2002        let row: Decimal = db.query_row("SELECT 9999999999::BIGINT", [], |r| r.get(0))?;
2003        assert_eq!(row, Decimal::new(10, 0, 9999999999_i64 as i128)?);
2004
2005        // FromSql: HUGEINT -> Decimal
2006        let row: Decimal = db.query_row("SELECT 12345678901234567890::HUGEINT", [], |r| r.get(0))?;
2007        assert_eq!(row, Decimal::new(20, 0, 12345678901234567890)?);
2008
2009        Ok(())
2010    }
2011
2012    #[test]
2013    fn test_decimal_from_sql_float_targets_preserve_fraction() -> Result<()> {
2014        let db = Connection::open_in_memory()?;
2015
2016        let row: f64 = db.query_row("SELECT 123.45::DECIMAL(10,2)", [], |r| r.get(0))?;
2017        assert!((row - 123.45).abs() < f64::EPSILON);
2018
2019        let row: f32 = db.query_row("SELECT 0.4::DECIMAL(10,1)", [], |r| r.get(0))?;
2020        assert!((row - 0.4).abs() < f32::EPSILON);
2021
2022        let row: f32 = db.query_row("SELECT -0.4::DECIMAL(10,1)", [], |r| r.get(0))?;
2023        assert!((row + 0.4).abs() < f32::EPSILON);
2024
2025        let row: f64 = db.query_row("SELECT 1.5::DECIMAL(10,1)", [], |r| r.get(0))?;
2026        assert!((row - 1.5).abs() < f64::EPSILON);
2027
2028        let row: f64 = db.query_row("SELECT -1.5::DECIMAL(10,1)", [], |r| r.get(0))?;
2029        assert!((row + 1.5).abs() < f64::EPSILON);
2030
2031        Ok(())
2032    }
2033
2034    #[test]
2035    fn test_variant_column_logical_type_metadata() -> Result<()> {
2036        let db = Connection::open_in_memory()?;
2037        let stmt = db.prepare("SELECT {'a': 42}::VARIANT AS variant_col")?;
2038
2039        let logical_type = stmt.column_logical_type(0);
2040        assert_eq!(logical_type.id(), LogicalTypeId::Variant);
2041        assert_eq!(logical_type.raw_id(), crate::ffi::DUCKDB_TYPE_DUCKDB_TYPE_VARIANT);
2042        assert_eq!(logical_type.num_children(), 0);
2043        assert_eq!(format!("{logical_type:?}"), "Variant");
2044
2045        Ok(())
2046    }
2047
2048    #[test]
2049    fn test_geometry_column_logical_type_metadata() -> Result<()> {
2050        let db = Connection::open_in_memory()?;
2051        let stmt = db.prepare("SELECT NULL::GEOMETRY AS geom")?;
2052
2053        let logical_type = stmt.column_logical_type(0);
2054        assert_eq!(logical_type.id(), LogicalTypeId::Geometry);
2055        assert_eq!(logical_type.raw_id(), crate::ffi::DUCKDB_TYPE_DUCKDB_TYPE_GEOMETRY);
2056        assert_eq!(logical_type.geometry_crs(), None);
2057        assert_eq!(format!("{logical_type:?}"), "Geometry");
2058
2059        Ok(())
2060    }
2061
2062    #[test]
2063    fn test_geometry_column_logical_type_crs_metadata() -> Result<()> {
2064        let db = Connection::open_in_memory()?;
2065        let stmt = db.prepare("SELECT ST_SetCRS('POINT EMPTY'::GEOMETRY, 'EPSG:4326') AS geom")?;
2066
2067        let logical_type = stmt.column_logical_type(0);
2068        assert_eq!(logical_type.id(), LogicalTypeId::Geometry);
2069        assert_eq!(logical_type.geometry_crs(), Some("EPSG:4326".to_string()));
2070
2071        Ok(())
2072    }
2073
2074    #[test]
2075    #[should_panic(expected = "could not retrieve logical type for result column at index 1")]
2076    fn test_column_logical_type_bad_index_panics() {
2077        let db = Connection::open_in_memory().unwrap();
2078        let stmt = db.prepare("SELECT 1").unwrap();
2079
2080        let _ = stmt.column_logical_type(1);
2081    }
2082
2083    #[test]
2084    fn test_variant_result_decode_unsupported() -> Result<()> {
2085        let db = Connection::open_in_memory()?;
2086
2087        let err = match db
2088            .prepare("SELECT 1 AS id, {'a': 42}::VARIANT AS variant_col")?
2089            .query([])
2090        {
2091            Ok(_) => panic!("expected Variant query to fail"),
2092            Err(err) => err,
2093        };
2094
2095        assert_variant_decode_error(err, 1);
2096        Ok(())
2097    }
2098
2099    #[test]
2100    fn test_variant_streaming_result_decode_unsupported() -> Result<()> {
2101        let db = Connection::open_in_memory()?;
2102        let err = match db
2103            .prepare("SELECT 1 AS id, {'a': 42}::VARIANT AS variant_col")?
2104            .stream_arrow([])
2105        {
2106            Ok(_) => panic!("expected Variant streaming query to fail"),
2107            Err(err) => err,
2108        };
2109
2110        assert_variant_decode_error(err, 1);
2111        Ok(())
2112    }
2113
2114    #[test]
2115    fn test_variant_returning_execute_rejected_before_mutation() -> Result<()> {
2116        let db = Connection::open_in_memory()?;
2117        db.execute_batch("CREATE TABLE t(id INTEGER, v VARIANT);")?;
2118
2119        let err = match db.execute("INSERT INTO t VALUES (1, {'a': 42}::VARIANT) RETURNING v", []) {
2120            Ok(_) => panic!("expected Variant RETURNING execute to fail"),
2121            Err(err) => err,
2122        };
2123
2124        assert_variant_decode_error(err, 0);
2125        let count: i64 = db.query_row("SELECT COUNT(*) FROM t", [], |row| row.get(0))?;
2126        assert_eq!(0, count);
2127        Ok(())
2128    }
2129
2130    #[test]
2131    fn test_variant_returning_streaming_rejected_before_mutation() -> Result<()> {
2132        let db = Connection::open_in_memory()?;
2133        db.execute_batch("CREATE TABLE t(id INTEGER, v VARIANT);")?;
2134
2135        let err = match db
2136            .prepare("INSERT INTO t VALUES (1, {'a': 42}::VARIANT) RETURNING v")?
2137            .stream_arrow([])
2138        {
2139            Ok(_) => panic!("expected Variant RETURNING streaming execute to fail"),
2140            Err(err) => err,
2141        };
2142
2143        assert_variant_decode_error(err, 0);
2144        let count: i64 = db.query_row("SELECT COUNT(*) FROM t", [], |row| row.get(0))?;
2145        assert_eq!(0, count);
2146        Ok(())
2147    }
2148
2149    #[test]
2150    fn test_nested_variant_result_decode_unsupported() -> Result<()> {
2151        let db = Connection::open_in_memory()?;
2152
2153        let cases = [
2154            "SELECT [123::VARIANT] AS variant_list",
2155            "SELECT {'v': 123::VARIANT} AS variant_struct",
2156            "SELECT map(['v'], [123::VARIANT]) AS variant_map",
2157        ];
2158
2159        for sql in cases {
2160            let err = match db.prepare(sql)?.query([]) {
2161                Ok(_) => panic!("expected nested Variant query to fail for {sql}"),
2162                Err(err) => err,
2163            };
2164            assert_variant_decode_error(err, 0);
2165        }
2166
2167        Ok(())
2168    }
2169
2170    #[test]
2171    fn test_bind_variant_parameter_delegates_to_duckdb() -> Result<()> {
2172        let db = Connection::open_in_memory()?;
2173        db.execute("CREATE TABLE t (id INTEGER, variant_col VARIANT)", [])?;
2174
2175        db.execute("INSERT INTO t VALUES (?, ?)", crate::params![1, "hello"])?;
2176
2177        let count: i64 = db.query_row("SELECT COUNT(*) FROM t WHERE variant_col IS NOT NULL", [], |row| {
2178            row.get(0)
2179        })?;
2180        assert_eq!(count, 1);
2181        Ok(())
2182    }
2183
2184    // Unsupported Value variants must surface an error instead of panicking.
2185    #[test]
2186    fn test_bind_unsupported_container_type_returns_error() -> Result<()> {
2187        use crate::types::Value;
2188
2189        struct OwnedList;
2190        impl ToSql for OwnedList {
2191            fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
2192                Ok(ToSqlOutput::Owned(Value::List(vec![Value::Int(1), Value::Int(2)])))
2193            }
2194        }
2195
2196        let db = Connection::open_in_memory()?;
2197        db.execute("CREATE TABLE t (id INTEGER, numbers INTEGER[])", [])?;
2198
2199        let list = OwnedList;
2200        let err = db
2201            .execute("INSERT INTO t VALUES (?, ?)", crate::params![1, list])
2202            .unwrap_err();
2203
2204        assert_binding_list_error(err);
2205
2206        let borrowed_list = BorrowedList::new();
2207        let err = db
2208            .execute("INSERT INTO t VALUES (?, ?)", crate::params![2, borrowed_list])
2209            .unwrap_err();
2210        assert_binding_list_error(err);
2211        Ok(())
2212    }
2213
2214    #[test]
2215    fn test_bind_error_clears_partial_parameter_state() -> Result<()> {
2216        let db = Connection::open_in_memory()?;
2217        db.execute("CREATE TABLE t (id INTEGER NOT NULL, name TEXT)", [])?;
2218
2219        let mut stmt = db.prepare("INSERT INTO t VALUES (?, ?)")?;
2220        let list = BorrowedList::new();
2221        let err = stmt.execute(crate::params![1, list]).unwrap_err();
2222        assert_binding_list_error(err);
2223
2224        stmt.raw_bind_parameter(2, "ok")?;
2225        assert!(stmt.raw_execute().is_err());
2226
2227        let count: i32 = db.query_row("SELECT COUNT(*) FROM t", [], |row| row.get(0))?;
2228        assert_eq!(count, 0);
2229
2230        stmt.raw_bind_parameter(1, 7)?;
2231        stmt.raw_bind_parameter(2, "ok")?;
2232        assert_eq!(stmt.raw_execute()?, 1);
2233
2234        let row = db.query_row("SELECT id, name FROM t", [], |row| {
2235            Ok((row.get::<_, i32>(0)?, row.get::<_, String>(1)?))
2236        })?;
2237        assert_eq!(row, (7, "ok".to_string()));
2238        Ok(())
2239    }
2240}