Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with Mutable Borrow of Statement when Accessing Column Names (Option::unwrap()) #251

Open
ravi-nallappan opened this issue Jan 5, 2024 · 3 comments

Comments

@ravi-nallappan
Copy link

I encountered a problem similar to a previously closed issue (#204). However, in my use case, I need to execute arbitrary select-SQL and generate a vector of maps, where each map represents a row with column name/value pairs.

Code Snippet:

use duckdb::types::Value as DuckValue;
use duckdb::AccessMode as DuckAccessMode;
use duckdb::Config as DuckConfig;
use duckdb::Connection as DuckConnection;
use duckdb::Error as DuckError;
use duckdb::Row as DuckRow;
use duckdb::Rows as DuckRows;
use fallible_iterator::FallibleIterator;

    fn query_duck(&self, sql: &str) -> Result<Vec<IndexMap<String, String>>> {
        let mut statement = self.duck_connection.prepare(sql)?;
        let names = statement
            .column_names()
            .into_iter()
            .map(String::from)
            .collect_vec();
        let value_to_text = |v: DuckValue| match v {
            DuckValue::Null => String::from("null"),
            DuckValue::BigInt(i) => i.to_string(),
            DuckValue::TinyInt(i) => i.to_string(),
            DuckValue::Double(d) => d.to_string(),
            DuckValue::Text(t) => t,
            DuckValue::Blob(v) => v.into_iter().map(|u| u.to_string()).join(","),
            _ => "".to_string(),
        };
        let row_to_vectuple = |r: &DuckRow| -> Result<Vec<(String, String)>, DuckError> {
            names
                .iter()
                .map(String::clone)
                .map(|n| r.get::<_, DuckValue>(n.as_str()).and_then(|v| Ok((n, v))))
                .map_ok(|(n, v)| (n, value_to_text(v)))
                .collect::<Result<Vec<_>, _>>()
        };
        let vectuple_to_map = |r: Vec<(String, String)>| -> IndexMap<String, String> {
            r.into_iter().collect::<IndexMap<_, _>>()
        };
        let rows: DuckRows<'_> = statement.query([])?;
        let result: Vec<Vec<(String, String)>> = rows.map(row_to_vectuple).collect()?;
        let result = result.into_iter().map(vectuple_to_map).collect_vec();
        Ok(result)
    }

Problem Description:
The issue arises because the statement is mutably borrowed during the time when column names need to be accessed.

Environment:

  • DuckDB version: { version = "0.9.2", features = ["json"] }
  • Rust version: 1.71.1 (eb26296b5 2023-08-03)
  • OS: Ubuntu 22.04

Could you suggest a workaround for this issue? Additionally, is this behavior intentional for reasons I might not be aware of?

Thank you for your assistance and for maintaining this project.

@ravi-nallappan ravi-nallappan changed the title Issue with Immutable Borrow of Statement in DuckDB when Accessing Column Names (Option::unwrap()) Issue with Mutable Borrow of Statement when Accessing Column Names (Option::unwrap()) Jan 5, 2024
@Timmmm
Copy link

Timmmm commented Feb 9, 2024

I ran into this exact issue. There's a catch-22 if you don't know the number of columns in advance. You can't get the column count without executing the query, and you can't execute the query without knowing the column count (because you don't know which columns to get).

@nikola-maric
Copy link

@ravi-nallappan @Timmmm
I bimped into same issue when I was doing some tinkering...I managed to find a solition by asking on reddit https://www.reddit.com/r/rust/s/UqXiz5BNaR

The trick is to call row.as_ref().column_names() once you matched single value with something like match row.next()... prior to getting column names. Hope it helps!

@danthegoodman1
Copy link

That worked for me too. This is an issue with the duckdb api because doing the same in rusqlite works fine:

    let mut stmt = conn.prepare("SELECT id, name, data FROM person")?;
    let cols: Vec<String> = stmt.column_names().iter().map(|&s| s.to_string()).collect();
    println!("cols: {:?}", cols);
    let mut rows = stmt.query([])?;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants