From 5f62b444713ba18fbf5eed85d029463ba7859da0 Mon Sep 17 00:00:00 2001 From: Mandy Alimaa Date: Fri, 11 Sep 2026 15:09:21 -0500 Subject: [PATCH 1/2] Add Druid quickstarts --- .github/data/databases.json | 4 ++ python/README.md | 1 + python/druid/README.md | 79 +++++++++++++++++++++++++++++++++++++ python/druid/main.py | 38 ++++++++++++++++++ python/druid/start-druid.sh | 50 +++++++++++++++++++++++ 5 files changed, 172 insertions(+) create mode 100644 python/druid/README.md create mode 100644 python/druid/main.py create mode 100644 python/druid/start-druid.sh diff --git a/.github/data/databases.json b/.github/data/databases.json index 9ce76763..93f2359e 100644 --- a/.github/data/databases.json +++ b/.github/data/databases.json @@ -39,6 +39,10 @@ "name": "DataFusion", "parent": null }, + "druid": { + "name": "Apache Druid", + "parent": null + }, "doris": { "name": "Apache Doris", "parent": "flightsql" diff --git a/python/README.md b/python/README.md index 72c8f67f..75f01834 100644 --- a/python/README.md +++ b/python/README.md @@ -32,6 +32,7 @@ Simple Python examples showing how to use ADBC to connect, run a query, and retu - [ClickHouse](./clickhouse) - [Databricks](./databricks) - [DataFusion](./datafusion) +- [Apache Druid](./druid) - [DuckDB-compatible systems](./duckdb) - [DuckDB](./duckdb/duckdb) - [MotherDuck](./duckdb/motherduck) diff --git a/python/druid/README.md b/python/druid/README.md new file mode 100644 index 00000000..a3e4087d --- /dev/null +++ b/python/druid/README.md @@ -0,0 +1,79 @@ + + +# Connecting Python and Apache Druid with ADBC + +## Instructions + +> [!TIP] +> If you already have a Druid instance running, skip the steps to set up Druid. + +### Prerequisites + +1. [Install uv](https://docs.astral.sh/uv/getting-started/installation/) + +1. [Install dbc](https://docs.columnar.tech/dbc/getting_started/installation/) + +### Set up Druid + +1. [Install Docker](https://docs.docker.com/get-started/get-docker/) + +1. Start a Druid 37 nano-quickstart instance: + + ```sh + docker run --detach --rm \ + --name druid \ + --platform linux/amd64 \ + --publish 8888:8888 \ + --volume "$PWD/start-druid.sh:/opt/druid/start-druid.sh:ro" \ + --entrypoint /bin/bash \ + apache/druid:37.0.0 /opt/druid/start-druid.sh + ``` + +1. Wait for Druid to accept SQL queries: + + ```sh + until curl --fail --silent --output /dev/null \ + --header 'Content-Type: application/json' \ + --data '{"query":"SELECT 1"}' \ + http://localhost:8888/druid/v2/sql; do sleep 2; done + ``` + +### Connect to Druid + +1. Install the Druid ADBC driver: + + ```sh + dbc install --pre druid + ``` + +1. Customize the Python script `main.py` as needed + - Change the connection arguments in `db_kwargs` + - Format `uri` according to the [driver documentation](https://docs.adbc-drivers.org/drivers/druid/index.html#connecting), or keep it as is + +1. Run the Python script: + + ```sh + uv run main.py + ``` + +### Clean up + +Stop the Docker container running Druid: + +```sh +docker stop druid +``` diff --git a/python/druid/main.py b/python/druid/main.py new file mode 100644 index 00000000..35ddee16 --- /dev/null +++ b/python/druid/main.py @@ -0,0 +1,38 @@ +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +# /// script +# requires-python = ">=3.10" +# dependencies = ["adbc-driver-manager>=1.9.0", "pyarrow>=20.0.0"] +# /// + +from adbc_driver_manager import dbapi + +with ( + dbapi.connect( + driver="druid", + db_kwargs={"uri": "druid://localhost:8888?tls=false"}, + autocommit=True, + ) as connection, + connection.cursor() as cursor, +): + cursor.execute(""" + SELECT "server", server_type, tier, curr_size, max_size + FROM sys.servers + ORDER BY server_type, "server" + LIMIT 10 + """) + table = cursor.fetch_arrow_table() + +print(table) diff --git a/python/druid/start-druid.sh b/python/druid/start-druid.sh new file mode 100644 index 00000000..576fd7c8 --- /dev/null +++ b/python/druid/start-druid.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +set -euo pipefail + +readonly config="conf/druid/single-server/nano-quickstart" +pids=() + +start() { + "$@" & + pids+=("$!") +} + +shutdown() { + trap - EXIT INT TERM + kill -TERM "${pids[@]}" 2>/dev/null || true + wait "${pids[@]}" 2>/dev/null || true +} + +trap shutdown EXIT INT TERM + +start bin/run-zk conf +start bin/run-druid coordinator-overlord "$config" +start bin/run-druid broker "$config" +start bin/run-druid router "$config" +start bin/run-druid historical "$config" +start bin/run-druid middleManager "$config" + +set +e +wait -n "${pids[@]}" +status=$? +set -e + +# A service exiting normally still means the container is no longer healthy. +if [[ $status -eq 0 ]]; then + status=1 +fi +exit "$status" From 6674458b69f1a0d21d5117c968dec6857feccd91 Mon Sep 17 00:00:00 2001 From: Mandy Alimaa Date: Tue, 15 Sep 2026 11:32:54 -0500 Subject: [PATCH 2/2] add other languages --- README.md | 1 + cpp/README.md | 1 + cpp/druid/CMakeLists.txt | 43 ++++++ cpp/druid/Makefile | 24 ++++ cpp/druid/README.md | 101 +++++++++++++ cpp/druid/main.cpp | 113 +++++++++++++++ cpp/druid/pixi.toml | 12 ++ cpp/druid/start-druid.sh | 50 +++++++ csharp/README.md | 1 + csharp/druid/BatchPrinter.cs | 52 +++++++ csharp/druid/Program.cs | 48 +++++++ csharp/druid/README.md | 79 ++++++++++ csharp/druid/druid.csproj | 29 ++++ csharp/druid/start-druid.sh | 50 +++++++ go/README.md | 1 + go/druid/README.md | 80 +++++++++++ go/druid/main.go | 74 ++++++++++ go/druid/start-druid.sh | 50 +++++++ java/README.md | 1 + java/druid/README.md | 79 ++++++++++ java/druid/pom.xml | 136 ++++++++++++++++++ .../src/main/java/tech/columnar/Example.java | 58 ++++++++ java/druid/start-druid.sh | 50 +++++++ javascript/README.md | 1 + javascript/druid/README.md | 100 +++++++++++++ javascript/druid/main.js | 37 +++++ javascript/druid/start-druid.sh | 50 +++++++ kotlin/README.md | 1 + kotlin/druid/README.md | 79 ++++++++++ kotlin/druid/build.gradle.kts | 31 ++++ kotlin/druid/gradle.properties | 1 + kotlin/druid/settings.gradle.kts | 1 + kotlin/druid/src/main/kotlin/Main.kt | 50 +++++++ kotlin/druid/start-druid.sh | 50 +++++++ r/README.md | 1 + r/druid/README.md | 85 +++++++++++ r/druid/main.R | 37 +++++ r/druid/start-druid.sh | 50 +++++++ ruby/README.md | 1 + ruby/druid/Gemfile | 17 +++ ruby/druid/README.md | 131 +++++++++++++++++ ruby/druid/main.rb | 36 +++++ ruby/druid/start-druid.sh | 50 +++++++ rust/Cargo.toml | 1 + rust/README.md | 1 + rust/druid/Cargo.toml | 24 ++++ rust/druid/README.md | 79 ++++++++++ rust/druid/src/main.rs | 56 ++++++++ rust/druid/start-druid.sh | 50 +++++++ 49 files changed, 2153 insertions(+) create mode 100644 cpp/druid/CMakeLists.txt create mode 100644 cpp/druid/Makefile create mode 100644 cpp/druid/README.md create mode 100644 cpp/druid/main.cpp create mode 100644 cpp/druid/pixi.toml create mode 100644 cpp/druid/start-druid.sh create mode 100644 csharp/druid/BatchPrinter.cs create mode 100644 csharp/druid/Program.cs create mode 100644 csharp/druid/README.md create mode 100644 csharp/druid/druid.csproj create mode 100644 csharp/druid/start-druid.sh create mode 100644 go/druid/README.md create mode 100644 go/druid/main.go create mode 100644 go/druid/start-druid.sh create mode 100644 java/druid/README.md create mode 100644 java/druid/pom.xml create mode 100644 java/druid/src/main/java/tech/columnar/Example.java create mode 100644 java/druid/start-druid.sh create mode 100644 javascript/druid/README.md create mode 100644 javascript/druid/main.js create mode 100644 javascript/druid/start-druid.sh create mode 100644 kotlin/druid/README.md create mode 100644 kotlin/druid/build.gradle.kts create mode 100644 kotlin/druid/gradle.properties create mode 100644 kotlin/druid/settings.gradle.kts create mode 100644 kotlin/druid/src/main/kotlin/Main.kt create mode 100644 kotlin/druid/start-druid.sh create mode 100644 r/druid/README.md create mode 100644 r/druid/main.R create mode 100644 r/druid/start-druid.sh create mode 100644 ruby/druid/Gemfile create mode 100644 ruby/druid/README.md create mode 100644 ruby/druid/main.rb create mode 100644 ruby/druid/start-druid.sh create mode 100644 rust/druid/Cargo.toml create mode 100644 rust/druid/README.md create mode 100644 rust/druid/src/main.rs create mode 100644 rust/druid/start-druid.sh diff --git a/README.md b/README.md index 8b4c0d94..cf1d457d 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ Simple examples showing how to use ADBC to connect, run a query, and return the - [ClickHouse](https://github.com/columnar-tech/adbc-quickstarts/tree/by-database/clickhouse) - [Databricks](https://github.com/columnar-tech/adbc-quickstarts/tree/by-database/databricks) - [DataFusion](https://github.com/columnar-tech/adbc-quickstarts/tree/by-database/datafusion) +- [Apache Druid](https://github.com/columnar-tech/adbc-quickstarts/tree/by-database/druid) - DuckDB-compatible systems - [DuckDB](https://github.com/columnar-tech/adbc-quickstarts/tree/by-database/duckdb) - [MotherDuck](https://github.com/columnar-tech/adbc-quickstarts/tree/by-database/motherduck) diff --git a/cpp/README.md b/cpp/README.md index 5397c3a5..abfad6a0 100644 --- a/cpp/README.md +++ b/cpp/README.md @@ -32,6 +32,7 @@ Simple C++ examples showing how to use ADBC to connect, run a query, and return - [ClickHouse](./clickhouse) - [Databricks](./databricks) - [DataFusion](./datafusion) +- [Apache Druid](./druid) - [DuckDB-compatible systems](./duckdb) - [DuckDB](./duckdb/duckdb) - [MotherDuck](./duckdb/motherduck) diff --git a/cpp/druid/CMakeLists.txt b/cpp/druid/CMakeLists.txt new file mode 100644 index 00000000..8b48f8ff --- /dev/null +++ b/cpp/druid/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +cmake_minimum_required(VERSION 3.15) +project(druid_demo CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Use conda environment paths +if(DEFINED ENV{CONDA_PREFIX}) + list(APPEND CMAKE_PREFIX_PATH "$ENV{CONDA_PREFIX}") +endif() + +find_package(Arrow REQUIRED) + +add_executable(druid_demo main.cpp) + +if(DEFINED ENV{CONDA_PREFIX}) + target_include_directories(druid_demo PRIVATE $ENV{CONDA_PREFIX}/include) + target_link_directories(druid_demo PRIVATE $ENV{CONDA_PREFIX}/lib) +endif() + +target_link_libraries(druid_demo + adbc_driver_manager + Arrow::arrow_shared +) + +target_compile_options(druid_demo PRIVATE + -Wall + -Werror +) diff --git a/cpp/druid/Makefile b/cpp/druid/Makefile new file mode 100644 index 00000000..9ac44beb --- /dev/null +++ b/cpp/druid/Makefile @@ -0,0 +1,24 @@ +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +CXXFLAGS = -std=c++17 -Wall -Werror -I$(CONDA_PREFIX)/include +LIBS = -L$(CONDA_PREFIX)/lib -ladbc_driver_manager -larrow -Wl,-rpath,$(CONDA_PREFIX)/lib + +druid_demo: main.cpp + $(CXX) $(CXXFLAGS) -o druid_demo main.cpp $(LIBS) + +clean: + rm -f druid_demo + +.PHONY: clean diff --git a/cpp/druid/README.md b/cpp/druid/README.md new file mode 100644 index 00000000..622d10ec --- /dev/null +++ b/cpp/druid/README.md @@ -0,0 +1,101 @@ + + +# Connecting C++ and Apache Druid with ADBC + +## Instructions + +> [!TIP] +> If you already have a Druid instance running, skip the steps to set up Druid. + +### Prerequisites + +1. [Install Pixi](https://pixi.prefix.dev/latest/) + +1. [Install dbc](https://docs.columnar.tech/dbc/getting_started/installation/) + +### Set up Druid + +1. [Install Docker](https://docs.docker.com/get-started/get-docker/) + +1. Start a Druid 37 nano-quickstart instance: + + ```sh + docker run --detach --rm \ + --name druid \ + --platform linux/amd64 \ + --publish 8888:8888 \ + --volume "$PWD/start-druid.sh:/opt/druid/start-druid.sh:ro" \ + --entrypoint /bin/bash \ + apache/druid:37.0.0 /opt/druid/start-druid.sh + ``` + +1. Wait for Druid to accept SQL queries: + + ```sh + until curl --fail --silent --output /dev/null \ + --header 'Content-Type: application/json' \ + --data '{"query":"SELECT 1"}' \ + http://localhost:8888/druid/v2/sql; do sleep 2; done + ``` + +### Connect to Druid + +1. Install the Druid ADBC driver: + + ```sh + dbc install --level user --pre druid + ``` + +1. Customize the C++ program `main.cpp` as needed, + - Change the connection arguments in the `AdbcDatabaseSetOption()` calls + - Format the URI according to the [driver documentation](https://docs.adbc-drivers.org/drivers/druid/index.html#connecting), or keep it as is + +1. Build and run the C++ program: + + Using Make: + ```sh + pixi run make + ./druid_demo + ``` + + Or using CMake: + ```sh + pixi run cmake -B build + pixi run cmake --build build + ./build/druid_demo + ``` + + +### Clean up + +1. Clean build artifacts: + + Using Make: + ```sh + pixi run make clean + ``` + + Using CMake: + ```sh + rm -rf build + ``` + +1. Stop the Docker container running Druid: + + ```sh + docker stop druid + ``` diff --git a/cpp/druid/main.cpp b/cpp/druid/main.cpp new file mode 100644 index 00000000..78e4b0ac --- /dev/null +++ b/cpp/druid/main.cpp @@ -0,0 +1,113 @@ +// Copyright 2026 Columnar Technologies Inc. +// +// Licensed 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. + +// For EXIT_SUCCESS +#include +// For strerror +#include +#include + +#include +#include +#include +#include + +// Error-checking helper for ADBC calls. +// Assumes that there is an AdbcError named `error` in scope. +#define CHECK_ADBC(EXPR) \ + if (AdbcStatusCode status = (EXPR); status != ADBC_STATUS_OK) { \ + if (error.message != nullptr) { \ + std::cerr << error.message << std::endl; \ + } \ + return EXIT_FAILURE; \ + } + +// Error-checking helper for ArrowArrayStream. +#define CHECK_STREAM(STREAM, EXPR) \ + if (int status = (EXPR); status != 0) { \ + std::cerr << "(" << std::strerror(status) << "): "; \ + const char *message = (STREAM).get_last_error(&(STREAM)); \ + if (message != nullptr) { \ + std::cerr << message << std::endl; \ + } else { \ + std::cerr << "(no error message)" << std::endl; \ + } \ + return EXIT_FAILURE; \ + } + +int main() { + AdbcError error = {}; + + AdbcDatabase database = {}; + CHECK_ADBC(AdbcDatabaseNew(&database, &error)); + + CHECK_ADBC(AdbcDatabaseSetOption(&database, "driver", "druid", &error)); + CHECK_ADBC(AdbcDatabaseSetOption( + &database, "uri", "druid://localhost:8888?tls=false", &error)); + CHECK_ADBC(AdbcDriverManagerDatabaseSetLoadFlags( + &database, ADBC_LOAD_FLAG_DEFAULT, &error)); + CHECK_ADBC(AdbcDatabaseInit(&database, &error)); + + AdbcConnection connection = {}; + CHECK_ADBC(AdbcConnectionNew(&connection, &error)); + CHECK_ADBC(AdbcConnectionInit(&connection, &database, &error)); + + AdbcStatement statement = {}; + CHECK_ADBC(AdbcStatementNew(&connection, &statement, &error)); + + struct ArrowArrayStream stream = {}; + int64_t rows_affected = -1; + + CHECK_ADBC(AdbcStatementSetSqlQuery( + &statement, + "SELECT \"server\", server_type, tier, curr_size, max_size " + "FROM sys.servers " + "ORDER BY server_type, \"server\" " + "LIMIT 10", + &error)); + CHECK_ADBC( + AdbcStatementExecuteQuery(&statement, &stream, &rows_affected, &error)); + + // Import stream as record batch reader + auto maybe_reader = arrow::ImportRecordBatchReader(&stream); + if (!maybe_reader.ok()) { + std::cerr << "Failed to import record batch reader: " + << maybe_reader.status().message() << std::endl; + return 1; + } + + auto reader = maybe_reader.ValueOrDie(); + + while (true) { + auto maybe_batch = reader->Next(); + if (!maybe_batch.ok()) { + std::cerr << "Error reading batch: " << maybe_batch.status().message() + << std::endl; + return 1; + } + + auto batch = maybe_batch.ValueOrDie(); + if (!batch) { + break; + } + + std::cout << batch->ToString() << std::endl; + } + + CHECK_ADBC(AdbcStatementRelease(&statement, &error)); + CHECK_ADBC(AdbcConnectionRelease(&connection, &error)); + CHECK_ADBC(AdbcDatabaseRelease(&database, &error)); + + return EXIT_SUCCESS; +} diff --git a/cpp/druid/pixi.toml b/cpp/druid/pixi.toml new file mode 100644 index 00000000..7c356b92 --- /dev/null +++ b/cpp/druid/pixi.toml @@ -0,0 +1,12 @@ +[workspace] +channels = ["conda-forge"] +name = "druid" +platforms = ["win-64", "linux-64", "osx-64", "osx-arm64"] + +[tasks] + +[dependencies] +cmake = ">=4.3.0,<5" +compilers = ">=1.11.0,<2" +libadbc-driver-manager = ">=1.10.0,<2" +libarrow = ">=23.0.1,<24" diff --git a/cpp/druid/start-druid.sh b/cpp/druid/start-druid.sh new file mode 100644 index 00000000..576fd7c8 --- /dev/null +++ b/cpp/druid/start-druid.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +set -euo pipefail + +readonly config="conf/druid/single-server/nano-quickstart" +pids=() + +start() { + "$@" & + pids+=("$!") +} + +shutdown() { + trap - EXIT INT TERM + kill -TERM "${pids[@]}" 2>/dev/null || true + wait "${pids[@]}" 2>/dev/null || true +} + +trap shutdown EXIT INT TERM + +start bin/run-zk conf +start bin/run-druid coordinator-overlord "$config" +start bin/run-druid broker "$config" +start bin/run-druid router "$config" +start bin/run-druid historical "$config" +start bin/run-druid middleManager "$config" + +set +e +wait -n "${pids[@]}" +status=$? +set -e + +# A service exiting normally still means the container is no longer healthy. +if [[ $status -eq 0 ]]; then + status=1 +fi +exit "$status" diff --git a/csharp/README.md b/csharp/README.md index 72638696..a4a4d5a3 100644 --- a/csharp/README.md +++ b/csharp/README.md @@ -32,6 +32,7 @@ Simple C# examples showing how to use ADBC to connect, run a query, and return t - [ClickHouse](./clickhouse) - [Databricks](./databricks) - [DataFusion](./datafusion) +- [Apache Druid](./druid) - [DuckDB-compatible systems](./duckdb) - [DuckDB](./duckdb/duckdb) - [MotherDuck](./duckdb/motherduck) diff --git a/csharp/druid/BatchPrinter.cs b/csharp/druid/BatchPrinter.cs new file mode 100644 index 00000000..ec83945d --- /dev/null +++ b/csharp/druid/BatchPrinter.cs @@ -0,0 +1,52 @@ +// Copyright 2026 Columnar Technologies Inc. +// +// Licensed 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. + +using System.Collections; +using Apache.Arrow; + +// Apache Arrow for C# has no built-in printer for record batches, so this helper +// prints one column per line. It is not part of the ADBC machinery the example +// demonstrates; it just makes the query results readable. +static class BatchPrinter +{ + public static void Print(RecordBatch batch) + { + for (int i = 0; i < batch.ColumnCount; i++) + { + Console.WriteLine($"{batch.Schema.FieldsList[i].Name}: {Render(batch.Column(i))}"); + } + } + + // Render a column's values as a comma-separated string. Most Arrow arrays + // enumerate as their values, but a few types need special handling: decimal + // arrays enumerate as raw bytes, and dictionary-encoded arrays (returned for + // low-cardinality columns) don't enumerate at all, so decode them by index. + // Nested types (list, struct, map) aren't enumerable either; this helper + // just names them rather than recursing into their values. + static string Render(IArrowArray column) => column switch + { + Decimal128Array decimals => string.Join(", ", + Enumerable.Range(0, decimals.Length).Select(decimals.GetString)), + Decimal256Array decimals => string.Join(", ", + Enumerable.Range(0, decimals.Length).Select(decimals.GetString)), + DictionaryArray dictionary => string.Join(", ", + dictionary.EnumeratePhysicalIndices().Select(i => i < 0 ? "" : DictValues(dictionary)[i])), + IEnumerable values => string.Join(", ", values.Cast()), + _ => $"<{column.Data.DataType.TypeId}>", + }; + + // Materialize a dictionary array's distinct values as strings. + static string?[] DictValues(DictionaryArray dictionary) => + ((IEnumerable)dictionary.Dictionary).Cast().Select(value => value?.ToString()).ToArray(); +} diff --git a/csharp/druid/Program.cs b/csharp/druid/Program.cs new file mode 100644 index 00000000..b8028adc --- /dev/null +++ b/csharp/druid/Program.cs @@ -0,0 +1,48 @@ +// Copyright 2026 Columnar Technologies Inc. +// +// Licensed 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. + +using Apache.Arrow.Adbc; +using Apache.Arrow.Adbc.DriverManager; +using Apache.Arrow.Ipc; + +using AdbcDriver driver = AdbcDriverManager.FindLoadDriver( + "druid", + loadOptions: AdbcLoadFlags.Default); + +using AdbcDatabase db = driver.Open(new Dictionary +{ + ["uri"] = "druid://localhost:8888?tls=false", +}); + +using AdbcConnection conn = db.Connect(null); +using AdbcStatement stmt = conn.CreateStatement(); + +stmt.SqlQuery = + """ + SELECT "server", server_type, tier, curr_size, max_size + FROM sys.servers + ORDER BY server_type, "server" + LIMIT 10 + """; + +QueryResult result = stmt.ExecuteQuery(); +using IArrowArrayStream stream = result.Stream!; + +while (await stream.ReadNextRecordBatchAsync() is { } batch) +{ + using (batch) + { + BatchPrinter.Print(batch); + } +} diff --git a/csharp/druid/README.md b/csharp/druid/README.md new file mode 100644 index 00000000..6fec34f8 --- /dev/null +++ b/csharp/druid/README.md @@ -0,0 +1,79 @@ + + +# Connecting C# and Apache Druid with ADBC + +## Instructions + +> [!TIP] +> If you already have a Druid instance running, skip the steps to set up Druid. + +### Prerequisites + +1. [Install the .NET SDK](https://dotnet.microsoft.com/download) + +1. [Install dbc](https://docs.columnar.tech/dbc/getting_started/installation/) + +### Set up Druid + +1. [Install Docker](https://docs.docker.com/get-started/get-docker/) + +1. Start a Druid 37 nano-quickstart instance: + + ```sh + docker run --detach --rm \ + --name druid \ + --platform linux/amd64 \ + --publish 8888:8888 \ + --volume "$PWD/start-druid.sh:/opt/druid/start-druid.sh:ro" \ + --entrypoint /bin/bash \ + apache/druid:37.0.0 /opt/druid/start-druid.sh + ``` + +1. Wait for Druid to accept SQL queries: + + ```sh + until curl --fail --silent --output /dev/null \ + --header 'Content-Type: application/json' \ + --data '{"query":"SELECT 1"}' \ + http://localhost:8888/druid/v2/sql; do sleep 2; done + ``` + +### Connect to Druid + +1. Install the Druid ADBC driver: + + ```sh + dbc install --pre druid + ``` + +1. Customize the C# program `Program.cs` as needed + - Change the connection arguments passed to `driver.Open()` + - Format `uri` according to the [driver documentation](https://docs.adbc-drivers.org/drivers/druid/index.html#connecting), or keep it as is + +1. Run the C# program: + + ```sh + dotnet run + ``` + +### Clean up + +1. Stop the Docker container running Druid: + + ```sh + docker stop druid + ``` diff --git a/csharp/druid/druid.csproj b/csharp/druid/druid.csproj new file mode 100644 index 00000000..846f6aab --- /dev/null +++ b/csharp/druid/druid.csproj @@ -0,0 +1,29 @@ + + + + + Exe + net10.0 + enable + enable + + + + + + + diff --git a/csharp/druid/start-druid.sh b/csharp/druid/start-druid.sh new file mode 100644 index 00000000..576fd7c8 --- /dev/null +++ b/csharp/druid/start-druid.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +set -euo pipefail + +readonly config="conf/druid/single-server/nano-quickstart" +pids=() + +start() { + "$@" & + pids+=("$!") +} + +shutdown() { + trap - EXIT INT TERM + kill -TERM "${pids[@]}" 2>/dev/null || true + wait "${pids[@]}" 2>/dev/null || true +} + +trap shutdown EXIT INT TERM + +start bin/run-zk conf +start bin/run-druid coordinator-overlord "$config" +start bin/run-druid broker "$config" +start bin/run-druid router "$config" +start bin/run-druid historical "$config" +start bin/run-druid middleManager "$config" + +set +e +wait -n "${pids[@]}" +status=$? +set -e + +# A service exiting normally still means the container is no longer healthy. +if [[ $status -eq 0 ]]; then + status=1 +fi +exit "$status" diff --git a/go/README.md b/go/README.md index 95d2ae01..07327350 100644 --- a/go/README.md +++ b/go/README.md @@ -32,6 +32,7 @@ Simple Go examples showing how to use ADBC to connect, run a query, and return t - [ClickHouse](./clickhouse) - [Databricks](./databricks) - [DataFusion](./datafusion) +- [Apache Druid](./druid) - [DuckDB-compatible systems](./duckdb) - [DuckDB](./duckdb/duckdb) - [MotherDuck](./duckdb/motherduck) diff --git a/go/druid/README.md b/go/druid/README.md new file mode 100644 index 00000000..cd83f655 --- /dev/null +++ b/go/druid/README.md @@ -0,0 +1,80 @@ + + +# Connecting Go and Apache Druid with ADBC + +## Instructions + +> [!TIP] +> If you already have a Druid instance running, skip the steps to set up Druid. + +### Prerequisites + +1. [Install Go](https://go.dev/doc/install) + +1. [Install dbc](https://docs.columnar.tech/dbc/getting_started/installation/) + +### Set up Druid + +1. [Install Docker](https://docs.docker.com/get-started/get-docker/) + +1. Start a Druid 37 nano-quickstart instance: + + ```sh + docker run --detach --rm \ + --name druid \ + --platform linux/amd64 \ + --publish 8888:8888 \ + --volume "$PWD/start-druid.sh:/opt/druid/start-druid.sh:ro" \ + --entrypoint /bin/bash \ + apache/druid:37.0.0 /opt/druid/start-druid.sh + ``` + +1. Wait for Druid to accept SQL queries: + + ```sh + until curl --fail --silent --output /dev/null \ + --header 'Content-Type: application/json' \ + --data '{"query":"SELECT 1"}' \ + http://localhost:8888/druid/v2/sql; do sleep 2; done + ``` + +### Connect to Druid + +1. Install the Druid ADBC driver: + + ```sh + dbc install --pre druid + ``` + +1. Customize the Go program `main.go` as needed + - Change the connection arguments in the `NewDatabase()` call + - Format `uri` according to the [driver documentation](https://docs.adbc-drivers.org/drivers/druid/index.html#connecting), or keep it as is + +1. Run the Go program: + + ```sh + go mod tidy + go run main.go + ``` + +### Clean up + +1. Stop the Docker container running Druid: + + ```sh + docker stop druid + ``` diff --git a/go/druid/main.go b/go/druid/main.go new file mode 100644 index 00000000..e55ee7af --- /dev/null +++ b/go/druid/main.go @@ -0,0 +1,74 @@ +// Copyright 2026 Columnar Technologies Inc. +// +// Licensed 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. + +package main + +import ( + "context" + "fmt" + "log" + + "github.com/apache/arrow-adbc/go/adbc/drivermgr" +) + +func main() { + var drv drivermgr.Driver + + db, err := drv.NewDatabase(map[string]string{ + "driver": "druid", + "uri": "druid://localhost:8888?tls=false", + }) + if err != nil { + log.Fatal(err) + } + defer db.Close() + + conn, err := db.Open(context.Background()) + if err != nil { + log.Fatal(err) + } + defer conn.Close() + + stmt, err := conn.NewStatement() + if err != nil { + log.Fatal(err) + } + defer stmt.Close() + + err = stmt.SetSqlQuery(` + SELECT "server", server_type, tier, curr_size, max_size + FROM sys.servers + ORDER BY server_type, "server" + LIMIT 10 + `) + if err != nil { + log.Fatal(err) + } + + stream, _, err := stmt.ExecuteQuery(context.Background()) + if err != nil { + log.Fatal(err) + } + defer stream.Release() + + // Read all record batches from the stream + for stream.Next() { + batch := stream.RecordBatch() + fmt.Println(batch) + } + + if err := stream.Err(); err != nil { + log.Fatal(err) + } +} diff --git a/go/druid/start-druid.sh b/go/druid/start-druid.sh new file mode 100644 index 00000000..576fd7c8 --- /dev/null +++ b/go/druid/start-druid.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +set -euo pipefail + +readonly config="conf/druid/single-server/nano-quickstart" +pids=() + +start() { + "$@" & + pids+=("$!") +} + +shutdown() { + trap - EXIT INT TERM + kill -TERM "${pids[@]}" 2>/dev/null || true + wait "${pids[@]}" 2>/dev/null || true +} + +trap shutdown EXIT INT TERM + +start bin/run-zk conf +start bin/run-druid coordinator-overlord "$config" +start bin/run-druid broker "$config" +start bin/run-druid router "$config" +start bin/run-druid historical "$config" +start bin/run-druid middleManager "$config" + +set +e +wait -n "${pids[@]}" +status=$? +set -e + +# A service exiting normally still means the container is no longer healthy. +if [[ $status -eq 0 ]]; then + status=1 +fi +exit "$status" diff --git a/java/README.md b/java/README.md index e2d2292c..37592120 100644 --- a/java/README.md +++ b/java/README.md @@ -32,6 +32,7 @@ Simple Java examples showing how to use ADBC to connect, run a query, and return - [ClickHouse](./clickhouse) - [Databricks](./databricks) - [DataFusion](./datafusion) +- [Apache Druid](./druid) - [DuckDB-compatible systems](./duckdb) - [DuckDB](./duckdb/duckdb) - [MotherDuck](./duckdb/motherduck) diff --git a/java/druid/README.md b/java/druid/README.md new file mode 100644 index 00000000..d4cc7f29 --- /dev/null +++ b/java/druid/README.md @@ -0,0 +1,79 @@ + + +# Connecting Java and Apache Druid with ADBC + +## Instructions + +> [!TIP] +> If you already have a Druid instance running, skip the steps to set up Druid. + +### Prerequisites + +1. [Install Maven](https://maven.apache.org/install.html) + +1. [Install dbc](https://docs.columnar.tech/dbc/getting_started/installation/) + +### Set up Druid + +1. [Install Docker](https://docs.docker.com/get-started/get-docker/) + +1. Start a Druid 37 nano-quickstart instance: + + ```sh + docker run --detach --rm \ + --name druid \ + --platform linux/amd64 \ + --publish 8888:8888 \ + --volume "$PWD/start-druid.sh:/opt/druid/start-druid.sh:ro" \ + --entrypoint /bin/bash \ + apache/druid:37.0.0 /opt/druid/start-druid.sh + ``` + +1. Wait for Druid to accept SQL queries: + + ```sh + until curl --fail --silent --output /dev/null \ + --header 'Content-Type: application/json' \ + --data '{"query":"SELECT 1"}' \ + http://localhost:8888/druid/v2/sql; do sleep 2; done + ``` + +### Connect to Druid + +1. Install the Druid ADBC driver: + + ```sh + dbc install --pre druid + ``` + +1. Customize the `main` method in `Example.java` + - Change the connection arguments in the `params.put()` calls + - Format `uri` according to the [driver documentation](https://docs.adbc-drivers.org/drivers/druid/index.html#connecting), or keep it as is + +1. Run the Java program: + + ```sh + mvn compile exec:exec + ``` + +### Clean up + +1. Stop the Docker container running Druid: + + ```sh + docker stop druid + ``` diff --git a/java/druid/pom.xml b/java/druid/pom.xml new file mode 100644 index 00000000..80af212f --- /dev/null +++ b/java/druid/pom.xml @@ -0,0 +1,136 @@ + + + + 4.0.0 + + tech.columnar + adbc-quickstart-druid + 1.0-SNAPSHOT + + adbc-quickstart-java-druid + + + UTF-8 + 17 + tech.columnar.Example + 18.3.0 + 0.21.0 + + + + + + org.apache.arrow + arrow-bom + ${arrow.version} + pom + import + + + + + + + org.apache.arrow + arrow-memory-core + + + org.apache.arrow + arrow-memory-netty + + + org.apache.arrow + arrow-vector + + + + org.apache.arrow.adbc + adbc-core + ${adbc.version} + + + org.apache.arrow.adbc + adbc-driver-manager + ${adbc.version} + + + org.apache.arrow.adbc + adbc-driver-jni + ${adbc.version} + + + + + + + + maven-clean-plugin + 3.4.0 + + + maven-resources-plugin + 3.3.1 + + + maven-compiler-plugin + 3.13.0 + + + maven-surefire-plugin + 3.3.0 + + + maven-jar-plugin + 3.4.2 + + + maven-install-plugin + 3.1.2 + + + maven-deploy-plugin + 3.1.2 + + + maven-site-plugin + 3.12.1 + + + maven-project-info-reports-plugin + 3.6.1 + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.5.1 + + java + + --add-opens=java.base/java.nio=org.apache.arrow.memory.core,ALL-UNNAMED + -classpath + + tech.columnar.Example + + + + + + diff --git a/java/druid/src/main/java/tech/columnar/Example.java b/java/druid/src/main/java/tech/columnar/Example.java new file mode 100644 index 00000000..fd5bada2 --- /dev/null +++ b/java/druid/src/main/java/tech/columnar/Example.java @@ -0,0 +1,58 @@ +/* + * Copyright 2026 Columnar Technologies Inc. + * + * Licensed 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. + */ + +package tech.columnar; + +import java.util.HashMap; +import java.util.Map; +import org.apache.arrow.adbc.core.AdbcConnection; +import org.apache.arrow.adbc.core.AdbcDatabase; +import org.apache.arrow.adbc.core.AdbcStatement; +import org.apache.arrow.adbc.driver.jni.JniDriver; +import org.apache.arrow.adbc.drivermanager.AdbcDriverManager; +import org.apache.arrow.memory.BufferAllocator; +import org.apache.arrow.memory.RootAllocator; +import org.apache.arrow.vector.ipc.ArrowReader; + +public class Example { + private static final String DRIVER_FACTORY = "org.apache.arrow.adbc.driver.jni.JniDriverFactory"; + + public static void main(String[] args) throws Exception { + Map params = new HashMap<>(); + JniDriver.PARAM_DRIVER.set(params, "druid"); + params.put("uri", "druid://localhost:8888?tls=false"); + + try (BufferAllocator allocator = new RootAllocator(); + AdbcDatabase db = + AdbcDriverManager.getInstance().connect(DRIVER_FACTORY, allocator, params); + AdbcConnection conn = db.connect(); + AdbcStatement stmt = conn.createStatement()) { + stmt.setSqlQuery( + """ + SELECT "server", server_type, tier, curr_size, max_size + FROM sys.servers + ORDER BY server_type, "server" + LIMIT 10 + """); + try (AdbcStatement.QueryResult result = stmt.executeQuery()) { + ArrowReader reader = result.getReader(); + while (reader.loadNextBatch()) { + System.out.println(reader.getVectorSchemaRoot().contentToTSVString()); + } + } + } + } +} diff --git a/java/druid/start-druid.sh b/java/druid/start-druid.sh new file mode 100644 index 00000000..576fd7c8 --- /dev/null +++ b/java/druid/start-druid.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +set -euo pipefail + +readonly config="conf/druid/single-server/nano-quickstart" +pids=() + +start() { + "$@" & + pids+=("$!") +} + +shutdown() { + trap - EXIT INT TERM + kill -TERM "${pids[@]}" 2>/dev/null || true + wait "${pids[@]}" 2>/dev/null || true +} + +trap shutdown EXIT INT TERM + +start bin/run-zk conf +start bin/run-druid coordinator-overlord "$config" +start bin/run-druid broker "$config" +start bin/run-druid router "$config" +start bin/run-druid historical "$config" +start bin/run-druid middleManager "$config" + +set +e +wait -n "${pids[@]}" +status=$? +set -e + +# A service exiting normally still means the container is no longer healthy. +if [[ $status -eq 0 ]]; then + status=1 +fi +exit "$status" diff --git a/javascript/README.md b/javascript/README.md index f750a8a9..0ce606bb 100644 --- a/javascript/README.md +++ b/javascript/README.md @@ -35,6 +35,7 @@ Simple JavaScript examples showing how to use ADBC to connect, run a query, and - [ClickHouse](./clickhouse) - [Databricks](./databricks) - [DataFusion](./datafusion) +- [Apache Druid](./druid) - [DuckDB-compatible systems](./duckdb) - [DuckDB](./duckdb/duckdb) - [MotherDuck](./duckdb/motherduck) diff --git a/javascript/druid/README.md b/javascript/druid/README.md new file mode 100644 index 00000000..ec07ce60 --- /dev/null +++ b/javascript/druid/README.md @@ -0,0 +1,100 @@ + + +# Connecting JavaScript and Apache Druid with ADBC + +## Instructions + +> [!TIP] +> If you already have a Druid instance running, skip the steps to set up Druid. + +### Prerequisites + +1. [Install Node.js](https://nodejs.org/) (version 22 or later) + - Alternatively, you can use [Bun](https://bun.sh/) or [Deno](https://deno.com/) + +1. [Install dbc](https://docs.columnar.tech/dbc/getting_started/installation/) + +### Set up Druid + +1. [Install Docker](https://docs.docker.com/get-started/get-docker/) + +1. Start a Druid 37 nano-quickstart instance: + + ```sh + docker run --detach --rm \ + --name druid \ + --platform linux/amd64 \ + --publish 8888:8888 \ + --volume "$PWD/start-druid.sh:/opt/druid/start-druid.sh:ro" \ + --entrypoint /bin/bash \ + apache/druid:37.0.0 /opt/druid/start-druid.sh + ``` + +1. Wait for Druid to accept SQL queries: + + ```sh + until curl --fail --silent --output /dev/null \ + --header 'Content-Type: application/json' \ + --data '{"query":"SELECT 1"}' \ + http://localhost:8888/druid/v2/sql; do sleep 2; done + ``` + +### Connect to Druid + +1. Install the Druid ADBC driver: + + ```sh + dbc install --pre druid + ``` + +1. Install dependencies: + + ```sh + npm --prefix .. install + ``` + +1. Customize the script `main.js` as needed + - Change the connection arguments in `databaseOptions` + - Format `uri` according to the [driver documentation](https://docs.adbc-drivers.org/drivers/druid/index.html#connecting), or keep it as is + +1. Run the script: + + **Node.js:** + + ```sh + node main.js + ``` + + **Bun:** + + ```sh + bun run main.js + ``` + + **Deno:** + + ```sh + deno run --allow-ffi --allow-env main.js + ``` + +### Clean up + +Stop the Docker container running Druid: + +```sh +docker stop druid +``` diff --git a/javascript/druid/main.js b/javascript/druid/main.js new file mode 100644 index 00000000..915e7b4e --- /dev/null +++ b/javascript/druid/main.js @@ -0,0 +1,37 @@ +// Copyright 2026 Columnar Technologies Inc. +// +// Licensed 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. + +import { AdbcDatabase } from '@apache-arrow/adbc-driver-manager'; + +const db = new AdbcDatabase({ + driver: 'druid', + databaseOptions: { + uri: 'druid://localhost:8888?tls=false', + }, +}); + +let conn; +try { + conn = await db.connect(); + const table = await conn.query(` + SELECT "server", server_type, tier, curr_size, max_size + FROM sys.servers + ORDER BY server_type, "server" + LIMIT 10 + `); + console.log(table.toString()); +} finally { + await conn?.close(); + await db.close(); +} diff --git a/javascript/druid/start-druid.sh b/javascript/druid/start-druid.sh new file mode 100644 index 00000000..576fd7c8 --- /dev/null +++ b/javascript/druid/start-druid.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +set -euo pipefail + +readonly config="conf/druid/single-server/nano-quickstart" +pids=() + +start() { + "$@" & + pids+=("$!") +} + +shutdown() { + trap - EXIT INT TERM + kill -TERM "${pids[@]}" 2>/dev/null || true + wait "${pids[@]}" 2>/dev/null || true +} + +trap shutdown EXIT INT TERM + +start bin/run-zk conf +start bin/run-druid coordinator-overlord "$config" +start bin/run-druid broker "$config" +start bin/run-druid router "$config" +start bin/run-druid historical "$config" +start bin/run-druid middleManager "$config" + +set +e +wait -n "${pids[@]}" +status=$? +set -e + +# A service exiting normally still means the container is no longer healthy. +if [[ $status -eq 0 ]]; then + status=1 +fi +exit "$status" diff --git a/kotlin/README.md b/kotlin/README.md index b79410c8..10d6a4a8 100644 --- a/kotlin/README.md +++ b/kotlin/README.md @@ -32,6 +32,7 @@ Simple Kotlin examples showing how to use ADBC to connect, run a query, and retu - [ClickHouse](./clickhouse) - [Databricks](./databricks) - [DataFusion](./datafusion) +- [Apache Druid](./druid) - [DuckDB-compatible systems](./duckdb) - [DuckDB](./duckdb/duckdb) - [MotherDuck](./duckdb/motherduck) diff --git a/kotlin/druid/README.md b/kotlin/druid/README.md new file mode 100644 index 00000000..f0ad7e71 --- /dev/null +++ b/kotlin/druid/README.md @@ -0,0 +1,79 @@ + + +# Connecting Kotlin and Apache Druid with ADBC + +## Instructions + +> [!TIP] +> If you already have a Druid instance running, skip the steps to set up Druid. + +### Prerequisites + +1. [Install Gradle](https://docs.gradle.org/current/userguide/installation.html) + +1. [Install dbc](https://docs.columnar.tech/dbc/getting_started/installation/) + +### Set up Druid + +1. [Install Docker](https://docs.docker.com/get-started/get-docker/) + +1. Start a Druid 37 nano-quickstart instance: + + ```sh + docker run --detach --rm \ + --name druid \ + --platform linux/amd64 \ + --publish 8888:8888 \ + --volume "$PWD/start-druid.sh:/opt/druid/start-druid.sh:ro" \ + --entrypoint /bin/bash \ + apache/druid:37.0.0 /opt/druid/start-druid.sh + ``` + +1. Wait for Druid to accept SQL queries: + + ```sh + until curl --fail --silent --output /dev/null \ + --header 'Content-Type: application/json' \ + --data '{"query":"SELECT 1"}' \ + http://localhost:8888/druid/v2/sql; do sleep 2; done + ``` + +### Connect to Druid + +1. Install the Druid ADBC driver: + + ```sh + dbc install --pre druid + ``` + +1. Customize the `main` function in `Main.kt` + - Change the connection arguments in `params` + - Format `uri` according to the [driver documentation](https://docs.adbc-drivers.org/drivers/druid/index.html#connecting), or keep it as is + +1. Run the Kotlin program: + + ```sh + gradle run + ``` + +### Clean up + +1. Stop the Docker container running Druid: + + ```sh + docker stop druid + ``` diff --git a/kotlin/druid/build.gradle.kts b/kotlin/druid/build.gradle.kts new file mode 100644 index 00000000..4e61efc5 --- /dev/null +++ b/kotlin/druid/build.gradle.kts @@ -0,0 +1,31 @@ +plugins { + kotlin("jvm") version "2.3.21" + application +} + +repositories { + mavenCentral() +} + +val arrowVersion = "18.3.0" +val adbcVersion = "0.21.0" + +dependencies { + implementation("org.apache.arrow:arrow-memory-core:$arrowVersion") + implementation("org.apache.arrow:arrow-memory-netty:$arrowVersion") + implementation("org.apache.arrow:arrow-vector:$arrowVersion") + implementation("org.apache.arrow.adbc:adbc-core:$adbcVersion") + implementation("org.apache.arrow.adbc:adbc-driver-manager:$adbcVersion") + implementation("org.apache.arrow.adbc:adbc-driver-jni:$adbcVersion") + implementation("org.slf4j:slf4j-nop:2.0.16") +} + +application { + mainClass.set("MainKt") + applicationDefaultJvmArgs = + listOf( + "--add-opens=java.base/java.nio=ALL-UNNAMED", + "--enable-native-access=ALL-UNNAMED", + "--sun-misc-unsafe-memory-access=allow", + ) +} diff --git a/kotlin/druid/gradle.properties b/kotlin/druid/gradle.properties new file mode 100644 index 00000000..5ad69748 --- /dev/null +++ b/kotlin/druid/gradle.properties @@ -0,0 +1 @@ +org.gradle.configuration-cache=true diff --git a/kotlin/druid/settings.gradle.kts b/kotlin/druid/settings.gradle.kts new file mode 100644 index 00000000..cae042ce --- /dev/null +++ b/kotlin/druid/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "adbc-quickstart-druid" diff --git a/kotlin/druid/src/main/kotlin/Main.kt b/kotlin/druid/src/main/kotlin/Main.kt new file mode 100644 index 00000000..2ad59081 --- /dev/null +++ b/kotlin/druid/src/main/kotlin/Main.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2026 Columnar Technologies Inc. + * + * Licensed 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. + */ + +import org.apache.arrow.adbc.driver.jni.JniDriver +import org.apache.arrow.adbc.drivermanager.AdbcDriverManager +import org.apache.arrow.memory.RootAllocator + +private const val DRIVER_FACTORY = "org.apache.arrow.adbc.driver.jni.JniDriverFactory" + +fun main() { + val params = mutableMapOf() + JniDriver.PARAM_DRIVER.set(params, "druid") + params["uri"] = "druid://localhost:8888?tls=false" + + RootAllocator().use { allocator -> + AdbcDriverManager.getInstance().connect(DRIVER_FACTORY, allocator, params).use { db -> + db.connect().use { conn -> + conn.createStatement().use { stmt -> + stmt.setSqlQuery( + """ + SELECT "server", server_type, tier, curr_size, max_size + FROM sys.servers + ORDER BY server_type, "server" + LIMIT 10 + """.trimIndent(), + ) + stmt.executeQuery().use { result -> + val reader = result.reader + while (reader.loadNextBatch()) { + println(reader.vectorSchemaRoot.contentToTSVString()) + } + } + } + } + } + } +} diff --git a/kotlin/druid/start-druid.sh b/kotlin/druid/start-druid.sh new file mode 100644 index 00000000..576fd7c8 --- /dev/null +++ b/kotlin/druid/start-druid.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +set -euo pipefail + +readonly config="conf/druid/single-server/nano-quickstart" +pids=() + +start() { + "$@" & + pids+=("$!") +} + +shutdown() { + trap - EXIT INT TERM + kill -TERM "${pids[@]}" 2>/dev/null || true + wait "${pids[@]}" 2>/dev/null || true +} + +trap shutdown EXIT INT TERM + +start bin/run-zk conf +start bin/run-druid coordinator-overlord "$config" +start bin/run-druid broker "$config" +start bin/run-druid router "$config" +start bin/run-druid historical "$config" +start bin/run-druid middleManager "$config" + +set +e +wait -n "${pids[@]}" +status=$? +set -e + +# A service exiting normally still means the container is no longer healthy. +if [[ $status -eq 0 ]]; then + status=1 +fi +exit "$status" diff --git a/r/README.md b/r/README.md index 0e221e1c..c0ab3cd5 100644 --- a/r/README.md +++ b/r/README.md @@ -32,6 +32,7 @@ Simple R examples showing how to use ADBC to connect, run a query, and return th - [ClickHouse](./clickhouse) - [Databricks](./databricks) - [DataFusion](./datafusion) +- [Apache Druid](./druid) - [DuckDB-compatible systems](./duckdb) - [DuckDB](./duckdb/duckdb) - [MotherDuck](./duckdb/motherduck) diff --git a/r/druid/README.md b/r/druid/README.md new file mode 100644 index 00000000..fbeefd89 --- /dev/null +++ b/r/druid/README.md @@ -0,0 +1,85 @@ + + +# Connecting R and Apache Druid with ADBC + +## Instructions + +> [!TIP] +> If you already have a Druid instance running, skip the steps to set up Druid. + +### Prerequisites + +1. [Install R](https://www.r-project.org/) + +1. [Install dbc](https://docs.columnar.tech/dbc/getting_started/installation/) + +1. Install R packages `adbcdrivermanager`, `arrow`, and `tibble`: + + ```r + install.packages(c("adbcdrivermanager", "arrow", "tibble")) + ``` + +### Set up Druid + +1. [Install Docker](https://docs.docker.com/get-started/get-docker/) + +1. Start a Druid 37 nano-quickstart instance: + + ```sh + docker run --detach --rm \ + --name druid \ + --platform linux/amd64 \ + --publish 8888:8888 \ + --volume "$PWD/start-druid.sh:/opt/druid/start-druid.sh:ro" \ + --entrypoint /bin/bash \ + apache/druid:37.0.0 /opt/druid/start-druid.sh + ``` + +1. Wait for Druid to accept SQL queries: + + ```sh + until curl --fail --silent --output /dev/null \ + --header 'Content-Type: application/json' \ + --data '{"query":"SELECT 1"}' \ + http://localhost:8888/druid/v2/sql; do sleep 2; done + ``` + +### Connect to Druid + +1. Install the Druid ADBC driver: + + ```sh + dbc install --pre druid + ``` + +1. Customize the R script `main.R` as needed + - Change the connection arguments in `adbc_database_init()` + - Format `uri` according to the [driver documentation](https://docs.adbc-drivers.org/drivers/druid/index.html#connecting), or keep it as is + +1. Run the R script: + + ```sh + Rscript main.R + ``` + +### Clean up + +1. Stop the Docker container running Druid: + + ```sh + docker stop druid + ``` diff --git a/r/druid/main.R b/r/druid/main.R new file mode 100644 index 00000000..8148eb34 --- /dev/null +++ b/r/druid/main.R @@ -0,0 +1,37 @@ +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +library(adbcdrivermanager) + +drv <- adbc_driver("druid") + +db <- adbc_database_init( + drv, + uri = "druid://localhost:8888?tls=false" +) + +con <- adbc_connection_init(db) + +con |> + read_adbc( + " + SELECT \"server\", server_type, tier, curr_size, max_size + FROM sys.servers + ORDER BY server_type, \"server\" + LIMIT 10 + " + ) |> + tibble::as_tibble() # or: +# arrow::as_arrow_table() # to keep result in Arrow format +# arrow::as_record_batch_reader() # for larger results diff --git a/r/druid/start-druid.sh b/r/druid/start-druid.sh new file mode 100644 index 00000000..576fd7c8 --- /dev/null +++ b/r/druid/start-druid.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +set -euo pipefail + +readonly config="conf/druid/single-server/nano-quickstart" +pids=() + +start() { + "$@" & + pids+=("$!") +} + +shutdown() { + trap - EXIT INT TERM + kill -TERM "${pids[@]}" 2>/dev/null || true + wait "${pids[@]}" 2>/dev/null || true +} + +trap shutdown EXIT INT TERM + +start bin/run-zk conf +start bin/run-druid coordinator-overlord "$config" +start bin/run-druid broker "$config" +start bin/run-druid router "$config" +start bin/run-druid historical "$config" +start bin/run-druid middleManager "$config" + +set +e +wait -n "${pids[@]}" +status=$? +set -e + +# A service exiting normally still means the container is no longer healthy. +if [[ $status -eq 0 ]]; then + status=1 +fi +exit "$status" diff --git a/ruby/README.md b/ruby/README.md index a72f9e29..a3d9497c 100644 --- a/ruby/README.md +++ b/ruby/README.md @@ -32,6 +32,7 @@ Simple Ruby examples showing how to use ADBC to connect, run a query, and return - [ClickHouse](./clickhouse) - [Databricks](./databricks) - [DataFusion](./datafusion) +- [Apache Druid](./druid) - [DuckDB-compatible systems](./duckdb) - [DuckDB](./duckdb/duckdb) - [MotherDuck](./duckdb/motherduck) diff --git a/ruby/druid/Gemfile b/ruby/druid/Gemfile new file mode 100644 index 00000000..8b02b5ac --- /dev/null +++ b/ruby/druid/Gemfile @@ -0,0 +1,17 @@ +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +source "https://rubygems.org" + +gem "red-adbc", "~> 1.11" diff --git a/ruby/druid/README.md b/ruby/druid/README.md new file mode 100644 index 00000000..3edce435 --- /dev/null +++ b/ruby/druid/README.md @@ -0,0 +1,131 @@ + + +# Connecting Ruby and Apache Druid with ADBC + +## Instructions + +> [!TIP] +> If you already have a Druid instance running, skip the steps to set up Druid. + +### Prerequisites + +1. [Install Ruby](https://www.ruby-lang.org/) + +1. [Install dbc](https://docs.columnar.tech/dbc/getting_started/installation/) + +1. Ensure the native Arrow GLib and ADBC GLib libraries required by `red-adbc` + are installed and discoverable. If `bundle install` reports missing `arrow`, + `arrow-glib`, or `adbc-glib`, use the platform-specific commands below. + +
+ macOS with Homebrew + + ```sh + brew install apache-arrow-glib apache-arrow-adbc-glib + ``` + +
+ +
+ Debian/Ubuntu + + ```sh + sudo apt install libarrow-glib-dev libadbc-glib-dev + ``` + +
+ +
+ RHEL-compatible distributions + + ```sh + sudo dnf install arrow-glib-devel adbc-glib-devel + ``` + +
+ +
+ Windows with RubyInstaller/MSYS2 UCRT64 + + ```sh + pacman -S --needed mingw-w64-ucrt-x86_64-arrow mingw-w64-ucrt-x86_64-arrow-adbc-glib + ``` + + If you use a different MSYS2 environment, adjust the package prefix to match + it; for example, use `mingw-w64-x86_64-*` from the MINGW64 shell. + +
+ +1. Install Ruby dependencies: + + ```sh + bundle install + ``` + + If you have multiple Ruby installations, ensure `ruby` and `bundle` resolve + to the same installation before running this command. + +### Set up Druid + +1. [Install Docker](https://docs.docker.com/get-started/get-docker/) + +1. Start a Druid 37 nano-quickstart instance: + + ```sh + docker run --detach --rm \ + --name druid \ + --platform linux/amd64 \ + --publish 8888:8888 \ + --volume "$PWD/start-druid.sh:/opt/druid/start-druid.sh:ro" \ + --entrypoint /bin/bash \ + apache/druid:37.0.0 /opt/druid/start-druid.sh + ``` + +1. Wait for Druid to accept SQL queries: + + ```sh + until curl --fail --silent --output /dev/null \ + --header 'Content-Type: application/json' \ + --data '{"query":"SELECT 1"}' \ + http://localhost:8888/druid/v2/sql; do sleep 2; done + ``` + +### Connect to Druid + +1. Install the Druid ADBC driver: + + ```sh + dbc install --level user --pre druid + ``` + +1. Customize the Ruby script `main.rb` as needed + - Change the connection arguments in `database.set_option()` + - Format `uri` according to the [driver documentation](https://docs.adbc-drivers.org/drivers/druid/index.html#connecting), or keep it as is + +1. Run the Ruby script: + + ```sh + bundle exec ruby main.rb + ``` + +### Clean up + +1. Stop the Docker container running Druid: + + ```sh + docker stop druid + ``` diff --git a/ruby/druid/main.rb b/ruby/druid/main.rb new file mode 100644 index 00000000..6b7a7900 --- /dev/null +++ b/ruby/druid/main.rb @@ -0,0 +1,36 @@ +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +require "adbc" + +database = ADBC::Database.new + +begin + database.set_option("driver", "druid") + database.set_option("uri", "druid://localhost:8888?tls=false") + database.set_load_flags(ADBC::LoadFlags::DEFAULT) + database.init + + database.connect do |connection| + table, = connection.query(<<~SQL) + SELECT "server", server_type, tier, curr_size, max_size + FROM sys.servers + ORDER BY server_type, "server" + LIMIT 10 + SQL + puts(table) + end +ensure + database.release +end diff --git a/ruby/druid/start-druid.sh b/ruby/druid/start-druid.sh new file mode 100644 index 00000000..576fd7c8 --- /dev/null +++ b/ruby/druid/start-druid.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +set -euo pipefail + +readonly config="conf/druid/single-server/nano-quickstart" +pids=() + +start() { + "$@" & + pids+=("$!") +} + +shutdown() { + trap - EXIT INT TERM + kill -TERM "${pids[@]}" 2>/dev/null || true + wait "${pids[@]}" 2>/dev/null || true +} + +trap shutdown EXIT INT TERM + +start bin/run-zk conf +start bin/run-druid coordinator-overlord "$config" +start bin/run-druid broker "$config" +start bin/run-druid router "$config" +start bin/run-druid historical "$config" +start bin/run-druid middleManager "$config" + +set +e +wait -n "${pids[@]}" +status=$? +set -e + +# A service exiting normally still means the container is no longer healthy. +if [[ $status -eq 0 ]]; then + status=1 +fi +exit "$status" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index fabfe98e..16b2f190 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -21,6 +21,7 @@ members = [ "clickhouse", "databricks", "datafusion", + "druid", "duckdb/*", "exasol", "flightsql/*", diff --git a/rust/README.md b/rust/README.md index 6ef888b6..f2367d38 100644 --- a/rust/README.md +++ b/rust/README.md @@ -32,6 +32,7 @@ Simple Rust examples showing how to use ADBC to connect, run a query, and return - [ClickHouse](./clickhouse) - [Databricks](./databricks) - [DataFusion](./datafusion) +- [Apache Druid](./druid) - [DuckDB-compatible systems](./duckdb) - [DuckDB](./duckdb/duckdb) - [MotherDuck](./duckdb/motherduck) diff --git a/rust/druid/Cargo.toml b/rust/druid/Cargo.toml new file mode 100644 index 00000000..7ba19acc --- /dev/null +++ b/rust/druid/Cargo.toml @@ -0,0 +1,24 @@ +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +[package] +name = "druid" +version = "0.1.0" +edition = "2024" + +[dependencies] +adbc_core = "0.21.0" +adbc_driver_manager = "0.21.0" +arrow = { version = "57.0.0", features = ["prettyprint"] } +arrow-array = "57.0.0" diff --git a/rust/druid/README.md b/rust/druid/README.md new file mode 100644 index 00000000..3cba0bdb --- /dev/null +++ b/rust/druid/README.md @@ -0,0 +1,79 @@ + + +# Connecting Rust and Apache Druid with ADBC + +## Instructions + +> [!TIP] +> If you already have a Druid instance running, skip the steps to set up Druid. + +### Prerequisites + +1. [Install Rust](https://www.rust-lang.org/tools/install) + +1. [Install dbc](https://docs.columnar.tech/dbc/getting_started/installation/) + +### Set up Druid + +1. [Install Docker](https://docs.docker.com/get-started/get-docker/) + +1. Start a Druid 37 nano-quickstart instance: + + ```sh + docker run --detach --rm \ + --name druid \ + --platform linux/amd64 \ + --publish 8888:8888 \ + --volume "$PWD/start-druid.sh:/opt/druid/start-druid.sh:ro" \ + --entrypoint /bin/bash \ + apache/druid:37.0.0 /opt/druid/start-druid.sh + ``` + +1. Wait for Druid to accept SQL queries: + + ```sh + until curl --fail --silent --output /dev/null \ + --header 'Content-Type: application/json' \ + --data '{"query":"SELECT 1"}' \ + http://localhost:8888/druid/v2/sql; do sleep 2; done + ``` + +### Connect to Druid + +1. Install the Druid ADBC driver: + + ```sh + dbc install --pre druid + ``` + +1. Customize `src/main.rs` as needed + - Change the connection arguments in `opts` + - Format `OptionDatabase::Uri` according to the [driver documentation](https://docs.adbc-drivers.org/drivers/druid/index.html#connecting), or keep it as is + +1. Run the Rust program: + + ```sh + cargo run + ``` + +### Clean up + +1. Stop the Docker container running Druid: + + ```sh + docker stop druid + ``` diff --git a/rust/druid/src/main.rs b/rust/druid/src/main.rs new file mode 100644 index 00000000..bdee49f9 --- /dev/null +++ b/rust/druid/src/main.rs @@ -0,0 +1,56 @@ +// Copyright 2026 Columnar Technologies Inc. +// +// Licensed 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. + +use adbc_core::options::{AdbcVersion, OptionDatabase}; +use adbc_core::{Connection, Database, Driver, LOAD_FLAG_DEFAULT, Statement}; +use adbc_driver_manager::ManagedDriver; +use arrow::util::pretty; +use arrow_array::RecordBatch; + +fn main() { + let mut driver = ManagedDriver::load_from_name( + "druid", + None, + AdbcVersion::default(), + LOAD_FLAG_DEFAULT, + None, + ) + .expect("Failed to load driver"); + + let opts = [( + OptionDatabase::Uri, + "druid://localhost:8888?tls=false".into(), + )]; + let db = driver + .new_database_with_opts(opts) + .expect("Failed to create database handle"); + + let mut conn = db.new_connection().expect("Failed to create connection"); + + let mut statement: adbc_driver_manager::ManagedStatement = conn.new_statement().unwrap(); + statement + .set_sql_query( + " + SELECT \"server\", server_type, tier, curr_size, max_size + FROM sys.servers + ORDER BY server_type, \"server\" + LIMIT 10 + ", + ) + .unwrap(); + let reader = statement.execute().unwrap(); + let batches: Vec = reader.collect::>().unwrap(); + + pretty::print_batches(&batches).expect("Failed to print batches"); +} diff --git a/rust/druid/start-druid.sh b/rust/druid/start-druid.sh new file mode 100644 index 00000000..576fd7c8 --- /dev/null +++ b/rust/druid/start-druid.sh @@ -0,0 +1,50 @@ +#!/bin/bash +# Copyright 2026 Columnar Technologies Inc. +# +# Licensed 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. + +set -euo pipefail + +readonly config="conf/druid/single-server/nano-quickstart" +pids=() + +start() { + "$@" & + pids+=("$!") +} + +shutdown() { + trap - EXIT INT TERM + kill -TERM "${pids[@]}" 2>/dev/null || true + wait "${pids[@]}" 2>/dev/null || true +} + +trap shutdown EXIT INT TERM + +start bin/run-zk conf +start bin/run-druid coordinator-overlord "$config" +start bin/run-druid broker "$config" +start bin/run-druid router "$config" +start bin/run-druid historical "$config" +start bin/run-druid middleManager "$config" + +set +e +wait -n "${pids[@]}" +status=$? +set -e + +# A service exiting normally still means the container is no longer healthy. +if [[ $status -eq 0 ]]; then + status=1 +fi +exit "$status"