Skip to content

Commit 46c1d5c

Browse files
Merge branch 'develop' into nemo/geo-q1
2 parents f52a2c4 + 67a2b22 commit 46c1d5c

66 files changed

Lines changed: 1733 additions & 359 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/cuda.yaml

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,31 @@ env:
2121
RUST_BACKTRACE: 1
2222

2323
jobs:
24+
changes:
25+
name: "Detect CUDA changes"
26+
runs-on: ubuntu-latest
27+
timeout-minutes: 10
28+
permissions:
29+
pull-requests: read
30+
outputs:
31+
run-cuda-san: ${{ github.event_name != 'pull_request' || steps.filter.outputs.cuda == 'true' }}
32+
steps:
33+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
34+
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4
35+
id: filter
36+
if: github.event_name == 'pull_request'
37+
with:
38+
filters: |
39+
cuda:
40+
- "vortex-cuda/**"
41+
- "vortex-test/**"
42+
- ".github/workflows/**"
43+
2444
cuda-build-lint:
25-
if: github.repository == 'vortex-data/vortex'
45+
needs: [changes]
46+
if: >-
47+
always() && github.repository == 'vortex-data/vortex' &&
48+
needs.changes.outputs.run-cuda-san == 'true'
2649
name: "CUDA build & lint"
2750
timeout-minutes: 30
2851
runs-on: runs-on=${{ github.run_id }}/runner=gpu/tag=cuda-build
@@ -52,7 +75,10 @@ jobs:
5275
-- -D warnings
5376
5477
cuda-test:
55-
if: github.repository == 'vortex-data/vortex'
78+
needs: [changes]
79+
if: >-
80+
always() && github.repository == 'vortex-data/vortex' &&
81+
needs.changes.outputs.run-cuda-san == 'true'
5682
name: "CUDA tests"
5783
timeout-minutes: 30
5884
runs-on: runs-on=${{ github.run_id }}/runner=gpu/tag=cuda-tests
@@ -70,7 +96,7 @@ jobs:
7096
with:
7197
repo-token: ${{ secrets.GITHUB_TOKEN }}
7298
- name: Install nextest
73-
uses: taiki-e/install-action@0631aa6515c7d545823c67cfae7ef4fc7f490154 # v2
99+
uses: taiki-e/install-action@7a79fe8c3a13344501c80d99cae481c1c9085912 # v2
74100
with:
75101
tool: nextest
76102
- name: Rust Tests
@@ -92,7 +118,10 @@ jobs:
92118
--verbose
93119
94120
cuda-test-sanitizer:
95-
if: github.repository == 'vortex-data/vortex'
121+
needs: [changes]
122+
if: >-
123+
always() && github.repository == 'vortex-data/vortex' &&
124+
needs.changes.outputs.run-cuda-san == 'true'
96125
name: "CUDA tests (${{ matrix.sanitizer }})"
97126
timeout-minutes: 30
98127
runs-on: runs-on=${{ github.run_id }}/runner=gpu/tag=cuda-test-sanitizer
@@ -130,7 +159,10 @@ jobs:
130159
run: cargo test --profile ci --locked -p vortex-cuda --all-features --target x86_64-unknown-linux-gnu
131160

132161
cuda-test-cudf:
133-
if: github.repository == 'vortex-data/vortex'
162+
needs: [changes]
163+
if: >-
164+
always() && github.repository == 'vortex-data/vortex' &&
165+
needs.changes.outputs.run-cuda-san == 'true'
134166
name: "CUDA tests (cudf)"
135167
timeout-minutes: 30
136168
runs-on: runs-on=${{ github.run_id }}/runner=gpu/tag=cuda-test-cudf

.github/workflows/fuzzer-fix-automation.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ jobs:
273273
CRASH_FILE: ${{ steps.extract.outputs.crash_file }}
274274
CRASH_FILE_PATH: ${{ steps.download.outputs.crash_file_path }}
275275
ARTIFACT_URL: ${{ steps.extract.outputs.artifact_url }}
276-
uses: anthropics/claude-code-action@fbda2eb1bdc90d319b8d853f5deb53bca199a7c1 # v1
276+
uses: anthropics/claude-code-action@d5726de019ec4498aa667642bc3a80fca83aa102 # v1
277277
with:
278278
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
279279
# Use the App token (not GITHUB_TOKEN) so the committed fix branch and

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ jobs:
9393
with:
9494
distribution: "corretto"
9595
java-version: "17"
96-
- uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6
96+
- uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6
9797
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
9898
with:
9999
pattern: libvortex_jni_*.zip

.github/workflows/report-fuzz-crash.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ jobs:
118118
steps.dedup.outputs.duplicate != 'true' ||
119119
steps.dedup.outputs.confidence != 'exact'
120120
continue-on-error: true
121-
uses: anthropics/claude-code-action@fbda2eb1bdc90d319b8d853f5deb53bca199a7c1 # v1
121+
uses: anthropics/claude-code-action@d5726de019ec4498aa667642bc3a80fca83aa102 # v1
122122
with:
123123
claude_code_oauth_token: ${{ secrets.claude_code_oauth_token }}
124124
github_token: ${{ secrets.gh_token }}

.github/workflows/rust-instrumented.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,8 @@ jobs:
221221
- name: Run tests
222222
run: |
223223
set -o pipefail
224-
./vortex-ffi/build/test/vortex_ffi_test 2>&1 | rustfilt
224+
# re-enable once we can fix this hang
225+
# ./vortex-ffi/build/test/vortex_ffi_test 2>&1 | rustfilt
225226
- name: Run examples
226227
run: |
227228
set -o pipefail

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

deny.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ ignore = [
1616
# Paste is no longer maintained because its essentially "finished".
1717
"RUSTSEC-2024-0436",
1818
# proc-macro-error-2 is unmaintained, only used by the `test_with` test dependency
19-
"RUSTSEC-2026-0173"
19+
"RUSTSEC-2026-0173",
20+
# Out-of-bounds read in `nth`/`nth_back` on pyo3 list/tuple iterators, fixed only in pyo3
21+
# 0.29.0. We cannot bump until pyo3-bytes, pyo3-log, and pyo3-object_store support 0.29 (all
22+
# pin pyo3 to <0.29, and pyo3-ffi `links = "python"` forbids two pyo3 versions in the graph).
23+
# Not exploitable here: `vortex-python` never calls `nth`/`nth_back` on these iterators.
24+
"RUSTSEC-2026-0176"
2025
]
2126

2227
[licenses]
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use vortex_array::IntoArray;
5+
use vortex_array::ValidityVTable;
6+
use vortex_array::arrays::ConstantArray;
7+
use vortex_array::builtins::ArrayBuiltins;
8+
use vortex_array::dtype::DType;
9+
use vortex_array::dtype::PType;
10+
use vortex_array::scalar::Scalar;
11+
use vortex_array::scalar_fn::fns::byte_length::ByteLengthKernel;
12+
use vortex_array::validity::Validity;
13+
14+
use crate::OnPair;
15+
use crate::OnPairArraySlotsExt;
16+
17+
// TODO(myrrc): this and FSST comparison should be in the same trait.
18+
// https://github.com/vortex-data/vortex/tree/myrrc/onpair-compare-trait
19+
impl ByteLengthKernel for OnPair {
20+
fn byte_length(
21+
array: vortex_array::ArrayView<'_, Self>,
22+
_ctx: &mut vortex_array::ExecutionCtx,
23+
) -> vortex_error::VortexResult<Option<vortex_array::ArrayRef>> {
24+
let nullable = array.dtype().nullability();
25+
let dtype = DType::Primitive(PType::U64, nullable);
26+
// Uncompressed lengths are non-nullable and may be less than u64 each
27+
let lengths = array.uncompressed_lengths().cast(dtype.clone())?;
28+
Ok(Some(match OnPair::validity(array)? {
29+
Validity::NonNullable | Validity::AllValid => lengths,
30+
Validity::Array(v) => lengths.mask(v)?,
31+
Validity::AllInvalid => {
32+
ConstantArray::new(Scalar::null(dtype), lengths.len()).into_array()
33+
}
34+
}))
35+
}
36+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// SPDX-FileCopyrightText: Copyright the Vortex contributors
3+
4+
use vortex_array::ArrayRef;
5+
use vortex_array::ArrayView;
6+
use vortex_array::ExecutionCtx;
7+
use vortex_array::IntoArray;
8+
use vortex_array::arrays::BoolArray;
9+
use vortex_array::arrays::ConstantArray;
10+
use vortex_array::builtins::ArrayBuiltins;
11+
use vortex_array::dtype::DType;
12+
use vortex_array::scalar::Scalar;
13+
use vortex_array::scalar_fn::fns::binary::CompareKernel;
14+
use vortex_array::scalar_fn::fns::operators::CompareOperator;
15+
use vortex_buffer::BitBuffer;
16+
use vortex_error::VortexResult;
17+
18+
use crate::OnPair;
19+
use crate::OnPairArraySlotsExt;
20+
21+
impl CompareKernel for OnPair {
22+
fn compare(
23+
lhs: ArrayView<'_, Self>,
24+
rhs: &ArrayRef,
25+
operator: CompareOperator,
26+
ctx: &mut ExecutionCtx,
27+
) -> VortexResult<Option<ArrayRef>> {
28+
let Some(constant) = rhs.as_constant() else {
29+
return Ok(None);
30+
};
31+
let is_empty = match constant.dtype() {
32+
DType::Utf8(_) => constant.as_utf8().is_empty(),
33+
DType::Binary(_) => constant.as_binary().is_empty(),
34+
_ => return Ok(None),
35+
};
36+
if is_empty != Some(true) {
37+
return Ok(None);
38+
}
39+
40+
let lengths = lhs.uncompressed_lengths();
41+
let buffer = match operator {
42+
// every value is greater than an empty string
43+
CompareOperator::Gte => BitBuffer::new_set(lhs.len()),
44+
// no value is less than an empty string
45+
CompareOperator::Lt => BitBuffer::new_unset(lhs.len()),
46+
_ => lengths
47+
.binary(
48+
ConstantArray::new(Scalar::zero_value(lengths.dtype()), lengths.len())
49+
.into_array(),
50+
operator.into(),
51+
)?
52+
.execute(ctx)?,
53+
};
54+
Ok(Some(
55+
BoolArray::new(
56+
buffer,
57+
lhs.validity()?
58+
.union_nullability(constant.dtype().nullability()),
59+
)
60+
.into_array(),
61+
))
62+
}
63+
}
64+
65+
#[cfg(test)]
66+
mod tests {
67+
use std::sync::LazyLock;
68+
69+
use rstest::rstest;
70+
use vortex_array::IntoArray;
71+
use vortex_array::VortexSessionExecute;
72+
use vortex_array::arrays::BoolArray;
73+
use vortex_array::arrays::ConstantArray;
74+
use vortex_array::arrays::VarBinArray;
75+
use vortex_array::assert_arrays_eq;
76+
use vortex_array::builtins::ArrayBuiltins;
77+
use vortex_array::dtype::DType;
78+
use vortex_array::dtype::Nullability;
79+
use vortex_array::scalar::Scalar;
80+
use vortex_array::scalar_fn::fns::operators::Operator;
81+
use vortex_array::session::ArraySession;
82+
use vortex_error::VortexResult;
83+
use vortex_session::VortexSession;
84+
85+
use crate::compress::DEFAULT_DICT12_CONFIG;
86+
use crate::compress::onpair_compress;
87+
88+
static SESSION: LazyLock<VortexSession> =
89+
LazyLock::new(|| VortexSession::empty().with::<ArraySession>());
90+
91+
#[cfg_attr(miri, ignore)]
92+
#[rstest]
93+
#[case(Operator::Eq, [true, false, true, false])]
94+
#[case(Operator::NotEq, [false, true, false, true])]
95+
#[case(Operator::Gt, [false, true, false, true])]
96+
#[case(Operator::Gte, [true, true, true, true])]
97+
#[case(Operator::Lt, [false, false, false, false])]
98+
#[case(Operator::Lte, [true, false, true, false])]
99+
fn compare_empty_string(#[case] op: Operator, #[case] expected: [bool; 4]) -> VortexResult<()> {
100+
let input = VarBinArray::from_iter(
101+
[Some(""), Some("a"), Some(""), Some("bbb")],
102+
DType::Utf8(Nullability::NonNullable),
103+
);
104+
let arr = onpair_compress(&input, input.len(), input.dtype(), DEFAULT_DICT12_CONFIG)?
105+
.into_array();
106+
107+
let mut ctx = SESSION.create_execution_ctx();
108+
let result = arr
109+
.binary(ConstantArray::new("", input.len()).into_array(), op)?
110+
.execute::<BoolArray>(&mut ctx)?;
111+
assert_arrays_eq!(&result, &BoolArray::from_iter(expected));
112+
Ok(())
113+
}
114+
115+
#[cfg_attr(miri, ignore)]
116+
#[test]
117+
fn compare_empty_string_nullable() -> VortexResult<()> {
118+
let input = VarBinArray::from_iter(
119+
[Some(""), None, Some("x")],
120+
DType::Utf8(Nullability::Nullable),
121+
);
122+
let arr = onpair_compress(&input, input.len(), input.dtype(), DEFAULT_DICT12_CONFIG)?
123+
.into_array();
124+
let mut ctx = SESSION.create_execution_ctx();
125+
126+
let eq_empty = arr
127+
.clone()
128+
.binary(ConstantArray::new("", arr.len()).into_array(), Operator::Eq)?
129+
.execute::<BoolArray>(&mut ctx)?;
130+
assert_arrays_eq!(
131+
&eq_empty,
132+
&BoolArray::from_iter([Some(true), None, Some(false)])
133+
);
134+
135+
let null_rhs =
136+
ConstantArray::new(Scalar::null(DType::Utf8(Nullability::Nullable)), arr.len());
137+
let eq_null = arr
138+
.binary(null_rhs.into_array(), Operator::Eq)?
139+
.execute::<BoolArray>(&mut ctx)?;
140+
assert_arrays_eq!(&eq_null, &BoolArray::from_iter([None::<bool>, None, None]));
141+
Ok(())
142+
}
143+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// SPDX-FileCopyrightText: Copyright the Vortex contributors
33

4+
mod byte_length;
45
mod cast;
6+
mod compare;
57
mod filter;
68
mod slice;

0 commit comments

Comments
 (0)