Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/data/databases.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
"name": "DataFusion",
"parent": null
},
"druid": {
"name": "Apache Druid",
"parent": null
},
"doris": {
"name": "Apache Doris",
"parent": "flightsql"
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions cpp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
43 changes: 43 additions & 0 deletions cpp/druid/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)
24 changes: 24 additions & 0 deletions cpp/druid/Makefile
Original file line number Diff line number Diff line change
@@ -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
101 changes: 101 additions & 0 deletions cpp/druid/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<!--
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.
-->

# 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
```
113 changes: 113 additions & 0 deletions cpp/druid/main.cpp
Original file line number Diff line number Diff line change
@@ -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 <cstdlib>
// For strerror
#include <cstring>
#include <iostream>

#include <arrow-adbc/adbc.h>
#include <arrow-adbc/adbc_driver_manager.h>
#include <arrow/c/bridge.h>
#include <arrow/record_batch.h>

// 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;
}
12 changes: 12 additions & 0 deletions cpp/druid/pixi.toml
Original file line number Diff line number Diff line change
@@ -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"
50 changes: 50 additions & 0 deletions cpp/druid/start-druid.sh
Original file line number Diff line number Diff line change
@@ -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"
1 change: 1 addition & 0 deletions csharp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading