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

Unable to retrieve array column due to SQL type mismatch: as SQL type ...[] is not compatible with SQL type _... #3204

Open
vegardgs-ksat opened this issue Apr 17, 2024 · 1 comment
Labels

Comments

@vegardgs-ksat
Copy link

vegardgs-ksat commented Apr 17, 2024

Bug Description

In postgres, when utilizing arrays of a custom type, the driver rejects retrieving a column from a row if the PgTypeInfo for PgHasArrayType is implemented using the [] syntax. The underlying implementation in postgres stores this array implementation on custom types using a underscore prefix. This does not occur when reading the array out through the query macro.

I get the following failure:

thread 'main' panicked at /Users/vegardgs/.cargo/registry/src/index.crates.io-6f17d22bba15001f/sqlx-core-0.7.4/src/row.rs:72:37:
called `Result::unwrap()` on an `Err` value: ColumnDecode { index: "\"tags\"", source: "mismatched types; Rust type `alloc::vec::Vec<sqlx_postgres_array::Tag>` (as SQL type `tag[]`) is not compatible with SQL type `_tag`" }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

It was quite surprising behavior, as I was unaware of the actual type name of the postgres enum array - I've always referred to it through the [] syntax. I neither saw any traces of this scenario mentioned in the Types nor PgHasArrayType documentation.

Is this expected behavior? Could this scenario be attempted auto-detected for postgres? Or perhaps only a clarification in the documentation of postgres enums when implementing PgHasArrayType?

Minimal Reproduction

use sqlx::postgres::{PgHasArrayType, PgTypeInfo};
use sqlx::Row;

#[derive(Debug, sqlx::Type)]
#[sqlx(type_name = "tag", rename_all = "UPPERCASE")]
enum Tag {
    Test,
    Internal,
}

impl PgHasArrayType for Tag {
    fn array_type_info() -> PgTypeInfo {
        // NOTE: Changing `tag[]` to `_tag` resolves the issue.
        PgTypeInfo::with_name("tag[]")
    }
}

#[tokio::main]
async fn main() -> sqlx::Result<()> {
    dotenvy::dotenv().ok();
    let database = sqlx::PgPool::connect(&std::env::var("DATABASE_URL").unwrap()).await?;

    sqlx::query!(
        r#"
    INSERT INTO Items (tags)
    VALUES
        ($1),
        ($2)
        "#,
        &vec![Tag::Test] as _,
        &vec![Tag::Internal, Tag::Test] as _,
    )
    .execute(&database)
    .await?;

    let by_macro: Vec<Vec<Tag>> = sqlx::query!(
        r#"
    SELECT tags as "tags: Vec<Tag>" from Items
        "#
    )
    .fetch_all(&database)
    .await?
    .into_iter()
    .map(|r| r.tags)
    .collect();
    assert!(by_macro.len() >= 2);

    let by_query: Vec<Vec<Tag>> = sqlx::query("SELECT tags FROM Items")
        .fetch_all(&database)
        .await?
        .into_iter()
        // NOTE: This fails
        .map(|r| r.get("tags"))
        .collect();
    assert!(by_query.len() >= 2);

    Ok(())
}
[dependencies]
dotenvy = "0.15.7"
sqlx = { version = "0.7.4", features = ["postgres", "runtime-tokio"] }
tokio = { version = "1.37.0", features = ["macros", "rt", "rt-multi-thread"] }
CREATE TYPE tag as ENUM ('TEST', 'INTERNAL');

CREATE TABLE Items (
    tags tag[] default '{}'::tag[] NOT NULL
);

This is the debug print of the column info for the select query:

   PgColumn {
        ordinal: 0,
        name: tags,
        type_info: PgTypeInfo(
            Custom(
                PgCustomType {
                    oid: Oid(
                        16392,
                    ),
                    name: _tag,
                    kind: Array(
                        PgTypeInfo(
                            Custom(
                                PgCustomType {
                                    oid: Oid(
                                        16393,
                                    ),
                                    name: tag,
                                    kind: Enum(
                                        [
                                            "TEST",
                                            "INTERNAL",
                                        ],
                                    ),
                                },
                            ),
                        ),
                    ),
                },
            ),
        ),
        relation_id: Some(
            16397,
        ),
        relation_attribute_no: Some(
            1,
        ),
    }

Info

  • SQLx version: 0.7.4
  • SQLx features enabled: postgres, runtime-tokio
  • Database server and version: Postgres 16.2
  • Operating system: macOS Sonomoa (version 14.4.1)
  • rustc --version: rustc 1.77.1 (7cf61ebde 2024-03-27)
@NickUfer
Copy link

NickUfer commented May 29, 2024

Got the same problem with the requirement of an underscore infront of the actual type name... don't know why this is necessary and would be good to know why.

Edit: Found the answer + one message above + some below: #1004 (comment)

It's Postgres' default naming convention for array types.

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

No branches or pull requests

2 participants