sqlx icon indicating copy to clipboard operation
sqlx copied to clipboard

Postgres: Insert into a composite type containing varchar(256) doesn't work.

Open jakubtrnka opened this issue 2 years ago • 1 comments

Bug Description

I have a table containing composite type that contains varchar(256). The table also contains other top-level fields, among whose there are the varchars are as well.

I used the derive trait for a rust type as instructed but keep receiving error

PgDatabaseError { severity: Error, code: "42804", message: "wrong data type: 25, expected 1043", detail: None, hint: None, position: None, where: None, schema: None, table: None, column: None, data_type: None, constraint: None, file: Some("rowtypes.c"), line: Some(557), routine: Some("record_recv") }

I found that the data type 25 is TEXT, while the expected 1043 is VARCHAR.

Indeed, if I change the type in the database, the insert statement succeeds.

Minimal Reproduction

I have a composite type containing varchar(256) (my_comptype) and a table (my_table) containing varchar(256) and my_comptype.

create schema my_schema;

create type my_schema.mycomp_type as (
    inner varchar(256),
    some_other varchar(256)
);

create table my_schema.my_table
(
    outter varchar(256),
    my_composite my_schema.mycomp_type
);

in the rust:

// definition
#[derive(sqlx::Type)]
#[sqlx(type_name = "my_schema.mycomp_type")]
// Macros are incompatible with struct lifetimes   <----- THIS IS BTW ALSO PROBLEM
pub(crate) struct MyCompType {
    inner: String,
    some_other: String,
}

pub(crate) struct DbItem {
    top_string: String,
    my_comp_type: Option<MyCompType>,
}

/* snip */

// bulk insert:
let mut query_builder: sqlx::QueryBuilder<sqlx::Postgres> =
        sqlx::QueryBuilder::new(r#"
INSERT INTO my_schema.my_table (
        outter,
        my_composite
) "#);
query_builder.push_values(top_string, |mut b, item| {
    b.push_bind(item.some_top_string)
        .push_bind(item.my_comp_type);
});
query_builder.build().execute(pool).await?;

This query fails if the DbItem.my_comp_type is Some(...) with the error given above. And succeeds if it is None.

Info

  • SQLx version: 0.7.1
  • SQLx features enabled: ["postgres", "bit-vec", "ipnetwork", "json", "chrono", "uuid", "runtime-tokio"]
  • Database server and version: PostgreSQL 13.8 (Debian 13.8-1.pgdg110+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit
  • Operating system: Arch linux: 6.4.2-arch1-1 #1 SMP PREEMPT_DYNAMIC Thu, 06 Jul 2023 18:35:54 +0000 x86_64 GNU/Linux
  • rustc --version: rustc 1.69.0 (84c898d65 2023-04-16)

jakubtrnka avatar Jul 17 '23 09:07 jakubtrnka

Yay! I am not doing something stupid.

I am having this issue too.

Doing something like this can "bypass" the issue.

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VarCharString(pub String);
impl Type<Postgres> for VarCharString {
    fn type_info() -> <Postgres as sqlx::Database>::TypeInfo {
        PgTypeInfo::with_oid(Oid(1043))
    }
}
impl Encode<'_, Postgres> for VarCharString {
    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
        self.0.encode_by_ref(buf)
    }
}
impl Decode<'_, Postgres> for VarCharString {
    fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
        String::decode(value).map(Self)
    }
}

wyatt-herkamp avatar May 20 '25 21:05 wyatt-herkamp