This is an automated email from the ASF dual-hosted git repository.

zhangstar333 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 07b5814ebc0 [chore](lance) add lance patch in thirdparty (#66868)
07b5814ebc0 is described below

commit 07b5814ebc0091c1e0eada2955454320e7f809a9
Author: zhangstar333 <[email protected]>
AuthorDate: Tue Aug 18 22:53:11 2026 +0800

    [chore](lance) add lance patch in thirdparty (#66868)
    
    ### What problem does this PR solve?
    Problem Summary:
    https://github.com/lance-format/lance-c/pull/59
    https://github.com/lance-format/lance-c/pull/60
---
 thirdparty/download-thirdparty.sh            |  13 +
 thirdparty/patches/lance-c-0.1.6-doris.patch | 632 +++++++++++++++++++++++++++
 2 files changed, 645 insertions(+)

diff --git a/thirdparty/download-thirdparty.sh 
b/thirdparty/download-thirdparty.sh
index 7c967d31dd9..830da72eacf 100755
--- a/thirdparty/download-thirdparty.sh
+++ b/thirdparty/download-thirdparty.sh
@@ -819,6 +819,19 @@ if [[ " ${TP_ARCHIVES[*]} " =~ " PAIMON_CPP " ]]; then
     echo "Finished patching ${PAIMON_CPP_SOURCE}"
 fi
 
+# Patch lance-c for fragment-scoped nearest-neighbor search and row-ID-based 
fetching.
+if [[ " ${TP_ARCHIVES[*]} " =~ " LANCE_C " ]]; then
+    if [[ "${LANCE_C_SOURCE}" == "lance-c-0.1.6" ]]; then
+        cd "${TP_SOURCE_DIR}/${LANCE_C_SOURCE}"
+        if [[ ! -f "${PATCHED_MARK}" ]]; then
+            patch -p1 <"${TP_PATCH_DIR}/lance-c-0.1.6-doris.patch"
+            touch "${PATCHED_MARK}"
+        fi
+        cd -
+    fi
+    echo "Finished patching ${LANCE_C_SOURCE}"
+fi
+
 if [[ " ${TP_ARCHIVES[*]} " =~ " CCTZ " ]] ; then
     cd $TP_SOURCE_DIR/$CCTZ_SOURCE
     if [[ ! -f "$PATCHED_MARK" ]] ; then
diff --git a/thirdparty/patches/lance-c-0.1.6-doris.patch 
b/thirdparty/patches/lance-c-0.1.6-doris.patch
new file mode 100644
index 00000000000..cc49293d9de
--- /dev/null
+++ b/thirdparty/patches/lance-c-0.1.6-doris.patch
@@ -0,0 +1,632 @@
+diff --git a/include/lance/lance.h b/include/lance/lance.h
+index 1de72fa..a0a8b5e 100644
+--- a/include/lance/lance.h
++++ b/include/lance/lance.h
+@@ -752,0 +753,25 @@ int32_t lance_dataset_take(
++/**
++ * Take rows by dataset row IDs.
++ *
++ * Row IDs are values from the `_rowid` scanner column, not zero-based row
++ * offsets. They must belong to the same dataset snapshot used for this read.
++ * Missing or deleted row IDs may be omitted from the result. For found rows,
++ * input order and duplicates are preserved.
++ *
++ * @param dataset      Open dataset snapshot.
++ * @param row_ids      Array of dataset row IDs. May be NULL only when
++ *                     `num_row_ids` is zero.
++ * @param num_row_ids  Length of `row_ids`.
++ * @param columns      NULL-terminated column names, or NULL for all. The
++ *                     system column `_rowid` may be requested explicitly.
++ * @param out          Pointer to caller-allocated ArrowArrayStream.
++ * @return 0 on success, -1 on error.
++ */
++int32_t lance_dataset_take_rows(
++    const LanceDataset* dataset,
++    const uint64_t* row_ids,
++    size_t num_row_ids,
++    const char* const* columns,
++    struct ArrowArrayStream* out
++);
++
+diff --git a/include/lance/lance.hpp b/include/lance/lance.hpp
+index 40aa9e3..d54dc5a 100644
+--- a/include/lance/lance.hpp
++++ b/include/lance/lance.hpp
+@@ -654,0 +655,24 @@ public:
++    /// Take rows by dataset row IDs. Results exported as ArrowArrayStream.
++    void take_rows(const uint64_t* row_ids, size_t num_row_ids,
++                   const std::vector<std::string>& columns,
++                   ArrowArrayStream* out) const {
++        std::vector<const char*> col_ptrs;
++        for (auto& c : columns) col_ptrs.push_back(c.c_str());
++        col_ptrs.push_back(nullptr);
++        const char* const* cols_ptr = columns.empty() ? nullptr : 
col_ptrs.data();
++
++        if (lance_dataset_take_rows(
++                handle_.get(), row_ids, num_row_ids, cols_ptr, out) != 0) {
++            check_error();
++        }
++    }
++
++    /// Take all columns by dataset row IDs.
++    void take_rows(const uint64_t* row_ids, size_t num_row_ids,
++                   ArrowArrayStream* out) const {
++        if (lance_dataset_take_rows(
++                handle_.get(), row_ids, num_row_ids, nullptr, out) != 0) {
++            check_error();
++        }
++    }
++
+diff --git a/src/dataset.rs b/src/dataset.rs
+index 91a735f..364be09 100644
+--- a/src/dataset.rs
++++ b/src/dataset.rs
+@@ -44,0 +45,20 @@ impl LanceDataset {
++fn projection_from_columns(
++    dataset: &Dataset,
++    columns: Option<&[String]>,
++) -> Result<lance::dataset::ProjectionRequest> {
++    match columns {
++        Some(columns) => {
++            let schema = dataset
++                .schema()
++                .project_preserve_system_columns(columns)
++                .map_err(|err| {
++                    lance_core::Error::invalid_input(format!("invalid columns 
{columns:?}: {err}"))
++                })?;
++            Ok(lance::dataset::ProjectionRequest::from_schema(schema))
++        }
++        None => Ok(lance::dataset::ProjectionRequest::from_schema(
++            dataset.schema().clone(),
++        )),
++    }
++}
++
+@@ -247,4 +267 @@ unsafe fn dataset_take_inner(
+-    let projection = match &col_names {
+-        Some(cols) => 
lance::dataset::ProjectionRequest::from_columns(cols.iter(), snap.schema()),
+-        None => 
lance::dataset::ProjectionRequest::from_schema(snap.schema().clone()),
+-    };
++    let projection = projection_from_columns(&snap, col_names.as_deref())?;
+@@ -263,0 +281,69 @@ unsafe fn dataset_take_inner(
++/// Take rows by dataset row IDs, returning results as an ArrowArrayStream.
++///
++/// - `row_ids`: array of dataset row IDs, such as values returned in the
++///   `_rowid` scanner column
++/// - `num_row_ids`: length of the row ID array
++/// - `columns`: NULL-terminated column name array, or NULL for all columns
++/// - `out`: pointer to a stack-allocated `ArrowArrayStream`
++///
++/// `row_ids` may be NULL only when `num_row_ids` is zero. Row IDs must belong
++/// to the same dataset snapshot used for this read. Missing or deleted row 
IDs
++/// may be omitted from the result by the upstream Lance implementation.
++///
++/// Returns 0 on success, -1 on error.
++#[unsafe(no_mangle)]
++pub unsafe extern "C" fn lance_dataset_take_rows(
++    dataset: *const LanceDataset,
++    row_ids: *const u64,
++    num_row_ids: usize,
++    columns: *const *const c_char,
++    out: *mut FFI_ArrowArrayStream,
++) -> i32 {
++    ffi_try!(
++        unsafe { dataset_take_rows_inner(dataset, row_ids, num_row_ids, 
columns, out) },
++        neg
++    )
++}
++
++unsafe fn dataset_take_rows_inner(
++    dataset: *const LanceDataset,
++    row_ids: *const u64,
++    num_row_ids: usize,
++    columns: *const *const c_char,
++    out: *mut FFI_ArrowArrayStream,
++) -> Result<i32> {
++    if dataset.is_null() {
++        return Err(lance_core::Error::invalid_input("dataset must not be 
NULL"));
++    }
++    if out.is_null() {
++        return Err(lance_core::Error::invalid_input("out must not be NULL"));
++    }
++    if num_row_ids > 0 && row_ids.is_null() {
++        return Err(lance_core::Error::invalid_input(format!(
++            "row_ids must not be NULL when num_row_ids = {num_row_ids}"
++        )));
++    }
++
++    let ds = unsafe { &*dataset };
++    let row_id_slice = if num_row_ids == 0 {
++        &[]
++    } else {
++        unsafe { std::slice::from_raw_parts(row_ids, num_row_ids) }
++    };
++    let col_names = unsafe { helpers::parse_c_string_array(columns)? };
++
++    let snap = ds.snapshot();
++    let projection = projection_from_columns(&snap, col_names.as_deref())?;
++
++    let batch = block_on(snap.take_rows(row_id_slice, projection))?;
++
++    // Match lance_dataset_take: export the single RecordBatch as an Arrow 
stream.
++    let schema = batch.schema();
++    let reader = 
arrow::record_batch::RecordBatchIterator::new(vec![Ok(batch)], schema);
++    let ffi_stream = FFI_ArrowArrayStream::new(Box::new(reader));
++    unsafe {
++        std::ptr::write_unaligned(out, ffi_stream);
++    }
++    Ok(0)
++}
++
+diff --git a/src/scanner.rs b/src/scanner.rs
+index c1c2f67..de646be 100644
+--- a/src/scanner.rs
++++ b/src/scanner.rs
+@@ -160,0 +161,6 @@ impl LanceScanner {
++        // Lance validates fragment-scoped nearest searches when nearest() is
++        // configured. Such searches are supported when the fragment scan is
++        // the input to a prefilter, so this flag must be set first.
++        if self.prefilter {
++            scanner.prefilter(true);
++        }
+@@ -178,3 +183,0 @@ impl LanceScanner {
+-            if self.prefilter {
+-                scanner.prefilter(true);
+-            }
+@@ -221,0 +225,5 @@ impl LanceScanner {
++        // nearest() checks the current prefilter setting before accepting a
++        // fragment-scoped search. Enable it before installing the query.
++        if self.prefilter {
++            scanner.prefilter(true);
++        }
+@@ -239,3 +246,0 @@ impl LanceScanner {
+-            if self.prefilter {
+-                scanner.prefilter(true);
+-            }
+diff --git a/tests/c_api_test.rs b/tests/c_api_test.rs
+index 2babda6..9ee5af9 100644
+--- a/tests/c_api_test.rs
++++ b/tests/c_api_test.rs
+@@ -9,0 +10 @@ use std::ffi::{CString, c_char};
++use std::process::Command;
+@@ -18 +19 @@ use arrow::record_batch::RecordBatchReader;
+-use arrow_array::{Array, Float32Array, Int32Array, RecordBatch, StringArray};
++use arrow_array::{Array, Float32Array, Int32Array, RecordBatch, StringArray, 
UInt64Array};
+@@ -393,0 +395,94 @@ fn test_dataset_take() {
++#[test]
++fn test_dataset_take_rows_empty_and_null_validation() {
++    let (_tmp, uri) = create_test_dataset();
++    let c_uri = c_str(&uri);
++    let ds = unsafe { lance_dataset_open(c_uri.as_ptr(), ptr::null(), 0) };
++    assert!(!ds.is_null());
++
++    let mut empty_stream = FFI_ArrowArrayStream::empty();
++    assert_eq!(
++        unsafe { lance_dataset_take_rows(ds, ptr::null(), 0, ptr::null(), 
&mut empty_stream) },
++        0
++    );
++    let reader = unsafe { ArrowArrayStreamReader::from_raw(&mut empty_stream) 
}.unwrap();
++    assert_eq!(
++        reader.map(|batch| batch.unwrap().num_rows()).sum::<usize>(),
++        0
++    );
++
++    let mut invalid_stream = FFI_ArrowArrayStream::empty();
++    assert_eq!(
++        unsafe { lance_dataset_take_rows(ds, ptr::null(), 1, ptr::null(), 
&mut invalid_stream) },
++        -1
++    );
++    let message = unsafe { 
std::ffi::CStr::from_ptr(lance_last_error_message()) }.to_string_lossy();
++    assert!(
++        message.contains("row_ids must not be NULL when num_row_ids = 1"),
++        "unexpected error: {message}"
++    );
++
++    let row_id = 0_u64;
++    assert_eq!(
++        unsafe {
++            lance_dataset_take_rows(ptr::null(), &row_id, 1, ptr::null(), 
&mut invalid_stream)
++        },
++        -1
++    );
++    assert_eq!(
++        unsafe { lance_dataset_take_rows(ds, &row_id, 1, ptr::null(), 
ptr::null_mut()) },
++        -1
++    );
++
++    unsafe { lance_dataset_close(ds) };
++}
++
++#[test]
++fn test_dataset_take_rows_invalid_column() {
++    const CHILD_ENV: &str = "LANCE_C_TEST_TAKE_ROWS_INVALID_COLUMN_CHILD";
++
++    if std::env::var_os(CHILD_ENV).is_some() {
++        let (_tmp, uri) = create_test_dataset();
++        let c_uri = c_str(&uri);
++        let ds = unsafe { lance_dataset_open(c_uri.as_ptr(), ptr::null(), 0) 
};
++        assert!(!ds.is_null());
++
++        let row_id = 0_u64;
++        let invalid_column = c_str("unknown_column");
++        let columns = [invalid_column.as_ptr(), ptr::null()];
++        let mut stream = FFI_ArrowArrayStream::empty();
++        assert_eq!(
++            unsafe { lance_dataset_take_rows(ds, &row_id, 1, 
columns.as_ptr(), &mut stream) },
++            -1
++        );
++        assert_eq!(lance_last_error_code(), LanceErrorCode::InvalidArgument);
++
++        let message_ptr = lance_last_error_message();
++        assert!(!message_ptr.is_null());
++        let message = unsafe { std::ffi::CStr::from_ptr(message_ptr) }
++            .to_string_lossy()
++            .into_owned();
++        unsafe { lance_free_string(message_ptr) };
++        assert!(
++            message.contains("unknown_column"),
++            "unexpected error: {message}"
++        );
++
++        unsafe { lance_dataset_close(ds) };
++        return;
++    }
++
++    let output = Command::new(std::env::current_exe().unwrap())
++        .arg("--exact")
++        .arg("test_dataset_take_rows_invalid_column")
++        .arg("--nocapture")
++        .env(CHILD_ENV, "1")
++        .output()
++        .unwrap();
++    assert!(
++        output.status.success(),
++        "invalid-column subprocess failed\nstdout:\n{}\nstderr:\n{}",
++        String::from_utf8_lossy(&output.stdout),
++        String::from_utf8_lossy(&output.stderr)
++    );
++}
++
+@@ -2497,0 +2593,73 @@ fn create_vector_dataset(num_rows: i32, dim: i32) -> 
(tempfile::TempDir, String)
++/// Create two vector fragments with deterministic vectors. Every component of
++/// row `id` is `id as f32`, making nearest-neighbor expectations unambiguous.
++fn create_multi_fragment_vector_dataset(
++    rows_per_fragment: i32,
++    dim: i32,
++    enable_stable_row_ids: bool,
++) -> (tempfile::TempDir, String) {
++    use arrow_array::builder::{FixedSizeListBuilder, Float32Builder};
++
++    let tmp = tempfile::tempdir().unwrap();
++    let uri = tmp
++        .path()
++        .join("multi_fragment_vec_ds")
++        .to_str()
++        .unwrap()
++        .to_string();
++    let schema = Arc::new(Schema::new(vec![
++        Field::new("id", DataType::Int32, false),
++        Field::new(
++            "embedding",
++            DataType::FixedSizeList(Arc::new(Field::new("item", 
DataType::Float32, true)), dim),
++            false,
++        ),
++    ]));
++
++    let make_batch = |first_id: i32| {
++        let ids: Vec<i32> = (first_id..first_id + 
rows_per_fragment).collect();
++        let mut embeddings = FixedSizeListBuilder::new(Float32Builder::new(), 
dim);
++        for id in &ids {
++            for _ in 0..dim {
++                embeddings.values().append_value(*id as f32);
++            }
++            embeddings.append(true);
++        }
++        RecordBatch::try_new(
++            schema.clone(),
++            vec![
++                Arc::new(Int32Array::from(ids)),
++                Arc::new(embeddings.finish()),
++            ],
++        )
++        .unwrap()
++    };
++
++    let first = make_batch(0);
++    let second = make_batch(rows_per_fragment);
++    lance_c::runtime::block_on(async {
++        Dataset::write(
++            arrow::record_batch::RecordBatchIterator::new(vec![Ok(first)], 
schema.clone()),
++            &uri,
++            Some(lance::dataset::WriteParams {
++                enable_stable_row_ids,
++                ..Default::default()
++            }),
++        )
++        .await
++        .unwrap();
++        Dataset::write(
++            arrow::record_batch::RecordBatchIterator::new(vec![Ok(second)], 
schema),
++            &uri,
++            Some(lance::dataset::WriteParams {
++                mode: lance::dataset::WriteMode::Append,
++                enable_stable_row_ids,
++                ..Default::default()
++            }),
++        )
++        .await
++        .unwrap();
++    });
++
++    (tmp, uri)
++}
++
+@@ -2723,0 +2892,99 @@ fn test_scanner_nearest_brute_force() {
++fn 
assert_dataset_take_rows_from_multi_fragment_ann_result(enable_stable_row_ids: 
bool) {
++    let (_tmp, uri) = create_multi_fragment_vector_dataset(32, 8, 
enable_stable_row_ids);
++    let uri_c = c_str(&uri);
++    let ds = unsafe { lance_dataset_open(uri_c.as_ptr(), ptr::null(), 0) };
++    assert!(!ds.is_null());
++
++    // A non-NULL array whose first element is NULL is an explicit empty
++    // projection. The ANN result should therefore contain only _distance and
++    // the explicitly requested _rowid system column.
++    let no_columns: [*const c_char; 1] = [ptr::null()];
++    let scanner = unsafe { lance_scanner_new(ds, no_columns.as_ptr(), 
ptr::null()) };
++    assert!(!scanner.is_null());
++    assert_eq!(unsafe { lance_scanner_with_row_id(scanner, true) }, 0);
++
++    let column = c_str("embedding");
++    let query = [40.0_f32; 8];
++    assert_eq!(
++        unsafe {
++            lance_scanner_nearest(
++                scanner,
++                column.as_ptr(),
++                query.as_ptr().cast(),
++                query.len(),
++                LanceDataType::Float32 as i32,
++                1,
++            )
++        },
++        0
++    );
++    assert_eq!(unsafe { lance_scanner_set_use_index(scanner, false) }, 0);
++
++    let mut ann_stream = FFI_ArrowArrayStream::empty();
++    assert_eq!(
++        unsafe { lance_scanner_to_arrow_stream(scanner, &mut ann_stream) },
++        0
++    );
++    let reader = unsafe { ArrowArrayStreamReader::from_raw(&mut ann_stream) 
}.unwrap();
++    let ann_batches = reader.map(|batch| batch.unwrap()).collect::<Vec<_>>();
++    assert_eq!(
++        ann_batches.iter().map(RecordBatch::num_rows).sum::<usize>(),
++        1
++    );
++    assert_eq!(ann_batches[0].num_columns(), 2);
++
++    let distance = ann_batches[0]
++        .column_by_name("_distance")
++        .unwrap()
++        .as_any()
++        .downcast_ref::<Float32Array>()
++        .unwrap()
++        .value(0);
++    assert_eq!(distance, 0.0);
++    let row_id = ann_batches[0]
++        .column_by_name("_rowid")
++        .unwrap()
++        .as_any()
++        .downcast_ref::<UInt64Array>()
++        .unwrap()
++        .value(0);
++    if !enable_stable_row_ids {
++        assert_ne!(
++            row_id >> 32,
++            0,
++            "expected an address-style row ID from the second fragment"
++        );
++    }
++
++    let id_column = c_str("id");
++    let columns = [id_column.as_ptr(), ptr::null()];
++    let mut take_stream = FFI_ArrowArrayStream::empty();
++    assert_eq!(
++        unsafe { lance_dataset_take_rows(ds, &row_id, 1, columns.as_ptr(), 
&mut take_stream) },
++        0
++    );
++    let reader = unsafe { ArrowArrayStreamReader::from_raw(&mut take_stream) 
}.unwrap();
++    let batches = reader.map(|batch| batch.unwrap()).collect::<Vec<_>>();
++    assert_eq!(batches.len(), 1);
++    let ids = batches[0]
++        .column_by_name("id")
++        .unwrap()
++        .as_any()
++        .downcast_ref::<Int32Array>()
++        .unwrap();
++    assert_eq!(ids.values(), &[40]);
++
++    unsafe { lance_scanner_close(scanner) };
++    unsafe { lance_dataset_close(ds) };
++}
++
++#[test]
++fn test_dataset_take_rows_from_multi_fragment_ann_result() {
++    assert_dataset_take_rows_from_multi_fragment_ann_result(false);
++}
++
++#[test]
++fn 
test_dataset_take_rows_from_multi_fragment_ann_result_with_stable_row_ids() {
++    assert_dataset_take_rows_from_multi_fragment_ann_result(true);
++}
++
+@@ -2855,0 +3123,132 @@ fn test_scanner_nearest_filter_postfilter() {
++#[test]
++fn test_scanner_nearest_prefilter_with_fragment_ids_next() {
++    let (_tmp, uri) = create_multi_fragment_vector_dataset(32, 8, false);
++    let uri_c = c_str(&uri);
++    let ds = unsafe { lance_dataset_open(uri_c.as_ptr(), ptr::null(), 0) };
++    assert!(!ds.is_null());
++
++    let mut fragment_ids = vec![0; unsafe { lance_dataset_fragment_count(ds) 
} as usize];
++    assert_eq!(fragment_ids.len(), 2);
++    assert_eq!(
++        unsafe { lance_dataset_fragment_ids(ds, fragment_ids.as_mut_ptr()) },
++        0
++    );
++
++    let filter = c_str("id >= 40");
++    let scanner = unsafe { lance_scanner_new(ds, ptr::null(), 
filter.as_ptr()) };
++    assert_eq!(
++        unsafe { lance_scanner_set_fragment_ids(scanner, 
fragment_ids[1..].as_ptr(), 1) },
++        0
++    );
++
++    // Match the Doris call order: nearest is configured before prefilter.
++    let column = c_str("embedding");
++    let query = [40.0_f32; 8];
++    assert_eq!(
++        unsafe {
++            lance_scanner_nearest(
++                scanner,
++                column.as_ptr(),
++                query.as_ptr().cast(),
++                query.len(),
++                LanceDataType::Float32 as i32,
++                5,
++            )
++        },
++        0
++    );
++    assert_eq!(unsafe { lance_scanner_set_prefilter(scanner, true) }, 0);
++    assert_eq!(unsafe { lance_scanner_set_use_index(scanner, false) }, 0);
++
++    let batches = scan_all_rows_from_scanner(scanner);
++    let mut ids = batches
++        .iter()
++        .flat_map(|batch| {
++            batch
++                .column_by_name("id")
++                .unwrap()
++                .as_any()
++                .downcast_ref::<Int32Array>()
++                .unwrap()
++                .values()
++                .iter()
++                .copied()
++                .collect::<Vec<_>>()
++        })
++        .collect::<Vec<_>>();
++    ids.sort_unstable();
++    assert_eq!(ids, vec![40, 41, 42, 43, 44]);
++
++    unsafe { lance_scanner_close(scanner) };
++    unsafe { lance_dataset_close(ds) };
++}
++
++#[test]
++fn test_scanner_nearest_prefilter_with_fragment_ids_arrow_stream() {
++    let (_tmp, uri) = create_multi_fragment_vector_dataset(32, 8, false);
++    let uri_c = c_str(&uri);
++    let ds = unsafe { lance_dataset_open(uri_c.as_ptr(), ptr::null(), 0) };
++    assert!(!ds.is_null());
++
++    let mut fragment_ids = vec![0; unsafe { lance_dataset_fragment_count(ds) 
} as usize];
++    assert_eq!(fragment_ids.len(), 2);
++    assert_eq!(
++        unsafe { lance_dataset_fragment_ids(ds, fragment_ids.as_mut_ptr()) },
++        0
++    );
++
++    let filter = c_str("id >= 60");
++    let scanner = unsafe { lance_scanner_new(ds, ptr::null(), 
filter.as_ptr()) };
++    assert_eq!(
++        unsafe { lance_scanner_set_fragment_ids(scanner, 
fragment_ids[1..].as_ptr(), 1) },
++        0
++    );
++
++    let column = c_str("embedding");
++    let query = [60.0_f32; 8];
++    assert_eq!(
++        unsafe {
++            lance_scanner_nearest(
++                scanner,
++                column.as_ptr(),
++                query.as_ptr().cast(),
++                query.len(),
++                LanceDataType::Float32 as i32,
++                10,
++            )
++        },
++        0
++    );
++    assert_eq!(unsafe { lance_scanner_set_prefilter(scanner, true) }, 0);
++    assert_eq!(unsafe { lance_scanner_set_use_index(scanner, false) }, 0);
++
++    let mut stream = FFI_ArrowArrayStream::empty();
++    assert_eq!(
++        unsafe { lance_scanner_to_arrow_stream(scanner, &mut stream) },
++        0,
++        "{}",
++        unsafe { std::ffi::CStr::from_ptr(lance_last_error_message()) 
}.to_string_lossy()
++    );
++    let reader = unsafe { ArrowArrayStreamReader::from_raw(&mut stream) 
}.unwrap();
++    let mut ids = reader
++        .flat_map(|batch| {
++            let batch = batch.unwrap();
++            batch
++                .column_by_name("id")
++                .unwrap()
++                .as_any()
++                .downcast_ref::<Int32Array>()
++                .unwrap()
++                .values()
++                .iter()
++                .copied()
++                .collect::<Vec<_>>()
++        })
++        .collect::<Vec<_>>();
++    ids.sort_unstable();
++    assert_eq!(ids, vec![60, 61, 62, 63]);
++
++    unsafe { lance_scanner_close(scanner) };
++    unsafe { lance_dataset_close(ds) };
++}
++
+diff --git a/tests/cpp/test_cpp_api.cpp b/tests/cpp/test_cpp_api.cpp
+index 43491f2..444fbdc 100644
+--- a/tests/cpp/test_cpp_api.cpp
++++ b/tests/cpp/test_cpp_api.cpp
+@@ -124,0 +125,29 @@ static void test_dataset_take(const std::string& uri) {
++static void test_dataset_take_rows(const std::string& uri) {
++    TEST(test_dataset_take_rows);
++
++    auto ds = lance::Dataset::open(uri);
++
++    // The smoke fixture has one fragment, so its first row IDs are 0, 1, 2.
++    uint64_t row_ids[] = {0, 1, 2};
++    ArrowArrayStream stream;
++    memset(&stream, 0, sizeof(stream));
++    ds.take_rows(row_ids, 3, &stream);
++
++    uint64_t total = 0;
++    while (true) {
++        ArrowArray arr;
++        memset(&arr, 0, sizeof(arr));
++        int rc = stream.get_next(&stream, &arr);
++        assert(rc == 0);
++        if (!arr.release) break;
++        total += (uint64_t)arr.length;
++        arr.release(&arr);
++    }
++
++    assert(total == 3);
++    printf("rows=%llu... ", (unsigned long long)total);
++
++    if (stream.release) stream.release(&stream);
++    PASS();
++}
++
+@@ -695,0 +725 @@ int main(int argc, char** argv) {
++    test_dataset_take_rows(uri);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to