Skip to content

Commit cb0591b

Browse files
committed
Test public PCO selection contracts without depending on optimized kernels
Signed-off-by: Daniel King <dan@spiraldb.com>
1 parent 2ffabd6 commit cb0591b

2 files changed

Lines changed: 151 additions & 0 deletions

File tree

‎encodings/pco/src/lib.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,6 @@ pub struct PcoMetadata {
6262

6363
#[cfg(test)]
6464
mod tests;
65+
66+
#[cfg(test)]
67+
mod selection_contract_tests;
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
//! Public selection contracts, independent of optional PCO metadata or execution strategy.
5+
6+
use rstest::rstest;
7+
use vortex_array::IntoArray;
8+
use vortex_array::VortexSessionExecute;
9+
use vortex_array::arrays::PiecewiseSequenceArray;
10+
use vortex_array::arrays::PrimitiveArray;
11+
use vortex_array::assert_arrays_eq;
12+
use vortex_array::session::ArraySessionExt;
13+
use vortex_array::validity::Validity;
14+
use vortex_error::VortexResult;
15+
use vortex_session::VortexSession;
16+
17+
use crate::Pco;
18+
19+
#[rstest]
20+
fn selected_values_and_nulls(#[values(false, true)] nullable: bool) -> VortexResult<()> {
21+
let session = VortexSession::empty();
22+
session.arrays().register(Pco);
23+
let mut ctx = session.create_execution_ctx();
24+
let input = PrimitiveArray::new(
25+
(0..4096i64).map(|i| i * 19).collect::<Vec<_>>(),
26+
if nullable {
27+
Validity::from_iter((0..4096).map(|i| i % 7 != 0))
28+
} else {
29+
Validity::NonNullable
30+
},
31+
);
32+
let compressed = Pco::from_primitive(input.as_view(), 3, 31, &mut ctx)?.into_array();
33+
let source = compressed.slice(17..4001)?.slice(13..3900)?;
34+
let expected = input.slice(30..3917)?;
35+
let indices = PrimitiveArray::from_option_iter([
36+
Some(3000u64),
37+
Some(0),
38+
None,
39+
Some(127),
40+
Some(1),
41+
Some(3000),
42+
Some(128),
43+
None,
44+
])
45+
.into_array();
46+
assert_arrays_eq!(
47+
source.take(indices.clone())?,
48+
expected.take(indices)?,
49+
&mut ctx
50+
);
51+
let mut probe = source.repeated_probe();
52+
for i in [3000, 0, 127, 1, 3000, 128, 0] {
53+
assert_eq!(
54+
probe.execute_scalar(i, &mut ctx)?,
55+
expected.execute_scalar(i, &mut ctx)?
56+
);
57+
}
58+
assert!(probe.execute_scalar(source.len(), &mut ctx).is_err());
59+
Ok(())
60+
}
61+
62+
#[rstest]
63+
#[case(vec![0u64, 40, 0, 70], vec![15u64, 20, 3, 0], vec![1u64, 1, 0, 1])]
64+
#[case(vec![100u64, 0, 20], vec![20u64, 20, 20], vec![1u64, 1, 2])]
65+
fn compact_ranges(
66+
#[case] starts: Vec<u64>,
67+
#[case] lengths: Vec<u64>,
68+
#[case] strides: Vec<u64>,
69+
) -> VortexResult<()> {
70+
let session = VortexSession::empty();
71+
let mut ctx = session.create_execution_ctx();
72+
let input =
73+
PrimitiveArray::from_option_iter((0..256i64).map(|i| (i % 5 != 0).then_some(i * i)));
74+
let compressed = Pco::from_primitive(input.as_view(), 3, 7, &mut ctx)?.into_array();
75+
let expected_indices = starts
76+
.iter()
77+
.zip(&lengths)
78+
.zip(&strides)
79+
.flat_map(|((&s, &n), &k)| (0..n).map(move |i| s + i * k))
80+
.collect::<Vec<_>>();
81+
let indices = PiecewiseSequenceArray::try_new(
82+
PrimitiveArray::from_iter(starts).into_array(),
83+
PrimitiveArray::from_iter(lengths).into_array(),
84+
PrimitiveArray::from_iter(strides).into_array(),
85+
expected_indices.len(),
86+
)?
87+
.into_array();
88+
assert_arrays_eq!(
89+
compressed.take(indices)?,
90+
input.take(PrimitiveArray::from_iter(expected_indices).into_array())?,
91+
&mut ctx
92+
);
93+
Ok(())
94+
}
95+
96+
#[rstest]
97+
fn null_runs_and_empty(#[values(false, true)] all_null: bool) -> VortexResult<()> {
98+
let session = VortexSession::empty();
99+
let mut ctx = session.create_execution_ctx();
100+
let input = PrimitiveArray::from_option_iter(
101+
(0..65536i32).map(|i| (!all_null && !(100..65000).contains(&i)).then_some(i)),
102+
);
103+
let compressed = Pco::from_primitive(input.as_view(), 3, 128, &mut ctx)?.into_array();
104+
let indices =
105+
PrimitiveArray::from_option_iter([Some(0u64), None, Some(65001), Some(30000), Some(0)])
106+
.into_array();
107+
assert_arrays_eq!(
108+
compressed.take(indices.clone())?,
109+
input.take(indices)?,
110+
&mut ctx
111+
);
112+
assert_arrays_eq!(
113+
compressed.slice(99..65002)?,
114+
input.slice(99..65002)?,
115+
&mut ctx
116+
);
117+
assert_arrays_eq!(
118+
compressed.take(PrimitiveArray::from_iter([] as [u64; 0]).into_array())?,
119+
input.take(PrimitiveArray::from_iter([] as [u64; 0]).into_array())?,
120+
&mut ctx
121+
);
122+
Ok(())
123+
}
124+
125+
#[test]
126+
fn invalid_indices_under_nulls_are_ignored() -> VortexResult<()> {
127+
let session = VortexSession::empty();
128+
let mut ctx = session.create_execution_ctx();
129+
let input = PrimitiveArray::from_iter([11i32, 22, 33]);
130+
let compressed = Pco::from_primitive(input.as_view(), 3, 2, &mut ctx)?.into_array();
131+
let indices = PrimitiveArray::new(
132+
vec![0i64, i64::MIN, i64::MAX, 2],
133+
Validity::from_iter([true, false, false, true]),
134+
)
135+
.into_array();
136+
assert_arrays_eq!(
137+
compressed.take(indices.clone())?,
138+
input.take(indices)?,
139+
&mut ctx
140+
);
141+
assert!(
142+
compressed
143+
.take(PrimitiveArray::from_iter([u64::MAX]).into_array())?
144+
.execute::<PrimitiveArray>(&mut ctx)
145+
.is_err()
146+
);
147+
Ok(())
148+
}

0 commit comments

Comments
 (0)