Skip to content

ruslandoga/duxdb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

59 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Experiments with DuckDB in Elixir.

Note

The current API tries to map 1-to-1 to DuckDB C API. This is very verbose and low-level but it enables more idiomatic libraries to be built on top of this one without any performance or feature sacrifice.

For an example of a higher level library, please see DuxDB.Ecto.

Here's how it can be used on its own:

brew install duckdb llvm

duckdb --version
# v1.2.1 8e52ec4395

clang --version
# Homebrew clang version 19.1.7

export CC=$(which clang)
export DUXDB_LDFLAGS=-L/opt/homebrew/opt/duckdb/lib
Mix.install([{:duxdb, github: "ruslandoga/duxdb"}], force: true)

db = DuxDB.open(":memory:", %{"max_memory" => "1GB"})
conn = DuxDB.connect(db)

stmt =
  DuxDB.prepare(conn, """
  SELECT * FROM (VALUES
    (101, 'Hello, ' || $database || '!', today(), -1.0),
    (102, 'Insert a lot of rows per batch', today() - 1, 1.41421),
    (102, 'Sort your data based on your commonly-used queries', today() + 1, 2.718),
    (101, 'Granules are the smallest chunks of data read', $day, $pi)
  ) AS my_first_table(user_id, message, timestamp, metric)
  """)

DuxDB.bind_varchar(stmt, DuxDB.bind_parameter_index(stmt, "database"), "ClickHouse")
DuxDB.bind_double(stmt, DuxDB.bind_parameter_index(stmt, "pi"), 3.14159)
DuxDB.bind_date(stmt, DuxDB.bind_parameter_index(stmt, "day"), ~D[2025-03-14])

result = DuxDB.execute_prepared(stmt)

read_chunks =
  fn result ->
    Stream.repeatedly(fn -> DuxDB.fetch_chunk(result) end)
    |> Stream.take_while(&is_reference/1)
    |> Enum.map(fn chunk ->
      Map.new(0..(DuxDB.column_count(result) - 1), fn i ->
        {DuxDB.column_name(result, i), DuxDB.data_chunk_get_vector(chunk, i)}
      end)
    end)
  end

read_chunks.(result)
# [
#   %{
#     "message" => [
#       "Hello, ClickHouse!",
#       "Insert a lot of rows per batch",
#       "Sort your data based on your commonly-used queries",
#       "Granules are the smallest chunks of data read"
#     ],
#     "metric" => [-1.0, 1.41421, 2.718, 3.14159],
#     "timestamp" => [~D[2024-10-29], ~D[2024-10-28], ~D[2024-10-30], ~D[2025-03-14]],
#     "user_id" => [101, 102, 102, 101]
#   }
# ]

For more examples, please see doctests and tests.

About

DuckDB bindings for Elixir

Resources

Stars

Watchers

Forks