Struct duckdb::AppenderParamsFromIter

source ·
pub struct AppenderParamsFromIter<I>(/* private fields */);
Expand description

Adapter type which allows any iterator over ToSql values to implement [Params].

This struct is created by the [params_from_iter] function.

This can be useful if you have something like an &[String] (of unknown length), and you want to use them with an API that wants something implementing Params. This way, you can avoid having to allocate storage for something like a &[&dyn ToSql].

This essentially is only ever actually needed when dynamically generating SQL — static SQL (by definition) has the number of parameters known statically. As dynamically generating SQL is itself pretty advanced, this API is itself for advanced use cases (See “Realistic use case” in the examples).

§Example

§Basic usage

use duckdb::{Connection, Result, params_from_iter};
use std::collections::BTreeSet;

fn query(conn: &Connection, ids: &BTreeSet<String>) -> Result<()> {
    assert_eq!(ids.len(), 3, "Unrealistic sample code");

    let mut stmt = conn.prepare("SELECT * FROM users WHERE id IN (?, ?, ?)")?;
    let _rows = stmt.query(params_from_iter(ids.iter()))?;

    // use _rows...
    Ok(())
}

§Realistic use case

Here’s how you’d use ParamsFromIter to call [Statement::exists] with a dynamic number of parameters.

use duckdb::{Connection, Result};

pub fn any_active_users(conn: &Connection, usernames: &[String]) -> Result<bool> {
    if usernames.is_empty() {
        return Ok(false);
    }

    // Note: `repeat_vars` never returns anything attacker-controlled, so
    // it's fine to use it in a dynamically-built SQL string.
    let vars = repeat_vars(usernames.len());

    let sql = format!(
        // In practice this would probably be better as an `EXISTS` query.
        "SELECT 1 FROM user WHERE is_active AND name IN ({}) LIMIT 1",
        vars,
    );
    let mut stmt = conn.prepare(&sql)?;
    stmt.exists(duckdb::params_from_iter(usernames))
}

// Helper function to return a comma-separated sequence of `?`.
// - `repeat_vars(0) => panic!(...)`
// - `repeat_vars(1) => "?"`
// - `repeat_vars(2) => "?,?"`
// - `repeat_vars(3) => "?,?,?"`
// - ...
fn repeat_vars(count: usize) -> String {
    assert_ne!(count, 0);
    let mut s = "?,".repeat(count);
    // Remove trailing comma
    s.pop();
    s
}

That is fairly complex, and even so would need even more work to be fully production-ready:

  • production code should ensure usernames isn’t so large that it will surpass conn.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER)), chunking if too large. (Note that the limits api requires duckdb to have the “limits” feature).

  • repeat_vars can be implemented in a way that avoids needing to allocate a String.

  • Etc…

This complexity reflects the fact that ParamsFromIter is mainly intended for advanced use cases — most of the time you should know how many parameters you have statically (and if you don’t, you’re either doing something tricky, or should take a moment to think about the design).

Trait Implementations§

source§

impl<I: Clone> Clone for AppenderParamsFromIter<I>

source§

fn clone(&self) -> AppenderParamsFromIter<I>

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl<I: Debug> Debug for AppenderParamsFromIter<I>

source§

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

Formats the value using the given formatter. Read more
source§

impl<I> AppenderParams for AppenderParamsFromIter<I>
where I: IntoIterator, I::Item: ToSql,

Auto Trait Implementations§

§

impl<I> Freeze for AppenderParamsFromIter<I>
where I: Freeze,

§

impl<I> RefUnwindSafe for AppenderParamsFromIter<I>
where I: RefUnwindSafe,

§

impl<I> Send for AppenderParamsFromIter<I>
where I: Send,

§

impl<I> Sync for AppenderParamsFromIter<I>
where I: Sync,

§

impl<I> Unpin for AppenderParamsFromIter<I>
where I: Unpin,

§

impl<I> UnwindSafe for AppenderParamsFromIter<I>
where I: UnwindSafe,

Blanket Implementations§

source§

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

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

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

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

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

source§

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

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

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

source§

fn into(self) -> U

Calls U::from(self).

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

source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

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

§

type Error = Infallible

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

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

Performs the conversion.
source§

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

§

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

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

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

Performs the conversion.
source§

impl<T> Allocation for T
where T: RefUnwindSafe + Send + Sync,