diff --git a/.github/workflows/bench-pr.yml b/.github/workflows/bench-pr.yml index 81687f1a91..c3556687f9 100644 --- a/.github/workflows/bench-pr.yml +++ b/.github/workflows/bench-pr.yml @@ -1,5 +1,8 @@ name: PR Benchmarks +env: + DUCKDB_VERSION: v1.2.2 + on: pull_request: types: [ labeled, synchronize ] @@ -22,8 +25,44 @@ jobs: with: labels: benchmark - bench: + build-duckdb: needs: label_trigger + runs-on: + - runs-on=${{ github.run_id }} + - family=c6id.8xlarge + - image=ubuntu24-full-x64 + - spot=false + - tag=${{ matrix.id }} + steps: + - uses: runs-on/action@v1 + - name: Cache duckdb compiled binary + uses: runs-on/cache@v4 + id: cache-duckdb-binary + with: + key: "${{ runner.os }}-duckdb-linux_amd64-${{ env.DUCKDB_VERSION }}" + path: ${{ github.workspace }}/duckdb/build/release/duckdb + + - name: Install duckdb compile requirements + if: steps.cache-duckdb-binary.outputs.cache-hit != 'true' + run: sudo apt-get update && sudo apt-get install ninja-build cmake build-essential make ccache clang -y + + - name: Build duckdb binary + if: steps.cache-duckdb-binary.outputs.cache-hit != 'true' + env: + CC: clang + CXX: clang++ + GEN: ninja + NATIVE_ARCH: 1 + DUCKDB_PLATFORM: linux_amd64 + LTO: thin + run: | + git clone https://github.com/duckdb/duckdb + cd duckdb + git checkout "$DUCKDB_VERSION" + make release + + bench: + needs: build-duckdb runs-on: - runs-on=${{ github.run_id }} - family=c6id.8xlarge @@ -46,15 +85,18 @@ jobs: submodules: "recursive" # rustup is pre-installed on the ubuntu24-full-x64 image. - # The compression benchmarks rely on DuckDB being installed to convert CSV to Parquet - - name: Install DuckDB - uses: opt-nc/setup-duckdb-action@v1.0.12 + - name: Cache duckdb compiled binary + uses: runs-on/cache@v4 + id: cache-duckdb-binary with: - version: v1.2.2 + # This should always be true so fail here, then we can figure out why the cache was empty. + fail-on-cache-miss: true + key: "${{ runner.os }}-duckdb-linux_amd64-${{ env.DUCKDB_VERSION }}" + path: ${{ github.workspace }}/duckdb/build/release/duckdb - # TODO(joe): remove ninja once duckdb is not built by the benchmark - - name: Install gzip - run: sudo apt-get update && sudo apt-get install -y gzip ninja-build + - name: Add duckdb to path + run: | + echo "${{ github.workspace }}/duckdb/build/release/" >> $GITHUB_PATH - name: Build binary shell: bash @@ -73,7 +115,7 @@ jobs: - name: Run ${{ matrix.benchmark.name }} benchmark shell: bash run: | - target/release_debug/${{ matrix.benchmark.id }} -d gh-json | tee ${{ matrix.benchmark.id }}.json + target/release_debug/${{ matrix.benchmark.id }} -d gh-json --skip-rebuild | tee ${{ matrix.benchmark.id }}.json - name: Setup AWS CLI uses: aws-actions/configure-aws-credentials@v4 diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 68a7c94ce9..124f6d13dd 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -31,7 +31,43 @@ jobs: bash scripts/commit-json.sh > new-commit.json bash scripts/cat-s3.sh vortex-benchmark-results-database commits.json new-commit.json + build-duckdb: + runs-on: + - runs-on=${{ github.run_id }} + - family=c6id.8xlarge + - image=ubuntu24-full-x64 + - spot=false + - tag=${{ matrix.id }} + steps: + - uses: runs-on/action@v1 + - name: Cache duckdb compiled binary + uses: runs-on/cache@v4 + id: cache-duckdb-binary + with: + key: "${{ runner.os }}-duckdb-linux_amd64-${{ env.DUCKDB_VERSION }}" + path: ${{ github.workspace }}/duckdb/build/release/duckdb + + - name: Install duckdb compile requirements + if: steps.cache-duckdb-binary.outputs.cache-hit != 'true' + run: sudo apt-get update && sudo apt-get install ninja-build cmake build-essential make ccache clang -y + + - name: Build duckdb binary + if: steps.cache-duckdb-binary.outputs.cache-hit != 'true' + env: + CC: clang + CXX: clang++ + GEN: ninja + NATIVE_ARCH: 1 + DUCKDB_PLATFORM: linux_amd64 + LTO: thin + run: | + git clone https://github.com/duckdb/duckdb + cd duckdb + git checkout "$DUCKDB_VERSION" + make release + bench: + needs: build-duckdb runs-on: - runs-on=${{ github.run_id }} - family=c6id.8xlarge @@ -50,19 +86,25 @@ jobs: - uses: actions/checkout@v4 with: submodules: "recursive" - # rustup is pre-installed on the ubuntu24-full-x64 image. - # The compression benchmarks rely on DuckDB being installed to convert CSV to Parquet - - name: Install DuckDB - uses: opt-nc/setup-duckdb-action@v1.0.12 + - name: Cache duckdb compiled binary + uses: runs-on/cache@v4 + id: cache-duckdb-binary with: - version: v1.2.1 + fail-on-cache-miss: true + key: "${{ runner.os }}-duckdb-linux_amd64-${{ env.DUCKDB_VERSION }}" + path: ${{ github.workspace }}/duckdb/build/release/duckdb + + - name: Add duckdb to path + run: | + echo "${{ github.workspace }}/duckdb/build/release/" >> $GITHUB_PATH # cat-s3 script uses gzip to append to compressed benchmark results # TODO(joe): remove ninja once duckdb is not built by the benchmark - name: Install gzip run: sudo apt-get update && sudo apt-get install -y gzip ninja-build + # rustup is pre-installed on the ubuntu24-full-x64 image. - name: Run ${{ matrix.benchmark.name }} benchmark shell: bash env: diff --git a/.github/workflows/sql-benchmarks.yml b/.github/workflows/sql-benchmarks.yml index 44c3a41fd8..d777e00b10 100644 --- a/.github/workflows/sql-benchmarks.yml +++ b/.github/workflows/sql-benchmarks.yml @@ -1,5 +1,8 @@ name: "SQL-related benchmarks" +env: + DUCKDB_VERSION: v1.2.2 + on: workflow_call: inputs: @@ -8,7 +11,43 @@ on: type: string jobs: + build-duckdb: + runs-on: + - runs-on=${{ github.run_id }} + - family=c6id.8xlarge + - image=ubuntu24-full-x64 + - spot=false + - tag=${{ matrix.id }} + steps: + - uses: runs-on/action@v1 + - name: Cache duckdb compiled binary + uses: runs-on/cache@v4 + id: cache-duckdb-binary + with: + key: "${{ runner.os }}-duckdb-linux_amd64-${{ env.DUCKDB_VERSION }}" + path: ${{ github.workspace }}/duckdb/build/release/duckdb + + - name: Install duckdb compile requirements + if: steps.cache-duckdb-binary.outputs.cache-hit != 'true' + run: sudo apt-get update && sudo apt-get install ninja-build cmake build-essential make ccache clang -y + + - name: Build duckdb binary + if: steps.cache-duckdb-binary.outputs.cache-hit != 'true' + env: + CC: clang + CXX: clang++ + GEN: ninja + NATIVE_ARCH: 1 + DUCKDB_PLATFORM: linux_amd64 + LTO: thin + run: | + git clone https://github.com/duckdb/duckdb + cd duckdb + git checkout "$DUCKDB_VERSION" + make release + bench: + needs: build-duckdb # S3 is shared state here, and we want to make sure only one of each job runs at a time concurrency: group: ${{ github.workflow }}-${{ github.ref_name }}-${{matrix.id}} @@ -33,8 +72,8 @@ jobs: binary_name: tpch name: TPC-H on S3 local_dir: bench-vortex/data/tpch/1 - remote_storage: s3://vortex-bench-dev-eu/${{github.ref_name}}/tpch-sf1/ - targets: "datafusion:parquet,datafusion:vortex" + remote_storage: s3://vortex-bench-dev-eu/${{github.ref_name}}/tpch/1/ + targets: "datafusion:parquet,datafusion:vortex,duckdb:parquet,duckdb:vortex" runs-on: - runs-on=${{ github.run_id }} - family=c6id.8xlarge @@ -55,6 +94,21 @@ jobs: with: submodules: "recursive" + - name: Cache duckdb compiled binary + uses: runs-on/cache@v4 + id: cache-duckdb-binary + with: + fail-on-cache-miss: true + key: "${{ runner.os }}-duckdb-linux_amd64-${{ env.DUCKDB_VERSION }}" + path: ${{ github.workspace }}/duckdb/build/release/duckdb + + - name: Add duckdb to path + run: | + echo "${{ github.workspace }}/duckdb/build/release/" >> $GITHUB_PATH + + - name: Verify duckdb available + run: duckdb --version + - name: Setup AWS CLI uses: aws-actions/configure-aws-credentials@v4 with: @@ -68,17 +122,16 @@ jobs: run: | cargo build --bin ${{ matrix.binary_name }} --package bench-vortex --profile release_debug - - name: Install Ninja - run: sudo apt-get update && sudo apt-get install -y ninja-build - - - name: Install gzip - run: sudo apt-get update && sudo apt-get install -y gzip + - name: Install ninja & gzip + run: sudo apt-get update && sudo apt-get install -y ninja-build gzip - name: DuckDB extension build env: # Build DuckDB and the Vortex extension with `-march=native`. # The `NATIVE_ARCH` environment variable is picked up by `duckdb/Makefile`. NATIVE_ARCH: 1 + DUCKDB_PLATFORM: linux_amd64 + CFLAGS: -ftls-model=global-dynamic run: GEN=ninja make release working-directory: ${{ github.workspace }}/duckdb-vortex @@ -86,9 +139,9 @@ jobs: shell: bash run: | # Generate data, running each query once to make sure they don't panic. - target/release_debug/${{ matrix.binary_name }} --targets datafusion:parquet -i1 -d gh-json - target/release_debug/${{ matrix.binary_name }} --targets datafusion:vortex -i1 -d gh-json - target/release_debug/${{ matrix.binary_name }} --targets duckdb:vortex -i1 -d gh-json + RUST_BACKTRACE=1 target/release_debug/${{ matrix.binary_name }} --targets datafusion:parquet -i1 -d gh-json --skip-rebuild + RUST_BACKTRACE=1 target/release_debug/${{ matrix.binary_name }} --targets datafusion:vortex -i1 -d gh-json --skip-rebuild + RUST_BACKTRACE=1 target/release_debug/${{ matrix.binary_name }} --targets duckdb:vortex -i1 -d gh-json --skip-rebuild - name: Upload data if: matrix.remote_storage != null @@ -120,6 +173,7 @@ jobs: -d gh-json \ --targets ${{ matrix.targets }} \ --export-spans \ + --skip-rebuild \ | tee results.json - name: Run ${{ matrix.name }} benchmark (remote) @@ -137,6 +191,7 @@ jobs: --use-remote-data-dir ${{ matrix.remote_storage }} \ --targets ${{ matrix.targets }} \ --export-spans \ + --skip-rebuild \ -d gh-json \ | tee results.json diff --git a/bench-vortex/src/bin/clickbench.rs b/bench-vortex/src/bin/clickbench.rs index bc1e868961..cfaafd95ac 100644 --- a/bench-vortex/src/bin/clickbench.rs +++ b/bench-vortex/src/bin/clickbench.rs @@ -71,6 +71,9 @@ struct Args { hide_progress_bar: bool, #[arg(long, default_value_t = false)] show_metrics: bool, + // Don't try to rebuild duckdb + #[arg(long)] + skip_rebuild: bool, } struct DataFusionCtx { @@ -211,7 +214,7 @@ fn main() -> anyhow::Result<()> { .then(|| { let path = ddb::get_executable_path(&args.duckdb_path); // If the path is to the duckdb-vortex extension, try to rebuild - if args.duckdb_path.is_none() { + if args.duckdb_path.is_none() && !args.skip_rebuild { ddb::build_vortex_duckdb(); } path diff --git a/bench-vortex/src/bin/tpcds.rs b/bench-vortex/src/bin/tpcds.rs index c3c42e0c33..dd5483768f 100644 --- a/bench-vortex/src/bin/tpcds.rs +++ b/bench-vortex/src/bin/tpcds.rs @@ -177,13 +177,12 @@ async fn bench_main( match engine { // TODO(joe): support datafusion Engine::DuckDB => { - let duckdb_path = duckdb_resolved_path; let temp_dir = tempdir()?; let duckdb_file = temp_dir .path() .join(format!("duckdb-file-{}.db", format.name())); - let executor = DuckDBExecutor::new(duckdb_path.to_owned(), duckdb_file); + let executor = DuckDBExecutor::new(duckdb_resolved_path.to_owned(), duckdb_file); register_tables(&executor, &url, format, BenchmarkDataset::TpcDS)?; for (query_idx, sql_query) in tpch_queries.clone() { diff --git a/bench-vortex/src/engines/ddb/mod.rs b/bench-vortex/src/engines/ddb/mod.rs index a5aa86bb0e..c187badc84 100644 --- a/bench-vortex/src/engines/ddb/mod.rs +++ b/bench-vortex/src/engines/ddb/mod.rs @@ -26,7 +26,7 @@ pub struct DuckDBExecutor { impl DuckDBExecutor { pub fn command(&self) -> Command { let mut command = Command::new(&self.duckdb_path); - command.arg(&self.duckdb_file); + command.arg("-unsigned").arg(&self.duckdb_file); command } @@ -38,21 +38,15 @@ impl DuckDBExecutor { } } -pub fn get_executable_path(user_supplied_path_flag: &Option) -> PathBuf { - let validate_path = |duckdb_path: &PathBuf| { - assert!( - duckdb_path.as_path().exists(), - "failed to find duckdb executable at: {}", - duckdb_path.display() - ); - }; - - // User supplied path takes priority. - if let Some(duckdb_path) = user_supplied_path_flag { - validate_path(duckdb_path); - return duckdb_path.to_owned(); - } +fn validate_path(duckdb_path: &Path) { + assert!( + duckdb_path.exists(), + "failed to find duckdb executable at: {}", + duckdb_path.display() + ); +} +pub fn vortex_duckdb_folder() -> PathBuf { // Try to find the 'vortex' top-level directory. This is preferred over logic along // the lines of `git rev-parse --show-toplevel`, as the repository uses submodules. let mut repo_root = None; @@ -69,38 +63,28 @@ pub fn get_executable_path(user_supplied_path_flag: &Option) -> PathBuf } } - let duckdb_vortex_path = PathBuf::from_str(&repo_root.unwrap_or_else(|| ".".to_string())) + PathBuf::from_str(&repo_root.unwrap_or_else(|| ".".to_string())) .expect("failed to find the vortex repo") - .join("duckdb-vortex"); - - let duckdb_path = duckdb_vortex_path.join("build/release/duckdb"); + .join("duckdb-vortex") +} - validate_path(&duckdb_path); +pub fn vortex_duckdb_extension_path() -> PathBuf { + vortex_duckdb_folder().join("build/release/extension/vortex/vortex.duckdb_extension") +} - duckdb_path +pub fn get_executable_path(user_supplied_path_flag: &Option) -> PathBuf { + // User supplied path takes priority. + if let Some(duckdb_path) = user_supplied_path_flag { + validate_path(duckdb_path); + return duckdb_path.to_owned(); + }; + // Use the binary + PathBuf::from("duckdb") } /// Finds the path to the DuckDB executable pub fn build_vortex_duckdb() { - // Try to find the 'vortex' top-level directory. This is preferred over logic along - // the lines of `git rev-parse --show-toplevel`, as the repository uses submodules. - let mut repo_root = None; - let mut current_dir = std::env::current_dir().expect("failed to get current dir"); - - while current_dir.file_name().is_some() { - if current_dir.file_name().and_then(|name| name.to_str()) == Some("vortex") { - repo_root = Some(current_dir.to_string_lossy().into_owned()); - break; - } - - if !current_dir.pop() { - break; - } - } - - let duckdb_vortex_path = PathBuf::from_str(&repo_root.unwrap_or_else(|| ".".to_string())) - .expect("failed to find the vortex repo") - .join("duckdb-vortex"); + let duckdb_vortex_path = vortex_duckdb_folder(); let mut command = Command::new("make"); command @@ -247,9 +231,7 @@ pub fn register_tables( f => f, }; - // println!("base url {}", base_url); let effective_url = resolve_storage_url(base_url, load_format, dataset); - // let effective_url = base_url; let extension = match load_format { Format::Parquet => "parquet", Format::OnDiskVortex => "vortex", @@ -258,6 +240,26 @@ pub fn register_tables( let mut command = duckdb_executor.command(); + let vortex_path = vortex_duckdb_extension_path(); + command + .arg("-c") + .arg(format!("load \"{}\";", vortex_path.to_string_lossy())); + + command + .arg("-c") + .arg("SET autoinstall_known_extensions=1;") + .arg("-c") + .arg("SET autoload_known_extensions=1;"); + + command.arg("-c").arg( + "CREATE OR REPLACE SECRET secret ( + TYPE s3, + PROVIDER credential_chain, + CHAIN config, + REGION 'eu-west-1' + );", + ); + command.arg("-c").arg(create_table_registration( &effective_url, extension, @@ -267,13 +269,16 @@ pub fn register_tables( trace!("register duckdb tables with command: {:?}", command); + // Don't trace env vars. + command.envs(std::env::vars_os()); let output = command.output()?; // DuckDB does not return non-zero exit codes in case of failures. // Therefore, we need to additionally check whether stderr is set. if !output.status.success() || !output.stderr.is_empty() { anyhow::bail!( - "DuckDB query failed: {}", + "DuckDB query failed: stdout=({})\n, stderr=({})", + String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr) ); }; @@ -288,6 +293,17 @@ pub fn execute_query( ) -> anyhow::Result { let mut command = duckdb_executor.command(); + let vortex_path = vortex_duckdb_extension_path(); + command + .arg("-c") + .arg(format!("load \"{}\";", vortex_path.to_string_lossy())); + + command + .arg("-c") + .arg("SET autoinstall_known_extensions=1;") + .arg("-c") + .arg("SET autoload_known_extensions=1;"); + let query = queries.join(";") + ";"; command .arg("-c") diff --git a/bench-vortex/src/tpch/duckdb.rs b/bench-vortex/src/tpch/duckdb.rs index 8e5544c509..de45afca02 100644 --- a/bench-vortex/src/tpch/duckdb.rs +++ b/bench-vortex/src/tpch/duckdb.rs @@ -6,7 +6,7 @@ use anyhow::Result; use xshell::Shell; use crate::Format; -use crate::ddb::DuckDBExecutor; +use crate::ddb::{DuckDBExecutor, vortex_duckdb_extension_path}; pub enum TpcDataset { TpcH, @@ -98,15 +98,23 @@ pub fn generate_tpc(opts: DuckdbTpcOptions) -> Result { let mut command = Command::new(opts.duckdb_path.unwrap_or_else(|| PathBuf::from("duckdb"))); + let vortex_path = vortex_duckdb_extension_path(); + command + .arg("-unsigned") + .arg("-c") + .arg(format!("load \"{}\";", vortex_path.to_string_lossy())); + + command + .arg("-c") + .arg("SET autoinstall_known_extensions=1;") + .arg("-c") + .arg("SET autoload_known_extensions=1;"); + match opts.dataset { TpcDataset::TpcH => command - .arg("-c") - .arg("load tpch;") .arg("-c") .arg(format!("call dbgen(sf={scale_factor})")), TpcDataset::TpcDs => command - .arg("-c") - .arg("load tpcds;") .arg("-c") .arg(format!("call dsdgen(sf={scale_factor})")), }; @@ -139,12 +147,13 @@ pub fn generate_tpc(opts: DuckdbTpcOptions) -> Result { Format::OnDiskDuckDB | Format::Arrow => { /* Do nothing */ } }; + command.envs(std::env::vars_os()); let output = command.output()?; if !output.status.success() || !output.stderr.is_empty() { let stdout = String::from_utf8_lossy(&output.stdout); let stderr = String::from_utf8_lossy(&output.stderr); - anyhow::bail!("duckdb failed: stdout=\"{stdout}\", stderr=\"{stderr}\""); + anyhow::bail!("duckdb failed, generating tpc*: stdout=\"{stdout}\", stderr=\"{stderr}\""); } // Write a success file to indicate this scale-factor is created. diff --git a/duckdb-vortex/CMakeLists.txt b/duckdb-vortex/CMakeLists.txt index e2fadf433b..dd50e76050 100644 --- a/duckdb-vortex/CMakeLists.txt +++ b/duckdb-vortex/CMakeLists.txt @@ -11,9 +11,9 @@ add_compile_options(-Wno-c++20-designator) include(FetchContent) FetchContent_Declare( - Corrosion - GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git - GIT_TAG v0.5.2 + Corrosion + GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git + GIT_TAG v0.5.2 ) FetchContent_MakeAvailable(Corrosion) @@ -26,10 +26,10 @@ if (APPLE) endif () corrosion_import_crate(MANIFEST_PATH ../Cargo.toml - CRATES vortex-ffi - FEATURES duckdb mimalloc - CRATE_TYPES staticlib - FLAGS --crate-type=staticlib + CRATES vortex-ffi + FEATURES duckdb mimalloc + CRATE_TYPES staticlib + FLAGS --crate-type=staticlib ) set(EXTENSION_NAME ${TARGET_NAME}_extension) @@ -45,19 +45,20 @@ protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS ${PROTO_FILES} PROTOC_OUT_DIR ${PROT include_directories(src/include ${PROTO_GEN_DIR} ../vortex-ffi/cinclude) build_static_extension(${TARGET_NAME} ${EXTENSION_SOURCES} ${PROTO_SRCS}) -build_loadable_extension(${TARGET_NAME} ${EXTENSION_SOURCES} ${PROTO_SRCS}) +set(PARAMETERS "-warnings") +build_loadable_extension(${TARGET_NAME} ${PARAMETERS} ${EXTENSION_SOURCES} ${PROTO_SRCS}) target_link_libraries(${EXTENSION_NAME} - vortex_ffi-static - protobuf::libprotobuf - ${SECURITY_FRAMEWORK} + vortex_ffi-static + protobuf::libprotobuf + ${SECURITY_FRAMEWORK} ) add_subdirectory(test) install( - TARGETS ${EXTENSION_NAME} - EXPORT "${DUCKDB_EXPORT_SET}" - LIBRARY DESTINATION "${INSTALL_LIB_DIR}" - ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" + TARGETS ${EXTENSION_NAME} + EXPORT "${DUCKDB_EXPORT_SET}" + LIBRARY DESTINATION "${INSTALL_LIB_DIR}" + ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" ) diff --git a/duckdb-vortex/Makefile b/duckdb-vortex/Makefile index e24ecdb1b3..13f0b5d500 100644 --- a/duckdb-vortex/Makefile +++ b/duckdb-vortex/Makefile @@ -17,4 +17,7 @@ export VCPKG_FEATURE_FLAGS=-binarycaching export VCPKG_OSX_DEPLOYMENT_TARGET=12.0 export VCPKG_TOOLCHAIN_PATH := ${PROJ_DIR}vcpkg/scripts/buildsystems/vcpkg.cmake +export BUILD_MAIN_DUCKDB_LIBRARY=0 +export DISABLE_BUILTIN_EXTENSIONS=1 + include extension-ci-tools/makefiles/duckdb_extension.Makefile diff --git a/duckdb-vortex/extension_config.cmake b/duckdb-vortex/extension_config.cmake index 8f44c85a90..0a32fd1de9 100644 --- a/duckdb-vortex/extension_config.cmake +++ b/duckdb-vortex/extension_config.cmake @@ -4,8 +4,4 @@ duckdb_extension_load(vortex SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR} LOAD_TESTS -) - -# These are only build and included with the duckdb prebuild executable used locally -duckdb_extension_load(tpch) -duckdb_extension_load(tpcds) +) \ No newline at end of file diff --git a/duckdb-vortex/src/vortex_extension.cpp b/duckdb-vortex/src/vortex_extension.cpp index 4f702b677a..b6d120ad79 100644 --- a/duckdb-vortex/src/vortex_extension.cpp +++ b/duckdb-vortex/src/vortex_extension.cpp @@ -40,11 +40,13 @@ std::string VortexExtension::Version() const { } extern "C" { +__attribute__((__visibility__("default"))) DUCKDB_EXTENSION_API void vortex_init(duckdb::DatabaseInstance &db) { duckdb::DuckDB db_wrapper(db); db_wrapper.LoadExtension(); } +__attribute__((__visibility__("default"))) DUCKDB_EXTENSION_API const char *vortex_version() { return duckdb::DuckDB::LibraryVersion(); }