{"record":{"id":"16d7414e97082a66","repo":"risingwavelabs/risingwave","slug":"failed-to-convert-struct-to-postgresql-point-expe","errorCode":null,"errorMessage":"failed to convert struct to PostgreSQL point: expected exactly two non-null float64 fields","messagePattern":"failed to convert struct to PostgreSQL point: expected exactly two non-null float64 fields","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/connector/src/parser/scalar_adapter.rs","lineNumber":238,"sourceCode":"    }\n\n    /// convert `ScalarRefImpl` to `ScalarAdapter` so that we can correctly encode to postgres value\n    pub(crate) fn from_scalar(\n        scalar: ScalarRefImpl<'_>,\n        ty: &Type,\n    ) -> ConnectorResult<ScalarAdapter> {\n        Ok(match (scalar, ty, ty.kind()) {\n            (ScalarRefImpl::Utf8(s), &Type::UUID, _) => ScalarAdapter::Uuid(s.parse()?),\n            (ScalarRefImpl::Struct(point), &Type::POINT, _) => {\n                let mut fields = point.iter_fields_ref();\n\n                let (\n                    Some(Some(ScalarRefImpl::Float64(x))),\n                    Some(Some(ScalarRefImpl::Float64(y))),\n                    None,\n                ) = (fields.next(), fields.next(), fields.next())\n                else {\n                    return Err(anyhow!(\n                        \"failed to convert struct to PostgreSQL point: expected exactly two non-null float64 fields\"\n                    )\n                    .into());\n                };\n\n                ScalarAdapter::Point(PgPoint {\n                    x: x.into_inner(),\n                    y: y.into_inner(),\n                })\n            }\n            (ScalarRefImpl::Utf8(s), &Type::NUMERIC, _) => {\n                ScalarAdapter::Numeric(string_to_pg_numeric(s))\n            }\n            (ScalarRefImpl::Int256(s), &Type::NUMERIC, _) => {\n                ScalarAdapter::Numeric(string_to_pg_numeric(&s.to_string()))\n            }\n            (ScalarRefImpl::Utf8(s), _, Kind::Enum(_)) => {\n                ScalarAdapter::Enum(EnumString(s.to_owned()))","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/connector/src/parser/scalar_adapter.rs#L220-L256","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","solutions":["Ensure the mapped struct has exactly two fields, both Double/Float64 and non-null at runtime.","Recreate/adjust the RW table so the struct is `struct<x double, y double>`.","Cast numeric fields to double precision before conversion, or preprocess null coordinates.","Project only the two coordinate fields in the sink's SELECT, dropping extras."],"exampleFix":"// before: struct<a decimal, b decimal, id int>\nstruct<a decimal, b decimal, id int>\n// after: exactly two double fields\nstruct<x double, y double>","handlingStrategy":"type-guard","validationCode":"fn is_valid_point_struct(s: &StructType) -> bool {\n    s.fields().len() == 2 && s.fields().iter().all(|f| f == &DataType::Float64)\n}","typeGuard":"fn is_point_compatible(fields: &[Option<ScalarRefImpl>]) -> bool {\n    matches!(fields,\n        [Some(Some(ScalarRefImpl::Float64(_))), Some(Some(ScalarRefImpl::Float64(_)))])\n}","tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"failed to convert struct to PostgreSQL point\") => {\n        // fix struct shape to exactly two non-null float64 fields\n    }\n    other => other?,\n}","preventionTips":["Declare point-mapped structs as exactly struct<x double, y double>.","Coerce float32/decimal fields to double before the sink conversion.","Filter out rows with null coordinates before writing.","Project only the two coordinate fields; drop extras."],"tags":["postgres","struct","point","type-conversion"],"backgroundTag":"shape-mismatch","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}