QuackDB (quackdb v0.4.0)

Copy Markdown View Source

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 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

append_type()

@type append_type() :: QuackDB.Type.spec()

Native append column type specs.

insert_column()

@type insert_column() :: {atom() | String.t(), [term()]}

insert_row()

@type insert_row() :: map() | Keyword.t()

start_option()

@type start_option() ::
  {:uri, String.t()}
  | {:token, String.t()}
  | {:name, GenServer.name()}
  | {:connect_timeout, timeout()}
  | {:receive_timeout, timeout()}
  | {:shutdown_timeout, timeout()}
  | {:mint_options, keyword()}

Functions

child_spec(options)

@spec child_spec([start_option()]) :: Supervisor.child_spec()

column_batches(connection, statement, params \\ [], options \\ [])

@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.

columnar(connection, statement, params \\ [], options \\ [])

@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.

columnar!(connection, statement, params \\ [], options \\ [])

@spec columnar!(DBConnection.conn(), iodata(), [term()], Keyword.t()) ::
  QuackDB.Columns.t()

columnar_batches(connection, statement, params \\ [], options \\ [])

@spec columnar_batches(DBConnection.conn(), iodata(), [term()], Keyword.t()) ::
  Enumerable.t()

Streams query results as QuackDB.Columns batches.

columns(connection, statement, params \\ [], options \\ [])

@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.

columns!(connection, statement, params \\ [], options \\ [])

@spec columns!(DBConnection.conn(), iodata(), [term()], Keyword.t()) :: %{
  required(String.t()) => [term()]
}

insert_columns(connection, table, columns, options \\ [])

@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.

insert_columns!(connection, table, columns, options \\ [])

@spec insert_columns!(
  DBConnection.conn(),
  String.t() | atom(),
  [insert_column()],
  Keyword.t()
) ::
  QuackDB.Result.t()

insert_rows(connection, table, rows, options \\ [])

@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.

insert_rows!(connection, table, rows, options \\ [])

@spec insert_rows!(
  DBConnection.conn(),
  String.t() | atom(),
  [insert_row()],
  Keyword.t()
) ::
  QuackDB.Result.t()

insert_stream(connection, table, rows, options \\ [])

@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.

insert_stream!(connection, table, rows, options \\ [])

@spec insert_stream!(
  DBConnection.conn(),
  String.t() | atom(),
  Enumerable.t(),
  Keyword.t()
) ::
  QuackDB.Result.t() | nil

insert_table(connection, table, tabular, options \\ [])

Appends any Table.Reader compatible tabular data through native append.

insert_table!(connection, table, tabular, options \\ [])

maps(connection, statement, params \\ [], options \\ [])

@spec maps(DBConnection.conn(), iodata(), [term()], Keyword.t()) :: Enumerable.t()

ping(connection, options \\ [])

@spec ping(DBConnection.conn(), Keyword.t()) :: :ok | {:error, Exception.t()}

prepare(connection, statement, options \\ [])

@spec prepare(DBConnection.conn(), iodata(), Keyword.t()) ::
  {:ok, QuackDB.Query.t()} | {:error, Exception.t()}

prepare_execute(connection, statement, params \\ [], options \\ [])

@spec prepare_execute(DBConnection.conn(), iodata(), [term()], Keyword.t()) ::
  {:ok, QuackDB.Query.t(), QuackDB.Result.t()} | {:error, Exception.t()}

query(connection, statement, params \\ [], options \\ [])

@spec query(DBConnection.conn(), iodata(), [term()], Keyword.t()) ::
  {:ok, QuackDB.Result.t()} | {:error, Exception.t()}

query!(connection, statement, params \\ [], options \\ [])

@spec query!(DBConnection.conn(), iodata(), [term()], Keyword.t()) ::
  QuackDB.Result.t()

rows(connection, statement, params \\ [], options \\ [])

@spec rows(DBConnection.conn(), iodata(), [term()], Keyword.t()) :: Enumerable.t()

start_link(options)

@spec start_link([start_option()]) :: GenServer.on_start()

stream(connection, statement, params \\ [], options \\ [])

@spec stream(DBConnection.conn(), iodata(), [term()], Keyword.t()) ::
  QuackDB.Stream.t()