Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/pgrepr/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,10 @@ impl Value {
Some(Value::Array { dims, elements })
}
(Datum::Array(array), SqlScalarType::Int2Vector) => {
let dims = array.dims().into_iter();
assert!(dims.count() == 1, "int2vector must be 1 dimensional");
assert!(
array.has_int2vector_dims(),
"int2vector must be 1 dimensional, or empty"
);
let elements = array
.elements()
.iter()
Expand Down
83 changes: 83 additions & 0 deletions src/repr/src/adt/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ impl<'a> Array<'a> {
pub fn elements(&self) -> DatumList<'a> {
self.elements
}

/// Returns true if this array's dimensions are valid for the Int2Vector type.
/// Int2Vector is 1-D; empty arrays use 0 dimensions (PostgreSQL convention).
pub fn has_int2vector_dims(&self) -> bool {
self.dims().len() == 1
|| (self.dims().len() == 0 && self.elements().iter().next().is_none())
}
}

/// The dimensions of an [`Array`].
Expand Down Expand Up @@ -289,12 +296,88 @@ impl FixedSizeCodec<ArrayDimension> for PackedArrayDimension {

#[cfg(test)]
mod tests {
use std::iter;

use mz_ore::assert_ok;
use mz_proto::protobuf_roundtrip;
use proptest::prelude::*;

use crate::Datum;
use crate::row::Row;

use super::*;

#[mz_ore::test]
fn test_has_int2vector_dims() {
// 1-D array with elements: valid for int2vector
let mut row = Row::default();
row.packer()
.try_push_array(
&[ArrayDimension {
lower_bound: 1,
length: 2,
}],
[Datum::Int16(1), Datum::Int16(2)],
)
.unwrap();
let arr = row.unpack_first().unwrap_array();
assert!(
arr.has_int2vector_dims(),
"1-D array should have int2vector dims"
);

// 1-D empty array (length 0): valid
let mut row = Row::default();
row.packer()
.try_push_array(
&[ArrayDimension {
lower_bound: 1,
length: 0,
}],
iter::empty::<Datum>(),
)
.unwrap();
let arr = row.unpack_first().unwrap_array();
assert!(
arr.has_int2vector_dims(),
"1-D empty array should have int2vector dims"
);

// 0-D empty array (PostgreSQL convention for empty): valid
let mut row = Row::default();
row.packer()
.try_push_array(&[], iter::empty::<Datum>())
.unwrap();
let arr = row.unpack_first().unwrap_array();
assert!(
arr.has_int2vector_dims(),
"0-D empty array should have int2vector dims"
);

// 2-D array: invalid for int2vector
let mut row = Row::default();
row.packer()
.try_push_array(
&[
ArrayDimension {
lower_bound: 1,
length: 1,
},
ArrayDimension {
lower_bound: 1,
length: 2,
},
],
[Datum::Int16(1), Datum::Int16(2)],
)
.unwrap();
let arr = row.unpack_first().unwrap_array();
assert!(
!arr.has_int2vector_dims(),
"2-D array should not have int2vector dims"
);
}

proptest! {
#[mz_ore::test]
fn invalid_array_error_protobuf_roundtrip(expect in any::<InvalidArrayError>()) {
Expand Down
4 changes: 2 additions & 2 deletions src/repr/src/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,7 @@ impl<'a> Datum<'a> {
})
}
(Datum::Array(array), ReprScalarType::Int2Vector) => {
array.dims().len() == 1
array.has_int2vector_dims()
&& array
.elements
.iter()
Expand Down Expand Up @@ -1204,7 +1204,7 @@ impl<'a> Datum<'a> {
})
}
(Datum::Array(array), SqlScalarType::Int2Vector) => {
array.dims().len() == 1
array.has_int2vector_dims()
&& array
.elements
.iter()
Expand Down
4 changes: 3 additions & 1 deletion src/transform/src/reprtypecheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,9 @@ fn datum_difference_with_column_type(
Ok(())
}
(Datum::Array(array), ReprScalarType::Int2Vector) => {
if array.dims().len() != 1 {
if !array.has_int2vector_dims() {
// Int2Vector is 1-D, but empty arrays use 0 dimensions (PostgreSQL convention,
// the error message just mentions 1 dimension, but 0 dimensions are allowed when empty.
return Err(DatumTypeDifference::MismatchDimensions {
ctor: "int2vector".to_string(),
got: array.dims().len(),
Expand Down
34 changes: 34 additions & 0 deletions test/sqllogictest/int2vector.slt
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,37 @@ FROM
{"1 2","3 4"}
5
6

# Ensure that empty int2vectors are handled correctly.
# Empty int2vector formats as (empty); cast to text so we don't send int2vector (no binary encoding).
query T
SELECT ''::pg_catalog.int2vector::text
----
(empty)

# Table with int2vector column and default ''; select returns x::text to avoid binary encoding.
statement ok
CREATE TABLE t (x int2vector default '');

query T
SELECT x::text FROM t;
----

# Insert empty int2vector
statement ok
INSERT INTO t VALUES ('');

query T
SELECT x::text FROM t;
----
(empty)

# Insert single-element int2vector
statement ok
INSERT INTO t VALUES ('1');

query T
SELECT x::text FROM t ORDER BY x::text DESC;
----
1
(empty)