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
13k stars 1.24k forks source link

Usage with Postgres uint extension #797

Open ohmree opened 3 years ago

ohmree commented 3 years ago

Currently sqlx 0.4.0-beta.1 doesn't support the Postgres uint extension.
What can I do to have my app support the unsigned types from the extension apart from switching to signed types in my database?
If I need to contribute code directly to sqlx, where do I start?

Here's an example of code that returns an error:

-- Assuming the uint extension is installed:
CREATE EXTENSION uint;

CREATE TABLE streamers (
    username_id text CONSTRAINT username PRIMARY KEY,
    video_id text REFERENCES video_ids (id)
);

CREATE TABLE video_ids (
    id text PRIMARY KEY,
    people_playing uint8
);

INSERT INTO video_ids (id)
VALUES ('8iEukLpRyDU');
use sqlx::postgres::PgPoolOptions;
use anyhow::Result;

#[async_std::main]
async fn main() -> Result<()> {
    let pool = PgPoolOptions::new()
        .max_connections(5)
        .connect(&"postgres://username@localhost/db_name").await?;

    let row: (u64,) = sqlx::query_as("SELECT people_playing FROM video_ids")
        .fetch_one(&pool).await?;

    println!("{}", row.0);

    Ok(())
}

And the error:

Error: error returned from database: no binary output function available for type uint8
mehcode commented 3 years ago

no binary output function available for type uint8

that's not an error from SQLx, does that query work in psql?

ohmree commented 3 years ago

It actually does work, both in Emacs' sql-mode (which I assume wraps psql) and in Rust after working on my app some more.

It must've been an error on my side so I'm closing this.

ohmree commented 3 years ago

Sorry for the spam, but I think I was wrong about there being no problem.

Running this through the Emacs pgsql interface while connected to my database:

CREATE EXTENSION uint;

CREATE TABLE video_ids (
    id varchar(11) CONSTRAINT id PRIMARY KEY,
    people_playing uint8 DEFAULT 0 NOT NULL
);

CREATE TABLE users (
    username text CONSTRAINT username PRIMARY KEY,
    video_id varchar(11) REFERENCES video_ids (id) NOT NULL,
    progress_seconds uint8 DEFAULT 0 NOT NULL
);

And then trying to compile this code:

use anyhow::Result;
use sqlx::postgres::PgPoolOptions;
use std::env;

#[async_std::main]
async fn main() -> Result<()> {
    dotenv::dotenv()?;

    let pool = PgPoolOptions::new()
        .max_connections(5)
        .connect(&env::var("DATABASE_URL")?)
        .await?;

    sqlx::query!(
        r#"
            UPDATE users
            SET progress_seconds = $1
            WHERE username = $2
            "#,
        32,
        "ohmree"
    )
    .execute(&pool)
    .await?;

    Ok(())
}

Results in the error error: unsupported type uint8 for param #1.
If I change 32 to 32_u64 compilation fails with the same error.

If it matters I installed pguint from the AUR, the non-git version.

rksm commented 3 years ago

I wonder what the best way for implementing support for this extension is? https://github.com/rksm/sqlx/commit/0d140dd00a01b11d1dba8f87320706d300b38147 is an approach but it hard codes the type ids for the extension types as returned by select oid, typarray from pg_type where typname = 'uint8'. Those change, however, with each CREATE EXTENSION. Is there a better approach?

JohnPeel commented 2 years ago

I've taken a crack at this, and it seems to work in my limited use case. JohnPeel/sqlx:pguint

It did however require some changes to pguint. I pulled in one of (the three) PRs for send & recv implementations on the uint types. JohnPeel/pguint:binary