Remote DuckDB Quack protocol client.
The public API is backed by DBConnection so it can grow into an Ecto adapter
without changing the lower-level protocol codec.
Summary
Functions
Streams query results as column-oriented batches.
Runs a query and returns a QuackDB.Columns struct.
Streams query results as QuackDB.Columns batches.
Runs a query and returns its result as a column-oriented map.
Appends column-oriented values to a DuckDB table through Quack's native append protocol.
Appends row-oriented values to a DuckDB table through Quack's native append protocol.
Appends an enumerable of row maps/keywords in batches through native append.
Appends any Table.Reader compatible tabular data through native append.
Types
Functions
@spec child_spec([start_option()]) :: Supervisor.child_spec()
@spec column_batches(DBConnection.conn(), iodata(), [term()], Keyword.t()) :: Enumerable.t()
Streams query results as column-oriented batches.
Each item is a map from disambiguated column names to the values in that fetch
batch. This keeps large analytical results vector-shaped without materializing
the whole result set. Prefer columnar_batches/4 when you also need batch
metadata.
@spec columnar(DBConnection.conn(), iodata(), [term()], Keyword.t()) :: {:ok, QuackDB.Columns.t()} | {:error, Exception.t()}
Runs a query and returns a QuackDB.Columns struct.
This preserves column order, original names, row count, and result metadata in addition to the column vectors.
@spec columnar!(DBConnection.conn(), iodata(), [term()], Keyword.t()) :: QuackDB.Columns.t()
@spec columnar_batches(DBConnection.conn(), iodata(), [term()], Keyword.t()) :: Enumerable.t()
Streams query results as QuackDB.Columns batches.
This uses a columnar cursor path so large analytical results can stay vector-shaped instead of being materialized as row lists first.
@spec columns(DBConnection.conn(), iodata(), [term()], Keyword.t()) :: {:ok, %{required(String.t()) => [term()]}} | {:error, Exception.t()}
Runs a query and returns its result as a column-oriented map.
Duplicate column names are disambiguated with suffixes such as _2 and _3.
Prefer columnar/4 when you also need column order and result metadata.
@spec insert_columns( DBConnection.conn(), String.t() | atom(), [insert_column()], Keyword.t() ) :: {:ok, QuackDB.Result.t()} | {:error, Exception.t()}
Appends column-oriented values to a DuckDB table through Quack's native append protocol.
Column values are provided as {name, values} pairs. All columns must have the
same row count. Pass :columns with type specs when values are empty, contain
only nils, or need a specific nested DuckDB type. Explicit MAP columns accept
ordinary Elixir maps and DuckDB-style key/value entries.
@spec insert_columns!( DBConnection.conn(), String.t() | atom(), [insert_column()], Keyword.t() ) :: QuackDB.Result.t()
@spec insert_rows( DBConnection.conn(), String.t() | atom(), [insert_row()], Keyword.t() ) :: {:ok, QuackDB.Result.t()} | {:error, Exception.t()}
Appends row-oriented values to a DuckDB table through Quack's native append protocol.
Rows are maps or keywords. Pass :columns with type specs when values are empty,
contain only nils, or need a specific nested DuckDB type.
Plain Elixir maps infer as DuckDB STRUCT values. For explicit
{:map, key_type, value_type} columns, ordinary Elixir maps are encoded as
DuckDB MAP values:
QuackDB.insert_rows!(conn, "events", [[labels: %{env: "prod"}]],
columns: [labels: {:map, :varchar, :varchar}]
)DuckDB-style key/value entries are also accepted for explicit MAP columns:
QuackDB.insert_rows!(conn, "events", [[labels: [%{key: "env", value: "prod"}]]],
columns: [labels: {:map, :varchar, :varchar}]
)Duplicate MAP keys decode with the later entry winning, matching Map.put/3.
Keys and values are encoded through the declared DuckDB key/value types; for
example, atom keys in {:map, :varchar, :varchar} columns become strings.
@spec insert_rows!( DBConnection.conn(), String.t() | atom(), [insert_row()], Keyword.t() ) :: QuackDB.Result.t()
@spec insert_stream( DBConnection.conn(), String.t() | atom(), Enumerable.t(), Keyword.t() ) :: {:ok, QuackDB.Result.t()} | {:error, Exception.t()}
Appends an enumerable of row maps/keywords in batches through native append.
@spec insert_stream!( DBConnection.conn(), String.t() | atom(), Enumerable.t(), Keyword.t() ) :: QuackDB.Result.t() | nil
Appends any Table.Reader compatible tabular data through native append.
@spec maps(DBConnection.conn(), iodata(), [term()], Keyword.t()) :: Enumerable.t()
@spec ping(DBConnection.conn(), Keyword.t()) :: :ok | {:error, Exception.t()}
@spec prepare(DBConnection.conn(), iodata(), Keyword.t()) :: {:ok, QuackDB.Query.t()} | {:error, Exception.t()}
@spec prepare_execute(DBConnection.conn(), iodata(), [term()], Keyword.t()) :: {:ok, QuackDB.Query.t(), QuackDB.Result.t()} | {:error, Exception.t()}
@spec query(DBConnection.conn(), iodata(), [term()], Keyword.t()) :: {:ok, QuackDB.Result.t()} | {:error, Exception.t()}
@spec query!(DBConnection.conn(), iodata(), [term()], Keyword.t()) :: QuackDB.Result.t()
@spec rows(DBConnection.conn(), iodata(), [term()], Keyword.t()) :: Enumerable.t()
@spec start_link([start_option()]) :: GenServer.on_start()
@spec stream(DBConnection.conn(), iodata(), [term()], Keyword.t()) :: QuackDB.Stream.t()