Skip to content

ARRAY vector literal loses every element on document_strict INSERT, reported as 0 elements under XX000 #306

Description

@farhan-syah

Version / build tested against

origin/main @ 66d0a22

Deployment mode

Origin — single node (local)

Engine(s) involved

Document (strict)

Summary

INSERT of an ARRAY[...] literal into a VECTOR(n) column on a document_strict collection fails. The strict coercion path drops every element, then reports the input as having 0 elements. The same literal succeeds on engine='vector' and on schemaless. A string-literal vector succeeds on document_strict. The error surfaces as XX000 (internal error) for what is a value-coercion problem.

Steps to reproduce

CREATE COLLECTION vp_strict (id TEXT PRIMARY KEY, embedding VECTOR(3))
  WITH (engine = 'document_strict');

INSERT INTO vp_strict (id, embedding) VALUES ('a1', ARRAY[0.1,0.2,0.3]);
-- ERROR: serialization error (binary_tuple): bad request:
--        column 'embedding': expected VECTOR(3), got 0 elements (SQLSTATE XX000)

INSERT INTO vp_strict (id, embedding) VALUES ('a2', '[0.1,0.2,0.3]');
-- OK

Isolation across engines, same literal:

Case Result
VECTOR(3) column on document_strict, DDL accepted
ARRAY[0.1,0.2,0.3] on document_strict got 0 elements, XX000
'[0.1,0.2,0.3]' string literal on document_strict accepted
ARRAY[0.1,0.2,0.3] on WITH (engine = 'vector') accepted
ARRAY[0.1,0.2,0.3] on schemaless accepted
FLOAT column on document_strict accepted

Expected behavior

ARRAY[0.1,0.2,0.3] inserts into a VECTOR(3) column on document_strict, matching every other engine. An element the coercion cannot read raises an error naming that element and its type, under a 22xxx or 42804 SQLSTATE. A client's malformed value is never XX000.

Actual behavior

extract_vector_floats at nodedb/src/data/executor/strict_format/coerce.rs:233 reads elements with a filter_map that accepts only Value::Float and Value::Integer:

arr.iter()
    .filter_map(|v| match v {
        Value::Float(f) => Some(*f as f32),
        Value::Integer(n) => Some(*n as f32),
        _ => None,
    })
    .collect()

Any other variant is dropped without an error. validate_and_encode_vector then compares the surviving count against the declared dimension and reports the input as having 0 elements, which misdescribes the cause: the literal carried three elements and all three were discarded.

nodedb_types::Value carries both Float and Decimal. A SQL decimal literal folding to Value::Decimal matches neither arm. That is the likely variant and is unconfirmed.

The typed Error::BadRequest raised there is flattened into Error::Serialization by the strict write wrapper, so it reaches the client as XX000.

This is not a regression. The recent plan-time column work touched coerce.rs and encode.rs only to thread a collection name through test call sites, and changed no element-reading logic.

What actually happened? (check all that are true)

  • A workaround exists (rewrite the query, avoid one path, etc.) — pass the vector as a string literal, or use engine='vector'

Proposed severity

SEV-3 — Medium: feature wrong, but operational and a workaround exists

Reproducibility

Always — every attempt

Last known-good version / commit (if a regression)

Not a regression. Unconfirmed how far back it goes.

Environment & logs

Linux x86_64. Reproduced through the pgwire test harness against a fresh data directory. The dropped elements produce no log line — the filter_map discards them silently.

Before submitting

  • I searched existing issues and this is not a duplicate.
  • I reproduced this on a current main build.
  • This is not a security vulnerability.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:sqlParser, planner, SQL semanticsengine:documentDocument engine (schemaless + strict)engine:vectorVector enginesev:3-mediumFeature wrong, but operational and a workaround existstype:bugA defect — broken, incorrect, or lost data

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions