3

I'm querying an instance of PostgreSQL and selecting a sum of a decimal value:

db=# SELECT SUM(distance) AS total_distance FROM table_name WHERE deleted_at IS NULL;
    total_distance
-----------------------
 3808.0666666666666578
(1 row)

When I try to execute this query in Rust:

extern crate postgres;
use postgres::{Connection, TlsMode};

fn main() {
    let conn = Connection::connect("postgresql://u:p@localhost:5432/db", TlsMode::None).unwrap();
    let query = "SELECT SUM(distance) AS total_distance FROM table_name WHERE deleted_at IS NULL;";
    for row in &conn.query(query, &[]).unwrap() {
        let total_distance: f64 = row.get("total_distance");
        println!("{}", total_distance);
    }
}

Results in:

thread 'main' panicked at 'error retrieving column "total_distance": Error(Conversion(WrongType(Type(Numeric))))'

I've seen in various threads that the Numeric type isn't supported by the Postgres crate, so I've tried creating my own numeric type:

#[derive(Debug)]
struct Float64(f64);

impl FromSql for Float64 {
    fn from_sql(ty: &Type, raw: &[u8]) -> Result<Float64, Box<Error + Sync + Send>> {
        let bytes = raw.try_into().expect("failed!");
        Ok(Float64(f64::from_be_bytes(bytes)))
    }

    fn from_sql_null(ty: &Type) -> Result<Float64, Box<Error + Sync + Send>> {
        Ok(Float64(0.0))
    }

    fn from_sql_nullable(
        ty: &Type,
        raw: Option<&[u8]>,
    ) -> Result<Float64, Box<Error + Sync + Send>> {
        match raw {
            None => Ok(Float64(0.0)),
            Some(value) => Float64::from_sql(ty, value),
        }
    }

    fn accepts(ty: &Type) -> bool {
        NUMERIC.eq(ty)
    }
}

impl Display for Float64 {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

But this still doesn't work as the raw bytes fail to unwrap:

thread 'main' panicked at 'failed!: TryFromSliceError(())', src/libcore/result.rs:1165:5

raw: &[u8] has the length of 18, which is why it can't unwrap. What would be the best way to convert an 18 byte slice to f64?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
noamt
  • 7,397
  • 2
  • 37
  • 59
  • 1
    Well `pub fn from_be_bytes(bytes: [u8; 8]) -> f64` expects 8 bytes, perhaps `raw: &[u8]` is returning more than that? There might be an in herent mismatch between the PG Numeric type and `f64` – Yuval Adam Nov 18 '19 at 09:41
  • 2
    `diesel` has this `diesel::pg::data_types::PgNumeric` type. Its `impl FromSql for PgNumeric` probably can show you how to do the conversion. – edwardw Nov 18 '19 at 10:04
  • [numeric | user-specified precision, exact | up to **131072** digits before the decimal point; up to **16383** digits after the decimal point](https://www.postgresql.org/docs/current/datatype-numeric.html). There is no Rust type that supports that. – Shepmaster Nov 18 '19 at 15:27
  • 1
    See also [C libpq: get float value from numeric](https://stackoverflow.com/q/32651069/155423) – Shepmaster Nov 18 '19 at 15:28

0 Answers0