risingwavelabs/risingwave · error

failed to convert struct to PostgreSQL point: expected…

Error message

failed to convert struct to PostgreSQL point: expected exactly two non-null float64 fields

What it means

When converting an RW Struct scalar into a PostgreSQL `point` adapter value, the struct must contain exactly two non-null Float64 fields (x, y) and nothing more. Wrong count, nulls, or non-float fields make the conversion fail. This is a strict shape contract for mapping RW structs onto Postgres point columns.

Solutions

  1. Ensure the mapped struct has exactly two fields, both Double/Float64 and non-null at runtime.
  2. Recreate/adjust the RW table so the struct is `struct<x double, y double>`.
  3. Cast numeric fields to double precision before conversion, or preprocess null coordinates.
  4. Project only the two coordinate fields in the sink's SELECT, dropping extras.

Example fix

// before: struct<a decimal, b decimal, id int>
struct<a decimal, b decimal, id int>
// after: exactly two double fields
struct<x double, y double>
Defensive patterns

Strategy: type-guard

Validate before calling

fn is_valid_point_struct(s: &StructType) -> bool {
    s.fields().len() == 2 && s.fields().iter().all(|f| f == &DataType::Float64)
}

Type guard

fn is_point_compatible(fields: &[Option<ScalarRefImpl>]) -> bool {
    matches!(fields,
        [Some(Some(ScalarRefImpl::Float64(_))), Some(Some(ScalarRefImpl::Float64(_)))])
}

Try / catch

match result {
    Err(e) if e.to_string().contains("failed to convert struct to PostgreSQL point") => {
        // fix struct shape to exactly two non-null float64 fields
    }
    other => other?,
}

Prevention

When it happens

Trigger: `ScalarAdapter::from_scalar` for the Point variant destructures the struct fields iterator expecting `(Some(Some(Float64)), Some(Some(Float64)), None)`; fires when the struct has missing fields, null x/y, non-Float64 fields (e.g. Float32/Decimal), or more than two fields.

Common situations: Sink mapping an RW struct with wrong field types onto a Postgres point column; struct fields declared as decimal or real instead of double; a struct with extra metadata fields mapped to a point; null coordinates in the data.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/16d7414e97082a66. Report an issue: GitHub.

Appendix: source

Thrown at src/connector/src/parser/scalar_adapter.rs:238

    }

    /// convert `ScalarRefImpl` to `ScalarAdapter` so that we can correctly encode to postgres value
    pub(crate) fn from_scalar(
        scalar: ScalarRefImpl<'_>,
        ty: &Type,
    ) -> ConnectorResult<ScalarAdapter> {
        Ok(match (scalar, ty, ty.kind()) {
            (ScalarRefImpl::Utf8(s), &Type::UUID, _) => ScalarAdapter::Uuid(s.parse()?),
            (ScalarRefImpl::Struct(point), &Type::POINT, _) => {
                let mut fields = point.iter_fields_ref();

                let (
                    Some(Some(ScalarRefImpl::Float64(x))),
                    Some(Some(ScalarRefImpl::Float64(y))),
                    None,
                ) = (fields.next(), fields.next(), fields.next())
                else {
                    return Err(anyhow!(
                        "failed to convert struct to PostgreSQL point: expected exactly two non-null float64 fields"
                    )
                    .into());
                };

                ScalarAdapter::Point(PgPoint {
                    x: x.into_inner(),
                    y: y.into_inner(),
                })
            }
            (ScalarRefImpl::Utf8(s), &Type::NUMERIC, _) => {
                ScalarAdapter::Numeric(string_to_pg_numeric(s))
            }
            (ScalarRefImpl::Int256(s), &Type::NUMERIC, _) => {
                ScalarAdapter::Numeric(string_to_pg_numeric(&s.to_string()))
            }
            (ScalarRefImpl::Utf8(s), _, Kind::Enum(_)) => {
                ScalarAdapter::Enum(EnumString(s.to_owned()))

View on GitHub (pinned to 6469eb736d)