launchbadge / sqlx

🧰 The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, and SQLite.
Apache License 2.0
13.26k stars 1.26k forks source link

Idiomatic column data and row data #642

Open CallumDowling opened 4 years ago

CallumDowling commented 4 years ago

Hi all, my use case is using the query! macro to extract both the column names and the row information such that when a column name or alias changes, I don't need to worry about change anywhere except for the query. The data is being used in a html table. At the moment I have to write the column names separately for my frontend, but if the query gets changed, and I don't update the column names the wrong data might be shown to the user.

I know I can use describe on a query to get a vector of header names, but this leaves me with the same problem because when i use map on the record, I may forget to add a column, or put it in the right order.

What I am looking to do is something like

pseudocode using mysql executor, excuse the dodgy code

let querystring = "SELECT * FROM...? ? ";
let result = query!(querystring, param1, param1).fetch_all(conn).await;
let description = conn.describe(querystring).await;

let headers = description.iter();
let rows = result.iter().map(|x| format!("{:?}",x)).collect(); //<-- Preferably display nullable fields as their values if Some, empty string if None

assert_eq!(headers,len(), rows.len());

Has anyone found an elegant way to do this?

ljluestc commented 1 month ago
use mysql::*;
use mysql::prelude::*;
use std::fmt;

#[derive(Debug)]
struct RowData(Vec<Option<String>>);

impl fmt::Display for RowData {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for value in &self.0 {
            match value {
                Some(val) => write!(f, "{}\t", val)?,
                None => write!(f, "N/A\t")?,
            }
        }
        writeln!(f)
    }
}

fn main() -> Result<()> {
    let url = "mysql://username:password@localhost:3306/database";
    let pool = Pool::new(url)?;
    let mut conn = pool.get_conn()?;

    let querystring = "SELECT * FROM your_table";

    // Execute the query
    let result: Vec<Row> = conn.query(querystring)?;

    // Fetch the column names
    let column_names: Vec<String> = conn.query(format!("DESCRIBE your_table"))?
        .into_iter()
        .map(|row| row.get::<String, _>(0).unwrap())
        .collect();

    // Process the rows
    let mut rows_data: Vec<RowData> = Vec::new();
    for row in result {
        let mut row_data = Vec::new();
        for (i, col) in row.columns_ref().iter().enumerate() {
            if let Some(value) = row.get_opt::<String, usize>(i) {
                row_data.push(value);
            } else {
                row_data.push(None);
            }
        }
        rows_data.push(RowData(row_data));
    }

    // Print headers
    println!("{}", column_names.join("\t"));

    // Print rows
    for row_data in rows_data {
        println!("{}", row_data);
    }

    Ok(())
}