diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 789c4a45..1cd15b99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,18 @@ jobs: otp-version: "26.2" elixir-version: "1.15.8" + - name: Install FlatBuffers 2.0.8 from source + run: | + sudo apt-get update && sudo apt install -y build-essential cmake + cd /tmp + wget https://github.com/google/flatbuffers/archive/v2.0.8.tar.gz + tar -xzf v2.0.8.tar.gz + cd flatbuffers-2.0.8 + cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local + make -j$(nproc) + sudo make install + sudo ldconfig + - name: Compile and Test run: | mix deps.get @@ -70,7 +82,7 @@ jobs: macos: if: contains(github.event.pull_request.labels.*.name, 'skip ci') != true - runs-on: macos-13 + runs-on: macos-14 env: MIX_ENV: test OTP_VERSION: "26.2" @@ -80,6 +92,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Install Flatbuffers using Homebrew + run: brew install gcc flatbuffers + - name: Install OTP and Elixir run: | curl -fsSO https://elixir-lang.org/install.sh diff --git a/.gitignore b/.gitignore index 8fbb164a..620ad28c 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,14 @@ adbc-*.tar # checksum checksum.exs + +# Make temporary + +/cmake_adbc/ +/cmake_adbc_nif/ +**/CMakeFiles/* +cmake_install.cmake +CTestTestfile.cmake + +# Build artifacts +/priv/ diff --git a/3rd_party/apache-arrow-adbc/c/CMakeLists.txt b/3rd_party/apache-arrow-adbc/c/CMakeLists.txt index bc6a8305..a489891f 100644 --- a/3rd_party/apache-arrow-adbc/c/CMakeLists.txt +++ b/3rd_party/apache-arrow-adbc/c/CMakeLists.txt @@ -101,6 +101,12 @@ if(ADBC_DRIVER_BIGQUERY) add_subdirectory(driver/bigquery) endif() +if(ADBC_DRIVER_CUBE) + install(FILES "${REPOSITORY_ROOT}/c/include/arrow-adbc/driver/cube.h" + DESTINATION include/arrow-adbc/driver) + add_subdirectory(driver/cube) +endif() + if(ADBC_INTEGRATION_DUCKDB) add_subdirectory(integration/duckdb) endif() @@ -155,6 +161,10 @@ LIBRARY=$" ${Python3_EXECUTABLE} -m pi if(ADBC_DRIVER_BIGQUERY) adbc_install_python_package(bigquery) endif() + + if(ADBC_DRIVER_CUBE) + adbc_install_python_package(cube) + endif() endif() validate_config() diff --git a/3rd_party/apache-arrow-adbc/c/driver/postgresql/copy/statement.h b/3rd_party/apache-arrow-adbc/c/driver/postgresql/copy/statement.h new file mode 100644 index 00000000..a2c3f5e8 --- /dev/null +++ b/3rd_party/apache-arrow-adbc/c/driver/postgresql/copy/statement.h @@ -0,0 +1,181 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include "copy/reader.h" +#include "driver/common/utils.h" +#include "postgres_type.h" + +#define ADBC_POSTGRESQL_OPTION_BATCH_SIZE_HINT_BYTES \ + "adbc.postgresql.batch_size_hint_bytes" + +#define ADBC_POSTGRESQL_OPTION_USE_COPY "adbc.postgresql.use_copy" + +namespace adbcpq { +class PostgresConnection; +class PostgresStatement; + +constexpr static int64_t kDefaultBatchSizeHintBytes = 16777216; + +/// \brief An ArrowArrayStream that reads tuples from a PGresult. +class TupleReader final : public std::enable_shared_from_this { + public: + TupleReader(PGconn* conn) + : status_(ADBC_STATUS_OK), + error_(ADBC_ERROR_INIT), + conn_(conn), + result_(nullptr), + pgbuf_(nullptr), + copy_reader_(nullptr), + row_id_(-1), + batch_size_hint_bytes_(kDefaultBatchSizeHintBytes), + is_finished_(false) { + ArrowErrorInit(&na_error_); + data_.data.as_char = nullptr; + data_.size_bytes = 0; + } + + int GetSchema(struct ArrowSchema* out); + int GetNext(struct ArrowArray* out); + const char* last_error() const { return error_.message; } + void Release(); + void ExportTo(struct ArrowArrayStream* stream); + + static const struct AdbcError* ErrorFromArrayStream(struct ArrowArrayStream* stream, + AdbcStatusCode* status); + + private: + friend class PostgresStatement; + + int GetCopyData(); + int AppendRowAndFetchNext(); + int BuildOutput(struct ArrowArray* out); + + static int GetSchemaTrampoline(struct ArrowArrayStream* self, struct ArrowSchema* out); + static int GetNextTrampoline(struct ArrowArrayStream* self, struct ArrowArray* out); + static const char* GetLastErrorTrampoline(struct ArrowArrayStream* self); + static void ReleaseTrampoline(struct ArrowArrayStream* self); + + AdbcStatusCode status_; + struct AdbcError error_; + struct ArrowError na_error_; + PGconn* conn_; + PGresult* result_; + char* pgbuf_; + struct ArrowBufferView data_; + std::unique_ptr copy_reader_; + int64_t row_id_; + int64_t batch_size_hint_bytes_; + bool is_finished_; +}; + +class PostgresStatement { + public: + PostgresStatement() + : connection_(nullptr), + query_(), + prepared_(false), + use_copy_(-1), + reader_(nullptr), + batch_size_hint_bytes_(kDefaultBatchSizeHintBytes) { + std::memset(&bind_, 0, sizeof(bind_)); + } + + // --------------------------------------------------------------------- + // ADBC API implementation + + AdbcStatusCode Bind(struct ArrowArray* values, struct ArrowSchema* schema, + struct AdbcError* error); + AdbcStatusCode Bind(struct ArrowArrayStream* stream, struct AdbcError* error); + AdbcStatusCode Cancel(struct AdbcError* error); + AdbcStatusCode ExecuteQuery(struct ArrowArrayStream* stream, int64_t* rows_affected, + struct AdbcError* error); + AdbcStatusCode ExecuteSchema(struct ArrowSchema* schema, struct AdbcError* error); + AdbcStatusCode GetOption(const char* key, char* value, size_t* length, + struct AdbcError* error); + AdbcStatusCode GetOptionBytes(const char* key, uint8_t* value, size_t* length, + struct AdbcError* error); + AdbcStatusCode GetOptionDouble(const char* key, double* value, struct AdbcError* error); + AdbcStatusCode GetOptionInt(const char* key, int64_t* value, struct AdbcError* error); + AdbcStatusCode GetParameterSchema(struct ArrowSchema* schema, struct AdbcError* error); + AdbcStatusCode New(struct AdbcConnection* connection, struct AdbcError* error); + AdbcStatusCode Prepare(struct AdbcError* error); + AdbcStatusCode Release(struct AdbcError* error); + AdbcStatusCode SetOption(const char* key, const char* value, struct AdbcError* error); + AdbcStatusCode SetOptionBytes(const char* key, const uint8_t* value, size_t length, + struct AdbcError* error); + AdbcStatusCode SetOptionDouble(const char* key, double value, struct AdbcError* error); + AdbcStatusCode SetOptionInt(const char* key, int64_t value, struct AdbcError* error); + AdbcStatusCode SetSqlQuery(const char* query, struct AdbcError* error); + + // --------------------------------------------------------------------- + // Helper methods + + void ClearResult(); + AdbcStatusCode CreateBulkTable(const std::string& current_schema, + const struct ArrowSchema& source_schema, + std::string* escaped_table, + std::string* escaped_field_list, + struct AdbcError* error); + AdbcStatusCode ExecuteIngest(struct ArrowArrayStream* stream, int64_t* rows_affected, + struct AdbcError* error); + AdbcStatusCode ExecuteBind(struct ArrowArrayStream* stream, int64_t* rows_affected, + struct AdbcError* error); + + private: + std::shared_ptr type_resolver_; + std::shared_ptr connection_; + + // Query state + std::string query_; + bool prepared_; + struct ArrowArrayStream bind_; + + // Bulk ingest state + enum class IngestMode { + kCreate, + kAppend, + kReplace, + kCreateAppend, + }; + + // Options + int use_copy_; + + struct { + std::string db_schema; + std::string target; + IngestMode mode = IngestMode::kCreate; + bool temporary = false; + } ingest_; + + std::shared_ptr reader_; + int64_t batch_size_hint_bytes_; + + int UseCopy(); +}; +} // namespace adbcpq diff --git a/CMakeLists.txt b/CMakeLists.txt index a7c434bd..0ee7876e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,19 @@ if(NOT DEFINED PRIV_DIR) endif() message(STATUS "Using PRIV_DIR: ${PRIV_DIR}") +if(UNIX AND NOT APPLE) + # Find the FlatBuffers package + include_directories(/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/cmake/flatbuffers) + find_package(FlatBuffers CONFIG REQUIRED) + # Print the directory where the config file was found + if(FlatBuffers_FOUND) + message(STATUS "FlatBuffers found in directory: ${FlatBuffers_DIR}") + message(STATUS "FlatBuffers config file path: ${FlatBuffers_CONFIG}") + else() + message(FATAL_ERROR "Could not find FlatBuffers package") + endif() +endif() + if(DEFINED ERTS_INCLUDE_DIR AND NOT "${ERTS_INCLUDE_DIR}" STREQUAL "") set(ERTS_INCLUDE_DIR "${ERTS_INCLUDE_DIR}") else() @@ -35,7 +48,11 @@ if(DEFINED ENV{TARGET_GCC_FLAGS}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} $ENV{TARGET_GCC_FLAGS}") endif() -message(STATUS "CMAKE_TOOLCHAIN_FILE: ${CMAKE_TOOLCHAIN_FILE}") +if(DEFINED CMAKE_TOOLCHAIN_FILE AND NOT "${CMAKE_TOOLCHAIN_FILE}" STREQUAL "") + message(STATUS "CMAKE_TOOLCHAIN_FILE: ${CMAKE_TOOLCHAIN_FILE}") +else() + message(STATUS "CMAKE_TOOLCHAIN_FILE: not set") +endif() find_package(AdbcDriverManager REQUIRED PATHS "${PRIV_DIR}/" NO_DEFAULT_PATH) include_directories("${PRIV_DIR}/include") diff --git a/Makefile b/Makefile index 50cfc663..1d5b6d97 100644 --- a/Makefile +++ b/Makefile @@ -34,11 +34,12 @@ ifdef CMAKE_TOOLCHAIN_FILE endif CMAKE_BUILD_TYPE ?= Release -DEFAULT_JOBS ?= 1 -CMAKE_ADBC_BUILD_DIR = $(MIX_APP_PATH)/cmake_adbc -CMAKE_ADBC_OPTIONS ?= "" -CMAKE_ADBC_NIF_BUILD_DIR = $(MIX_APP_PATH)/cmake_adbc_nif -CMAKE_ADBC_NIF_OPTIONS ?= "" +DEFAULT_JOBS ?= 22 +BUILD_DIR = $(MIX_APP_PATH)/_build +CMAKE_ADBC_BUILD_DIR = $(BUILD_DIR)/cmake/adbc +CMAKE_ADBC_OPTIONS ?= +CMAKE_ADBC_NIF_BUILD_DIR = $(BUILD_DIR)/cmake/nif +CMAKE_ADBC_NIF_OPTIONS ?= MAKE_BUILD_FLAGS ?= -j$(DEFAULT_JOBS) .DEFAULT_GLOBAL := build @@ -51,8 +52,7 @@ build: $(NIF_SO_REL) clean: @rm -rf "$(NIF_SO_REL)" @rm -rf "$(NIF_SO)" - @rm -rf "$(CMAKE_ADBC_NIF_BUILD_DIR)" - @rm -rf "$(CMAKE_ADBC_BUILD_DIR)" + @rm -rf "$(BUILD_DIR)" @rm -rf "$(PRIV_DIR)" priv_dir: @@ -70,6 +70,7 @@ adbc: priv_dir cmake --no-warn-unused-cli \ -DADBC_BUILD_SHARED="ON" \ -DADBC_DRIVER_MANAGER="ON" \ + -DADBC_DRIVER_CUBE="OFF" \ -DADBC_DRIVER_POSTGRESQL="OFF" \ -DADBC_DRIVER_SQLITE="OFF" \ -DADBC_DRIVER_FLIGHTSQL="OFF" \ @@ -89,12 +90,12 @@ $(NIF_SO_REL): priv_dir adbc $(C_SRC_REL)/adbc_nif_resource.hpp $(C_SRC_REL)/adb @ mkdir -p "$(CMAKE_ADBC_NIF_BUILD_DIR)" && \ cd "$(CMAKE_ADBC_NIF_BUILD_DIR)" && \ cmake --no-warn-unused-cli \ - -D CMAKE_BUILD_TYPE="$(CMAKE_BUILD_TYPE)" \ - -D C_SRC="$(C_SRC)" \ - -D ADBC_SRC="$(ADBC_SRC)" \ - -D MIX_APP_PATH="$(MIX_APP_PATH)" \ - -D PRIV_DIR="$(PRIV_DIR)" \ - -D ERTS_INCLUDE_DIR="$(ERTS_INCLUDE_DIR)" \ + -DCMAKE_BUILD_TYPE="$(CMAKE_BUILD_TYPE)" \ + -DC_SRC="$(C_SRC)" \ + -DADBC_SRC="$(ADBC_SRC)" \ + -DMIX_APP_PATH="$(MIX_APP_PATH)" \ + -DPRIV_DIR="$(PRIV_DIR)" \ + $(if $(ERTS_INCLUDE_DIR),-DERTS_INCLUDE_DIR="$(ERTS_INCLUDE_DIR)") \ $(CMAKE_CONFIGURE_FLAGS) $(CMAKE_ADBC_NIF_OPTIONS) "$(shell pwd)" && \ make "$(MAKE_BUILD_FLAGS)" && \ cp "$(CMAKE_ADBC_NIF_BUILD_DIR)/adbc_nif.so" "$(NIF_SO)" diff --git a/c_src/adbc_arrow_schema.hpp b/c_src/adbc_arrow_schema.hpp index 4b1afcb3..3b2fe42e 100644 --- a/c_src/adbc_arrow_schema.hpp +++ b/c_src/adbc_arrow_schema.hpp @@ -110,7 +110,7 @@ static int get_map_schema(ErlNifEnv *env, struct ArrowSchema * schema, uint64_t return erlang::nif::error(env, "invalid ArrowSchema (map), its entries n_children != 2"); } - ERL_NIF_TERM key_schema, value_schema; + ERL_NIF_TERM key_schema = kAtomNil, value_schema = kAtomNil; int kv = 0; for (int64_t child_i = 0; child_i < 2; child_i++) { struct ArrowSchema * entry_schema = entries_schema->children[child_i]; @@ -141,7 +141,7 @@ static int get_map_schema(ErlNifEnv *env, struct ArrowSchema * schema, uint64_t } ERL_NIF_TERM map_kv_keys[] = { kAtomKey, kAtomValue }; - ERL_NIF_TERM map_kv_values[] = { key_schema, value_schema }; + ERL_NIF_TERM map_kv_values[] = { key_schema, value_schema }; // Now guaranteed to be initialized // only fail if there are duplicated keys // so we don't need to check the return value enif_make_map_from_arrays(env, map_kv_keys, map_kv_values, 2, &map_kv_schema); diff --git a/c_src/adbc_nif_resource.hpp b/c_src/adbc_nif_resource.hpp index 829678b1..6b58c960 100644 --- a/c_src/adbc_nif_resource.hpp +++ b/c_src/adbc_nif_resource.hpp @@ -61,7 +61,7 @@ template struct NifRes { error = erlang::nif::error(env, "cannot allocate Nif resource\n"); return res; } - memset(&res->val, 0, sizeof(val_type)); + res->val = val_type(); res->private_data = nullptr; return res; } diff --git a/lib/adbc_driver.ex b/lib/adbc_driver.ex index 36c407a4..da9183fc 100644 --- a/lib/adbc_driver.ex +++ b/lib/adbc_driver.ex @@ -6,9 +6,10 @@ defmodule Adbc.Driver do # == GENERATED CONSTANTS == - # Generated by update.exs at 2025-10-24T12:43:16. Do not change manually. + # Generated by update.exs at 2026-01-09T21:22:30. Do not change manually. @generated_driver_versions %{ duckdb: "1.4.1", + cube: "0.1.2", sqlite: "1.7.0", postgresql: "1.7.0", flightsql: "1.7.0", @@ -40,6 +41,12 @@ defmodule Adbc.Driver do "https://github.com/duckdb/duckdb/releases/download/v1.4.1/libduckdb-windows-amd64.zip" } }, + cube: %{ + "x86_64-linux-gnu" => %{ + url: + "https://github.com/borodark/adbc_driver_cube/releases/download/v0.1.2/adbc_driver_cube-0.1.2-x86_64-linux-gnu.tar.gz" + } + }, sqlite: %{ "aarch64-apple-darwin" => %{ url: @@ -259,7 +266,8 @@ defmodule Adbc.Driver do defp cached_download(url, ignore_proxy, driver_name, version, triplet) do cache_dir = adbc_cache_dir() - cache_path = Path.join(cache_dir, "#{driver_name}-#{triplet}-#{version}.zip") + cache_ext = archive_extension(url) + cache_path = Path.join(cache_dir, "#{driver_name}-#{triplet}-#{version}#{cache_ext}") if File.exists?(cache_path) do {:ok, cache_path} @@ -274,6 +282,18 @@ defmodule Adbc.Driver do end end + defp archive_extension(url) do + url = String.downcase(url) + + cond do + String.ends_with?(url, ".tar.gz") -> ".tar.gz" + String.ends_with?(url, ".tgz") -> ".tgz" + String.ends_with?(url, ".zip") -> ".zip" + String.ends_with?(url, ".whl") -> ".whl" + true -> ".bin" + end + end + defp adbc_cache_dir do if dir = System.get_env("ADBC_CACHE_DIR") do Path.expand(dir) @@ -283,6 +303,24 @@ defmodule Adbc.Driver do end defp extract!(cache_path, driver_name, version, triplet) do + case archive_type(cache_path) do + :zip -> extract_zip!(cache_path, driver_name, version, triplet) + :tar_gz -> extract_tar!(cache_path, driver_name, version, triplet, [:compressed]) + :tar -> extract_tar!(cache_path, driver_name, version, triplet, []) + end + end + + defp archive_type(path) do + path = String.downcase(path) + + cond do + String.ends_with?(path, ".tar.gz") or String.ends_with?(path, ".tgz") -> :tar_gz + String.ends_with?(path, ".tar") -> :tar + true -> :zip + end + end + + defp extract_zip!(cache_path, driver_name, version, triplet) do adbc_so_priv_dir = adbc_so_priv_dir() File.mkdir_p!(adbc_so_priv_dir) @@ -291,7 +329,7 @@ defmodule Adbc.Driver do {:ok, zip_files} = :zip.table(cache_path) for {:zip_file, filename, _, _, _, _} <- zip_files, - Path.extname(filename) in [".so", ".dylib", ".dll"] do + Path.extname(to_string(filename)) in [".so", ".dylib", ".dll"] do {:ok, {_filename, file_data}} = :zip.zip_get(filename, zip_handle) filepath = adbc_driver_so(driver_name, version, triplet) @@ -301,6 +339,22 @@ defmodule Adbc.Driver do :ok = :zip.zip_close(zip_handle) end + defp extract_tar!(cache_path, driver_name, version, triplet, opts) do + adbc_so_priv_dir = adbc_so_priv_dir() + File.mkdir_p!(adbc_so_priv_dir) + + cache_path = String.to_charlist(cache_path) + {:ok, tar_files} = :erl_tar.extract(cache_path, [:memory] ++ opts) + + for {filename, file_data} <- tar_files, + Path.extname(to_string(filename)) in [".so", ".dylib", ".dll"] do + filepath = adbc_driver_so(driver_name, version, triplet) + File.write!(filepath, file_data) + end + + :ok + end + def so_path(driver_name, opts \\ []) def so_path(driver_name, opts) when is_atom(driver_name) do diff --git a/mix.lock b/mix.lock index c7ec331a..239d4eed 100644 --- a/mix.lock +++ b/mix.lock @@ -1,13 +1,18 @@ %{ + "aws_signature": {:hex, :aws_signature, "0.4.2", "1b35482c89ff5b91f5ead647a2bbc0d9620877479b44800915de92bacf9f1476", [:rebar3], [], "hexpm", "1df4a2d1dff200c7bdfa8f9f935efc71a51273adfc6dd39a9f2cc937e01baa01"}, "castore": {:hex, :castore, "1.0.6", "ffc42f110ebfdafab0ea159cd43d31365fa0af0ce4a02ecebf1707ae619ee727", [:mix], [], "hexpm", "374c6e7ca752296be3d6780a6d5b922854ffcc74123da90f2f328996b962d33a"}, "cc_precompiler": {:hex, :cc_precompiler, "0.1.10", "47c9c08d8869cf09b41da36538f62bc1abd3e19e41701c2cea2675b53c704258", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f6e046254e53cd6b41c6bacd70ae728011aa82b2742a80d6e2214855c6e06b22"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, "elixir_make": {:hex, :elixir_make, "0.8.3", "d38d7ee1578d722d89b4d452a3e36bcfdc644c618f0d063b874661876e708683", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.0", [hex: :certifi, repo: "hexpm", optional: true]}], "hexpm", "5c99a18571a756d4af7a4d89ca75c28ac899e6103af6f223982f09ce44942cc9"}, "ex_doc": {:hex, :ex_doc, "0.39.1", "e19d356a1ba1e8f8cfc79ce1c3f83884b6abfcb79329d435d4bbb3e97ccc286e", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "8abf0ed3e3ca87c0847dfc4168ceab5bedfe881692f1b7c45f4a11b232806865"}, + "explorer": {:hex, :explorer, "0.11.1", "f8fe87cdf37c4cca8fe7120fb5806f8327497f61290c18f9d4f33cef6b2fc5a0", [:mix], [{:adbc, "~> 0.1", [hex: :adbc, repo: "hexpm", optional: true]}, {:aws_signature, "~> 0.3", [hex: :aws_signature, repo: "hexpm", optional: false]}, {:decimal, "~> 2.1", [hex: :decimal, repo: "hexpm", optional: false]}, {:flame, "~> 0.3", [hex: :flame, repo: "hexpm", optional: true]}, {:fss, "~> 0.1", [hex: :fss, repo: "hexpm", optional: false]}, {:nx, "~> 0.4", [hex: :nx, repo: "hexpm", optional: true]}, {:rustler, "~> 0.36.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.7", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}, {:table, "~> 0.1.2", [hex: :table, repo: "hexpm", optional: false]}, {:table_rex, "~> 4.1", [hex: :table_rex, repo: "hexpm", optional: false]}], "hexpm", "acc679ea15790d03d9a406bb45284bd4e30531d01a650d9194393cbadcdefccd"}, + "fss": {:hex, :fss, "0.1.1", "9db2344dbbb5d555ce442ac7c2f82dd975b605b50d169314a20f08ed21e08642", [:mix], [], "hexpm", "78ad5955c7919c3764065b21144913df7515d52e228c09427a004afe9c1a16b0"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, + "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.4", "700a878312acfac79fb6c572bb8b57f5aae05fe1cf70d34b5974850bbf2c05bf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "3b33d99b540b15f142ba47944f7a163a25069f6d608783c321029bc1ffb09514"}, "table": {:hex, :table, "0.1.2", "87ad1125f5b70c5dea0307aa633194083eb5182ec537efc94e96af08937e14a8", [:mix], [], "hexpm", "7e99bc7efef806315c7e65640724bf165c3061cdc5d854060f74468367065029"}, + "table_rex": {:hex, :table_rex, "4.1.0", "fbaa8b1ce154c9772012bf445bfb86b587430fb96f3b12022d3f35ee4a68c918", [:mix], [], "hexpm", "95932701df195d43bc2d1c6531178fc8338aa8f38c80f098504d529c43bc2601"}, } diff --git a/test/adbc_postgres_test.exs b/test/adbc_postgres_test.exs index 1917f902..6e675047 100644 --- a/test/adbc_postgres_test.exs +++ b/test/adbc_postgres_test.exs @@ -8,7 +8,11 @@ defmodule Adbc.PostgresTest do setup do db = start_supervised!( - {Adbc.Database, driver: :postgresql, uri: "postgres://postgres:postgres@localhost"} + {Adbc.Database, + driver: :postgresql, + uri: + "postgres://postgres:postgres@localhost:" <> + System.get_env("PG_PORT", "5432")} ) conn = start_supervised!({Connection, database: db}) diff --git a/update.exs b/update.exs index 99b57908..5c285d62 100644 --- a/update.exs +++ b/update.exs @@ -20,15 +20,47 @@ defmodule Update do @adbc_tag "apache-arrow-adbc-19" @adbc_drivers ~w(sqlite postgresql flightsql snowflake bigquery)a + @cube_driver_version System.get_env("CUBE_DRIVER_VERSION") || "0.1.0" + @cube_repo System.get_env("CUBE_DRIVER_REPO") + def versions do Map.new(@adbc_drivers, &{&1, @adbc_driver_version}) |> Map.merge(%{duckdb: @duckdb_version}) + |> Map.merge(%{cube: @cube_driver_version}) end def mappings do %{} |> Map.merge(adbc_mappings(@adbc_driver_version, @adbc_tag)) |> Map.merge(duckdb_mappings(@duckdb_version)) + |> Map.merge(cube_mappings(@cube_driver_version, @cube_repo)) + end + + defp cube_mappings(_version, nil) do + IO.puts("Skipping cube mappings (set CUBE_DRIVER_REPO=org/repo to enable).") + %{cube: %{}} + end + + defp cube_mappings(version, repo) do + assets = fetch_assets!("https://api.github.com/repos/#{repo}/releases/tags/v#{version}") + + IO.puts("Generating cube") + + prefix = "adbc_driver_cube-#{version}" + suffix = ".tar.gz" + + archives = + Enum.filter(assets, fn %{"name" => name} -> + String.starts_with?(name, prefix) and String.ends_with?(name, suffix) + end) + + {x86_64_linux_gnu, archives} = data_for(archives, ["x86_64-linux-gnu"]) + + if archives != [] do + IO.puts("The following archives for cube are not being used:\n\n#{inspect(archives)}") + end + + %{cube: %{"x86_64-linux-gnu" => x86_64_linux_gnu}} end defp duckdb_mappings(duckdb_version) do