Lantao Jin created CALCITE-7857:
-----------------------------------

             Summary: Add a VECTOR data type
                 Key: CALCITE-7857
                 URL: https://issues.apache.org/jira/browse/CALCITE-7857
             Project: Calcite
          Issue Type: New Feature
          Components: core
            Reporter: Lantao Jin


h2. Motivation

Vector (embedding) search has become a standard database feature, and most SQL 
engines now have a vector type whose dimension is part of the type: PostgreSQL 
with pgvector {{vector(n)}} (also {{halfvec(n)}}, {{bit(n)}}), SQL Server 2025 
{{VECTOR(n [, float16])}}, Oracle 26ai {{VECTOR(n, FLOAT32|INT8|BINARY)}}, 
Snowflake {{VECTOR(FLOAT|INT, n)}}, MariaDB, MySQL and TiDB {{VECTOR(n)}}, 
Cassandra 5 {{VECTOR<FLOAT, n>}}, SAP HANA {{REAL_VECTOR(n)}}. DuckDB uses 
fixed-size arrays {{FLOAT[n]}}, and Spark has an open SPIP (SPARK-58787) for 
{{VECTOR(n, type)}}.

Calcite has no vector type, and cannot represent an embedding's dimension: 
{{RelDataTypeFactory.createArrayType(elementType, maxCardinality)}} accepts a 
cardinality, but {{SqlTypeFactoryImpl.createArrayType}} asserts 
{{maxCardinality == -1}}, and {{ArraySqlType}} stores only the element type.

Downstream projects work around this. The OpenSearch SQL plugin's RFC for 
vector search in PPL on Calcite (opensearch-project/sql#5875) emulates a vector 
type with a custom {{ArraySqlType}} subclass; the proposed Milvus adapter 
(CALCITE-7353) maps vector fields to existing types through adapter metadata. 
Neither can check dimensions during validation, generate the right type in SQL 
for a database that has a vector type, or give distance functions a precise 
operand type.

h2. Proposal

Add a type {{VECTOR(dimension [, elementType])}}, where {{dimension}} is a 
positive integer and {{elementType}} is {{REAL}} (4-byte float, the default) or 
{{TINYINT}} (1-byte integer).

{code:sql}
SELECT CAST(ARRAY[1, 2.5, -3] AS VECTOR(3));         -- type VECTOR(3, REAL)
SELECT CAST('[1, 2.5, -3]' AS VECTOR(3));            -- same value
SELECT CAST(ARRAY[1, 2, 3] AS VECTOR(3, TINYINT));
SELECT CAST(v AS REAL ARRAY), CAST(v AS VARCHAR) FROM t;

-- With the server module's DDL
CREATE TABLE docs (id INT, embedding VECTOR(768));
INSERT INTO docs VALUES (1, ARRAY[...]), (2, '[...]'); -- implicit cast on 
INSERT
{code}

h2. Semantics

# *Type.* New {{SqlTypeName.VECTOR}}, {{SqlTypeFamily.VECTOR}} and class 
{{VectorSqlType}} (a sibling of {{ArraySqlType}}, not a subclass), created by a 
new method {{RelDataTypeFactory.createVectorType(elementType, dimension)}}. The 
type's digest is {{VECTOR(3, REAL)}}; {{getComponentType()}} returns the 
element type, and {{getPrecision()}} returns the dimension, so JDBC reports the 
dimension as the column size.
# *Nullability.* Elements are never null; a vector value as a whole may be null.
# *Explicit casts.* An {{ARRAY}} of numbers and a {{VECTOR}} cast to each 
other, element by element. A character string such as {{'[1, 2.5, -3]'}} 
(numbers separated by commas, in square brackets) casts to a {{VECTOR}}, and a 
{{VECTOR}} casts to a string in the same format. A {{VECTOR}} casts to a 
{{VECTOR}} of the same dimension with a different element type. Casts to and 
from other types (numbers, {{BINARY}}, {{VARIANT}}, {{MULTISET}}, {{MAP}}) are 
not allowed.
# *Dimension checks.* If the operand of {{CAST}} is an array constructor or a 
character literal, a wrong number of elements is a validation error; otherwise 
it is a run-time error, as is a null element.
# *Implicit conversions.* None in expressions. A vector can be assigned to a 
{{VECTOR}} of the same dimension and element type, and {{CASE}}, {{COALESCE}} 
and {{UNION ALL}} accept vectors of the same dimension and element type. With 
type coercion, {{INSERT}} and {{UPDATE}} cast an {{ARRAY}}, a character string, 
or a vector with another element type to the target column's {{VECTOR}} type.
# *Comparability.* {{VectorSqlType.getComparability()}} returns {{NONE}}, so 
{{=}}, {{<>}}, {{<}}, {{IS DISTINCT FROM}}, {{NULLIF}}, {{MIN}} and {{MAX}} 
reject vectors. The validator also rejects vectors as keys of {{ORDER BY}}, 
{{GROUP BY}}, {{PARTITION BY}}, window {{ORDER BY}}, {{SELECT DISTINCT}}, 
aggregate {{DISTINCT}}, {{WITHIN DISTINCT}} and {{WITHIN GROUP}}, {{UNION}}, 
{{INTERSECT}}, {{EXCEPT}} and {{USING}}/{{NATURAL}} joins; {{IN}} and 
{{BETWEEN}} already reject them. These checks use the type's comparability, so 
they also apply to user-defined structured types whose comparability is 
{{NONE}}. SQL Server and Oracle have similar restrictions; pgvector allows 
ordering and equality. Allowing equality later would be backward-compatible, 
but disallowing it later would not.
# *Run time.* As with {{ARRAY}}, a vector is a {{java.util.List}} of its 
elements; JDBC returns it as a {{java.sql.Array}}.
# *SQL generation.* The Calcite dialect writes {{VECTOR(n, elementType)}}. The 
PostgreSQL dialect writes pgvector's {{VECTOR(n)}} for {{REAL}} elements. Other 
dialects throw {{UnsupportedOperationException}}; by default, 
{{SqlDialect.supportsDataType}} now returns false for {{VECTOR}}.
# *Serialization.* {{RelJson}} writes a vector type with its {{component}} and 
{{dimension}}. There are no {{RexLiteral}}s of type {{VECTOR}}; a constant 
vector is a {{CAST}} of an array constructor.

h2. Scope

This issue: the type, parser, validator, casts, Enumerable run time, 
{{RelJson}}, SQL generation for the Calcite and PostgreSQL dialects, and 
documentation in {{reference.md}}.

Follow-up issues:
* Distance functions, such as {{COSINE_DISTANCE}}, {{L2_DISTANCE}} and 
{{INNER_PRODUCT}}, in {{SqlLibraryOperators}} for each library (pgvector's 
{{<->}}, {{<=>}} and {{<#>}} operators, {{VECTOR_DISTANCE}} in SQL Server and 
Oracle, {{VECTOR_COSINE_SIMILARITY}} in Snowflake), with SQL generation for 
each dialect.
* More element types (DOUBLE, half precision, bit vectors), sparse vectors, and 
a maximum dimension in {{RelDataTypeSystem}}.
* SQL generation for SQL Server, Oracle, Snowflake, MySQL and MariaDB; mapping 
of vendor vector types in the JDBC adapter.
* Casts to and from {{VARIANT}}; element access {{v[i]}} and {{CARDINALITY(v)}} 
without a cast to {{ARRAY}}.
* Comparability of a {{ROW}} that contains a vector: {{ROW(v, 1) = ROW(v, 1)}} 
is not yet rejected, because a record type is always comparable.

Out of scope: index-aware planning, such as rules that use an 
approximate-nearest-neighbor index for {{ORDER BY distance LIMIT k}}.

h2. Open questions

# Syntax: {{VECTOR(n, type)}} (SQL Server, Oracle, the Spark SPIP), 
{{VECTOR(type, n)}} (Snowflake), {{VECTOR<type, n>}} (Cassandra), or {{type 
VECTOR(n)}} (by analogy with {{type ARRAY}})?
# Element type names: Calcite's ({{REAL}}, {{TINYINT}}) or vendor names 
({{FLOAT32}}, {{INT8}})? Which element types belong in the first version?
# Should {{VECTOR}} be a subtype of {{ARRAY}} for coercion, so that array 
functions accept vectors directly, or stay a distinct type as proposed?
# Should vectors support equality (comparability {{UNORDERED}}), and therefore 
{{GROUP BY}} and {{DISTINCT}}, as pgvector does?
# Should the element type have a default ({{REAL}}), or be required?
# Should a cast from a string accept {{NaN}} and infinite values? (pgvector 
rejects them.)

h2. References

* pgvector: https://github.com/pgvector/pgvector
* SQL Server vector data type: 
https://learn.microsoft.com/en-us/sql/t-sql/data-types/vector-data-type
* Oracle VECTOR data type: 
https://docs.oracle.com/en/database/oracle/oracle-database/23/vecse/create-tables-using-vector-data-type.html
* Snowflake VECTOR data type: 
https://docs.snowflake.com/en/sql-reference/data-types-vector
* SPARK-58787 (SPIP: VECTOR type)
* OpenSearch SQL RFC, vector search in PPL: 
https://github.com/opensearch-project/sql/issues/5875
* CALCITE-7353 (Milvus adapter)
* Precedents for a new type: CALCITE-6738 (UUID) and follow-ups CALCITE-6777, 
CALCITE-6779, CALCITE-6992, CALCITE-7128, CALCITE-7158, CALCITE-7716, 
CALCITE-7727, CALCITE-7768; CALCITE-4918 (VARIANT)



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to