From 587b8ca0a60264701bdadcf8e194fd72c6f93269 Mon Sep 17 00:00:00 2001 From: Phillip LeBlanc Date: Wed, 26 Mar 2025 12:10:42 +0900 Subject: [PATCH 1/6] Implement support for `duckdb_arrow_scan` for ingesting data via Arrow (#18) * Implement support for `duckdb_arrow_scan` for ingesting data via Arrow * Map the NulError --- crates/duckdb/src/arrow_scan.rs | 233 ++++++++++++++++++++++++++++++++ crates/duckdb/src/error.rs | 2 +- crates/duckdb/src/lib.rs | 1 + 3 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 crates/duckdb/src/arrow_scan.rs diff --git a/crates/duckdb/src/arrow_scan.rs b/crates/duckdb/src/arrow_scan.rs new file mode 100644 index 00000000..0ee97b64 --- /dev/null +++ b/crates/duckdb/src/arrow_scan.rs @@ -0,0 +1,233 @@ +use std::ffi::CString; + +use arrow::ffi_stream::FFI_ArrowArrayStream; + +use crate::{error::error_from_duckdb_code, ffi, Connection, Error, Result}; + +impl Connection { + /// Registers a temporary view in DuckDB based on an Arrow stream. + /// + /// Note the underlying `duckdb_arrow_scan` C API is marked for deprecation. + /// However, similar functionality will be preserved in a new yet-to-be-determined API. + /// + /// # Arguments + /// + /// * `view_name`: The name of the view to register + /// * `arrow_scan`: The Arrow stream to register + pub fn register_arrow_scan_view(&self, view_name: &str, arrow_scan: &FFI_ArrowArrayStream) -> Result<()> { + let conn = self.db.borrow_mut().con; + let c_str = CString::new(view_name).map_err(Error::NulError)?; + let transmuted_arrow_scan = arrow_scan as *const _ as ffi::duckdb_arrow_stream; + let r = unsafe { ffi::duckdb_arrow_scan(conn, c_str.as_ptr(), transmuted_arrow_scan) }; + if r != ffi::DuckDBSuccess { + return error_from_duckdb_code(r, Some("duckdb_arrow_scan failed to register view".to_string())); + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use arrow::{ + array::{Int32Array, StringArray}, + datatypes::{DataType, Field, Schema, SchemaRef}, + error::ArrowError, + record_batch::RecordBatch, + }; + use std::sync::Arc; + + /// A simple RecordBatchReader implementation for testing + struct TestRecordBatchReader { + schema: SchemaRef, + batches: Vec, + index: usize, + } + + impl TestRecordBatchReader { + fn new(batches: Vec) -> Self { + // All batches should have the same schema, so we can use the first one + let schema = batches[0].schema(); + TestRecordBatchReader { + schema, + batches, + index: 0, + } + } + } + + impl Iterator for TestRecordBatchReader { + type Item = std::result::Result; + + fn next(&mut self) -> Option { + if self.index < self.batches.len() { + let batch = self.batches[self.index].clone(); + self.index += 1; + Some(Ok(batch)) + } else { + None + } + } + } + + impl arrow::record_batch::RecordBatchReader for TestRecordBatchReader { + fn schema(&self) -> SchemaRef { + self.schema.clone() + } + } + + #[test] + fn test_register_arrow_scan_view() -> Result<()> { + // Create a test database connection + let db = Connection::open_in_memory()?; + + // Create Arrow arrays for test data + let id_array = Int32Array::from(vec![1, 2, 3, 4, 5]); + let name_array = StringArray::from(vec!["Alice", "Bob", "Charlie", "Dave", "Eve"]); + + // Create a schema and record batch + let schema = Arc::new(Schema::new(vec![ + Field::new("id", DataType::Int32, false), + Field::new("name", DataType::Utf8, false), + ])); + + let record_batch = RecordBatch::try_new(schema, vec![Arc::new(id_array), Arc::new(name_array)]) + .expect("Failed to create record batch"); + + // Create a RecordBatchReader + let reader = TestRecordBatchReader::new(vec![record_batch]); + + // Convert to FFI_ArrowArrayStream - this needs to live longer than any queries to the view + let stream = arrow::ffi_stream::FFI_ArrowArrayStream::new( + Box::new(reader) as Box + ); + + // Register the view + db.register_arrow_scan_view("test_view", &stream)?; + + // Query the view to verify it works + let rows = db + .prepare("SELECT id, name FROM test_view ORDER BY id")? + .query_map([], |row| Ok((row.get::<_, i32>(0)?, row.get::<_, String>(1)?)))? + .collect::>>()?; + + // Verify results + assert_eq!(rows.len(), 5); + assert_eq!(rows[0], (1, "Alice".to_string())); + assert_eq!(rows[1], (2, "Bob".to_string())); + assert_eq!(rows[2], (3, "Charlie".to_string())); + assert_eq!(rows[3], (4, "Dave".to_string())); + assert_eq!(rows[4], (5, "Eve".to_string())); + + Ok(()) + } + + #[test] + fn test_register_arrow_scan_view_with_nulls() -> Result<()> { + // Create a test database connection + let db = Connection::open_in_memory()?; + + // Create Arrow arrays with null values + let id_array = Int32Array::from(vec![Some(1), Some(2), None, Some(4), Some(5)]); + let name_array = StringArray::from(vec![Some("Alice"), None, Some("Charlie"), Some("Dave"), Some("Eve")]); + + // Create a schema and record batch + let schema = Arc::new(Schema::new(vec![ + Field::new("id", DataType::Int32, true), + Field::new("name", DataType::Utf8, true), + ])); + + let record_batch = RecordBatch::try_new(schema, vec![Arc::new(id_array), Arc::new(name_array)]) + .expect("Failed to create record batch"); + + // Create a RecordBatchReader + let reader = TestRecordBatchReader::new(vec![record_batch]); + + // Convert to FFI_ArrowArrayStream + let stream = arrow::ffi_stream::FFI_ArrowArrayStream::new( + Box::new(reader) as Box + ); + + // Register the view + db.register_arrow_scan_view("test_view_nulls", &stream)?; + + // Query the view to verify it works, including handling of nulls + let rows = db + .prepare("SELECT id, name FROM test_view_nulls ORDER BY id NULLS LAST")? + .query_map([], |row| { + let id: Option = row.get(0)?; + let name: Option = row.get(1)?; + Ok((id, name)) + })? + .collect::>>()?; + + // Verify results + assert_eq!(rows.len(), 5); + assert_eq!(rows[0], (Some(1), Some("Alice".to_string()))); + assert_eq!(rows[1], (Some(2), None)); + assert_eq!(rows[2], (Some(4), Some("Dave".to_string()))); + assert_eq!(rows[3], (Some(5), Some("Eve".to_string()))); + assert_eq!(rows[4], (None, Some("Charlie".to_string()))); + + Ok(()) + } + + #[test] + fn test_register_arrow_scan_view_multiple_batches() -> Result<()> { + // Create a test database connection + let db = Connection::open_in_memory()?; + + // Create schema + let schema = Arc::new(Schema::new(vec![ + Field::new("id", DataType::Int32, false), + Field::new("name", DataType::Utf8, false), + ])); + + // Create first batch + let batch1 = RecordBatch::try_new( + schema.clone(), + vec![ + Arc::new(Int32Array::from(vec![1, 2, 3])), + Arc::new(StringArray::from(vec!["Alice", "Bob", "Charlie"])), + ], + ) + .expect("Failed to create record batch"); + + // Create second batch + let batch2 = RecordBatch::try_new( + schema.clone(), + vec![ + Arc::new(Int32Array::from(vec![4, 5])), + Arc::new(StringArray::from(vec!["Dave", "Eve"])), + ], + ) + .expect("Failed to create record batch"); + + // Create a RecordBatchReader with multiple batches + let reader = TestRecordBatchReader::new(vec![batch1, batch2]); + + // Convert to FFI_ArrowArrayStream + let stream = arrow::ffi_stream::FFI_ArrowArrayStream::new( + Box::new(reader) as Box + ); + + // Register the view + db.register_arrow_scan_view("test_view_multi", &stream)?; + + // Query all data to verify correct ordering + let rows = db + .prepare("SELECT id, name FROM test_view_multi ORDER BY id")? + .query_map([], |row| Ok((row.get::<_, i32>(0)?, row.get::<_, String>(1)?)))? + .collect::>>()?; + + // Verify results + assert_eq!(rows.len(), 5); + assert_eq!(rows[0], (1, "Alice".to_string())); + assert_eq!(rows[1], (2, "Bob".to_string())); + assert_eq!(rows[2], (3, "Charlie".to_string())); + assert_eq!(rows[3], (4, "Dave".to_string())); + assert_eq!(rows[4], (5, "Eve".to_string())); + + Ok(()) + } +} diff --git a/crates/duckdb/src/error.rs b/crates/duckdb/src/error.rs index b56e2c35..71c03871 100644 --- a/crates/duckdb/src/error.rs +++ b/crates/duckdb/src/error.rs @@ -219,7 +219,7 @@ impl error::Error for Error { // These are public but not re-exported by lib.rs, so only visible within crate. #[inline] -fn error_from_duckdb_code(code: ffi::duckdb_state, message: Option) -> Result<()> { +pub(crate) fn error_from_duckdb_code(code: ffi::duckdb_state, message: Option) -> Result<()> { Err(Error::DuckDBFailure(ffi::Error::new(code), message)) } diff --git a/crates/duckdb/src/lib.rs b/crates/duckdb/src/lib.rs index b7d796bd..48a0e833 100644 --- a/crates/duckdb/src/lib.rs +++ b/crates/duckdb/src/lib.rs @@ -103,6 +103,7 @@ mod error; mod appender; mod appender_params; mod arrow_batch; +mod arrow_scan; mod cache; mod column; mod config; From 8be45e1d94e1203790943f029cf916ed03921bbd Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Wed, 9 Apr 2025 12:56:53 +0200 Subject: [PATCH 2/6] fix clippy warnings --- crates/duckdb/src/appender_params.rs | 18 +++++++++--------- crates/duckdb/src/params.rs | 18 +++++++++--------- crates/duckdb/src/vtab/arrow.rs | 21 ++++++++------------- 3 files changed, 26 insertions(+), 31 deletions(-) diff --git a/crates/duckdb/src/appender_params.rs b/crates/duckdb/src/appender_params.rs index 223a97ab..94b770d8 100644 --- a/crates/duckdb/src/appender_params.rs +++ b/crates/duckdb/src/appender_params.rs @@ -44,15 +44,15 @@ use sealed::Sealed; /// - a reference to an array of references, as in `thing.query(&["foo", /// "bar", "baz"])` or `thing.query(&[&1i32, &2, &3])`. /// -/// (Note: in this case we don't implement this for slices for coherence -/// reasons, so it really is only for the "reference to array" types — -/// hence why the number of parameters must be <= 32 or you need to -/// reach for `duckdb::params!`) -/// -/// Unfortunately, in the current design it's not possible to allow this for -/// references to arrays of non-references (e.g. `&[1i32, 2, 3]`). Code like -/// this should instead either use `params!`, an array literal, a `&[&dyn -/// ToSql]` or if none of those work, [`ParamsFromIter`]. +/// (Note: in this case we don't implement this for slices for coherence +/// reasons, so it really is only for the "reference to array" types — +/// hence why the number of parameters must be <= 32 or you need to +/// reach for `duckdb::params!`) +/// +/// Unfortunately, in the current design it's not possible to allow this for +/// references to arrays of non-references (e.g. `&[1i32, 2, 3]`). Code like +/// this should instead either use `params!`, an array literal, a `&[&dyn +/// ToSql]` or if none of those work, [`ParamsFromIter`]. /// /// - As a slice of `ToSql` trait object references, e.g. `&[&dyn ToSql]`. This /// is mostly useful for passing parameter lists around as arguments without diff --git a/crates/duckdb/src/params.rs b/crates/duckdb/src/params.rs index c26a9482..8adc4df4 100644 --- a/crates/duckdb/src/params.rs +++ b/crates/duckdb/src/params.rs @@ -44,15 +44,15 @@ use sealed::Sealed; /// - a reference to an array of references, as in `thing.query(&["foo", /// "bar", "baz"])` or `thing.query(&[&1i32, &2, &3])`. /// -/// (Note: in this case we don't implement this for slices for coherence -/// reasons, so it really is only for the "reference to array" types — -/// hence why the number of parameters must be <= 32 or you need to -/// reach for `duckdb::params!`) -/// -/// Unfortunately, in the current design it's not possible to allow this for -/// references to arrays of non-references (e.g. `&[1i32, 2, 3]`). Code like -/// this should instead either use `params!`, an array literal, a `&[&dyn -/// ToSql]` or if none of those work, [`ParamsFromIter`]. +/// (Note: in this case we don't implement this for slices for coherence +/// reasons, so it really is only for the "reference to array" types — +/// hence why the number of parameters must be <= 32 or you need to +/// reach for `duckdb::params!`) +/// +/// Unfortunately, in the current design it's not possible to allow this for +/// references to arrays of non-references (e.g. `&[1i32, 2, 3]`). Code like +/// this should instead either use `params!`, an array literal, a `&[&dyn +/// ToSql]` or if none of those work, [`ParamsFromIter`]. /// /// - As a slice of `ToSql` trait object references, e.g. `&[&dyn ToSql]`. This /// is mostly useful for passing parameter lists around as arguments without diff --git a/crates/duckdb/src/vtab/arrow.rs b/crates/duckdb/src/vtab/arrow.rs index 3fbd86f3..b7d9c6fc 100644 --- a/crates/duckdb/src/vtab/arrow.rs +++ b/crates/duckdb/src/vtab/arrow.rs @@ -1,20 +1,19 @@ use super::{BindInfo, DataChunkHandle, InitInfo, LogicalTypeHandle, TableFunctionInfo, VTab}; -use std::sync::Arc; -use std::sync::{atomic::AtomicBool, Mutex}; +use std::sync::{atomic::AtomicBool, Arc, Mutex}; use crate::{ core::{ArrayVector, FlatVector, Inserter, ListVector, LogicalTypeId, StructVector, Vector}, types::DuckString, }; -use arrow::array::as_map_array; use arrow::{ array::{ - as_boolean_array, as_generic_binary_array, as_large_list_array, as_list_array, as_primitive_array, - as_string_array, as_struct_array, Array, ArrayData, AsArray, BinaryArray, BinaryViewArray, BooleanArray, - Date32Array, Decimal128Array, FixedSizeBinaryArray, FixedSizeListArray, GenericBinaryBuilder, GenericListArray, - GenericStringArray, IntervalMonthDayNanoArray, LargeBinaryArray, LargeStringArray, OffsetSizeTrait, - PrimitiveArray, StringArray, StringViewArray, StructArray, TimestampMicrosecondArray, TimestampNanosecondArray, + as_boolean_array, as_generic_binary_array, as_large_list_array, as_list_array, as_map_array, + as_primitive_array, as_string_array, as_struct_array, Array, ArrayData, AsArray, BinaryArray, BinaryViewArray, + BooleanArray, Date32Array, Decimal128Array, FixedSizeBinaryArray, FixedSizeListArray, GenericBinaryBuilder, + GenericListArray, GenericStringArray, IntervalMonthDayNanoArray, LargeBinaryArray, LargeStringArray, + OffsetSizeTrait, PrimitiveArray, StringArray, StringViewArray, StructArray, TimestampMicrosecondArray, + TimestampNanosecondArray, }, buffer::{BooleanBuffer, NullBuffer}, compute::cast, @@ -1714,11 +1713,7 @@ mod test { let mut stmt = db.prepare("SELECT * FROM arrow(?, ?)").unwrap(); - let res = match stmt.execute(arrow_recordbatch_to_query_params(batch)) { - Ok(..) => None, - Err(e) => Some(e), - } - .unwrap(); + let res = stmt.execute(arrow_recordbatch_to_query_params(batch)).err().unwrap(); assert_eq!( res, From 89138908e9142c6b0680fabbf75f40773b500c2e Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Wed, 9 Apr 2025 13:33:41 +0200 Subject: [PATCH 3/6] try to add symbolizer to path --- .github/workflows/rust.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml index 3e8bfd98..251c138a 100644 --- a/.github/workflows/rust.yaml +++ b/.github/workflows/rust.yaml @@ -142,6 +142,8 @@ jobs: # We cannot run "modern-full" with asan as the chrono feature will auto-load relase binaries of # the ICU-extension, which are not built with ASAN and will cause a crash. run: | + export ASAN_SYMBOLIZER_PATH=$(which llvm-symbolizer) + echo $ASAN_SYMBOLIZER_PATH cargo -Z build-std test --features "serde_json url r2d2 uuid polars extensions-full" --target x86_64-unknown-linux-gnu --package duckdb - name: publish crates --dry-run uses: katyo/publish-crates@v2 From b7610d40271fd3fa95d1e7981477c86503a7fc9f Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Wed, 9 Apr 2025 13:55:01 +0200 Subject: [PATCH 4/6] add llvm --- .github/workflows/rust.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml index 251c138a..4a11c599 100644 --- a/.github/workflows/rust.yaml +++ b/.github/workflows/rust.yaml @@ -128,12 +128,14 @@ jobs: with: toolchain: nightly components: 'rust-src' - + - name: Install LLVM + run: | + sudo apt-get install -y llvm - name: Tests with asan env: RUSTFLAGS: -Zsanitizer=address -C debuginfo=0 RUSTDOCFLAGS: -Zsanitizer=address - ASAN_OPTIONS: "detect_stack_use_after_return=1:detect_leaks=1" + ASAN_OPTIONS: "detect_stack_use_after_return=1:detect_leaks=1:symbolize=1" # Work around https://github.com/rust-lang/rust/issues/59125 by # disabling backtraces. In an ideal world we'd probably suppress the # leak sanitization, but we don't care about backtraces here, so long From 366f47f31f96c9f9cc7936109024ba676fad4b4d Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Wed, 9 Apr 2025 13:55:49 +0200 Subject: [PATCH 5/6] remove other CI while testing --- .github/workflows/rust.yaml | 107 ------------------------------------ 1 file changed, 107 deletions(-) diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml index 4a11c599..a1ad8e16 100644 --- a/.github/workflows/rust.yaml +++ b/.github/workflows/rust.yaml @@ -8,114 +8,7 @@ on: env: RUST_BACKTRACE: 1 jobs: - test: - name: Test ${{ matrix.target }} - strategy: - fail-fast: true - matrix: - include: - - { target: x86_64-pc-windows-msvc, os: windows-latest, duckdb: libduckdb-windows-amd64.zip } - - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, duckdb: libduckdb-linux-amd64.zip } - #- { target: x86_64-apple-darwin, os: macos-latest } - #- { - #target: x86_64-pc-windows-gnu, - #os: windows-latest, - #host: -x86_64-pc-windows-gnu, - #} - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v2 - - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.target }} - components: 'rustfmt, clippy' - - # download libduckdb - - uses: robinraju/release-downloader@v1.4 - name: Download duckdb - with: - repository: "duckdb/duckdb" - tag: "v1.2.1" - fileName: ${{ matrix.duckdb }} - out-file-path: . - - # For Linux - - name: Linux extract duckdb - if: matrix.os == 'ubuntu-latest' - uses: ihiroky/extract-action@v1 - with: - file_path: ${{ github.workspace }}/${{ matrix.duckdb }} - extract_dir: libduckdb - - - run: cargo fmt --all -- --check - if: matrix.os == 'ubuntu-latest' - - - name: run cargo clippy - if: matrix.os == 'ubuntu-latest' - env: - DUCKDB_LIB_DIR: ${{ github.workspace }}/libduckdb - DUCKDB_INCLUDE_DIR: ${{ github.workspace }}/libduckdb - LD_LIBRARY_PATH: ${{ github.workspace }}/libduckdb - run: | - cargo clippy --all-targets --workspace --all-features -- -D warnings -A clippy::redundant-closure - - - # For windows - - name: Windows extract duckdb - if: matrix.os == 'windows-latest' - uses: DuckSoft/extract-7z-action@v1.0 - with: - pathSource: D:\a\duckdb-rs\duckdb-rs\${{ matrix.duckdb }} - pathTarget: ${{ github.workspace }}/libduckdb - - - name: Add path to PATH environment variable - if: matrix.os == 'windows-latest' - uses: myci-actions/export-env-var-powershell@1 - with: - name: PATH - value: $env:PATH;${{ github.workspace }}/libduckdb - - - name: Run cargo-test - if: matrix.os == 'windows-latest' - run: cargo test --features "modern-full vtab-full vtab-loadable" - env: - DUCKDB_LIB_DIR: ${{ github.workspace }}/libduckdb - DUCKDB_INCLUDE_DIR: ${{ github.workspace }}/libduckdb - - - name: Build loadable extension - run: cargo build --example hello-ext --features="vtab-loadable" - env: - DUCKDB_LIB_DIR: ${{ github.workspace }}/libduckdb - DUCKDB_INCLUDE_DIR: ${{ github.workspace }}/libduckdb - LD_LIBRARY_PATH: ${{ github.workspace }}/libduckdb - - - name: Build loadable extension - run: cargo build --example hello-ext-capi --features="vtab-loadable loadable-extension" - env: - DUCKDB_LIB_DIR: ${{ github.workspace }}/libduckdb - DUCKDB_INCLUDE_DIR: ${{ github.workspace }}/libduckdb - LD_LIBRARY_PATH: ${{ github.workspace }}/libduckdb - - Windows: - name: Windows build from source - needs: test - runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v3 - with: - path: ~/.cargo/registry/index - key: index-${{ runner.os }}-${{ github.run_number }} - restore-keys: | - index-${{ runner.os }}- - - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: stable - target: x86_64-pc-windows-msvc - - - run: cargo install cargo-examples Sanitizer: name: Address Sanitizer From 3fa83dc6ebd9330d7c8daa7ba82ec9f174acb501 Mon Sep 17 00:00:00 2001 From: Max Gabrielsson Date: Wed, 9 Apr 2025 13:55:49 +0200 Subject: [PATCH 6/6] remove other CI while testing --- .github/workflows/rust.yaml | 110 ------------------------------------ 1 file changed, 110 deletions(-) diff --git a/.github/workflows/rust.yaml b/.github/workflows/rust.yaml index 4a11c599..339f0c47 100644 --- a/.github/workflows/rust.yaml +++ b/.github/workflows/rust.yaml @@ -8,118 +8,8 @@ on: env: RUST_BACKTRACE: 1 jobs: - test: - name: Test ${{ matrix.target }} - strategy: - fail-fast: true - matrix: - include: - - { target: x86_64-pc-windows-msvc, os: windows-latest, duckdb: libduckdb-windows-amd64.zip } - - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, duckdb: libduckdb-linux-amd64.zip } - #- { target: x86_64-apple-darwin, os: macos-latest } - #- { - #target: x86_64-pc-windows-gnu, - #os: windows-latest, - #host: -x86_64-pc-windows-gnu, - #} - - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v2 - - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.target }} - components: 'rustfmt, clippy' - - # download libduckdb - - uses: robinraju/release-downloader@v1.4 - name: Download duckdb - with: - repository: "duckdb/duckdb" - tag: "v1.2.1" - fileName: ${{ matrix.duckdb }} - out-file-path: . - - # For Linux - - name: Linux extract duckdb - if: matrix.os == 'ubuntu-latest' - uses: ihiroky/extract-action@v1 - with: - file_path: ${{ github.workspace }}/${{ matrix.duckdb }} - extract_dir: libduckdb - - - run: cargo fmt --all -- --check - if: matrix.os == 'ubuntu-latest' - - - name: run cargo clippy - if: matrix.os == 'ubuntu-latest' - env: - DUCKDB_LIB_DIR: ${{ github.workspace }}/libduckdb - DUCKDB_INCLUDE_DIR: ${{ github.workspace }}/libduckdb - LD_LIBRARY_PATH: ${{ github.workspace }}/libduckdb - run: | - cargo clippy --all-targets --workspace --all-features -- -D warnings -A clippy::redundant-closure - - - # For windows - - name: Windows extract duckdb - if: matrix.os == 'windows-latest' - uses: DuckSoft/extract-7z-action@v1.0 - with: - pathSource: D:\a\duckdb-rs\duckdb-rs\${{ matrix.duckdb }} - pathTarget: ${{ github.workspace }}/libduckdb - - - name: Add path to PATH environment variable - if: matrix.os == 'windows-latest' - uses: myci-actions/export-env-var-powershell@1 - with: - name: PATH - value: $env:PATH;${{ github.workspace }}/libduckdb - - - name: Run cargo-test - if: matrix.os == 'windows-latest' - run: cargo test --features "modern-full vtab-full vtab-loadable" - env: - DUCKDB_LIB_DIR: ${{ github.workspace }}/libduckdb - DUCKDB_INCLUDE_DIR: ${{ github.workspace }}/libduckdb - - - name: Build loadable extension - run: cargo build --example hello-ext --features="vtab-loadable" - env: - DUCKDB_LIB_DIR: ${{ github.workspace }}/libduckdb - DUCKDB_INCLUDE_DIR: ${{ github.workspace }}/libduckdb - LD_LIBRARY_PATH: ${{ github.workspace }}/libduckdb - - - name: Build loadable extension - run: cargo build --example hello-ext-capi --features="vtab-loadable loadable-extension" - env: - DUCKDB_LIB_DIR: ${{ github.workspace }}/libduckdb - DUCKDB_INCLUDE_DIR: ${{ github.workspace }}/libduckdb - LD_LIBRARY_PATH: ${{ github.workspace }}/libduckdb - - Windows: - name: Windows build from source - needs: test - runs-on: windows-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v3 - with: - path: ~/.cargo/registry/index - key: index-${{ runner.os }}-${{ github.run_number }} - restore-keys: | - index-${{ runner.os }}- - - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - toolchain: stable - target: x86_64-pc-windows-msvc - - - run: cargo install cargo-examples - Sanitizer: name: Address Sanitizer - needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v2