ducky
Native DuckDB driver for Gleam.
Quick Start
import ducky
import gleam/int
import gleam/io
pub fn main() {
use conn <- ducky.with_connection(":memory:")
// Create our duck pond
let assert Ok(_) = ducky.query(conn, "
CREATE TABLE ducks (name TEXT, quack_volume INT, is_rubber BOOLEAN)
")
let assert Ok(_) = ducky.query(conn, "
INSERT INTO ducks VALUES
('Sir Quacksalot', 95, false),
('Duck Norris', 100, false),
('Mallard Fillmore', 72, false),
('Squeaky', 0, true)
")
// Find the loudest quacker
let assert Ok(result) = ducky.query(conn, "
SELECT name, quack_volume FROM ducks
WHERE is_rubber = false
ORDER BY quack_volume DESC LIMIT 1
")
case result.rows {
[ducky.Row([ducky.Text(name), ducky.Integer(volume)])] ->
io.println(name <> " wins at " <> int.to_string(volume) <> " decibels!")
_ -> io.println("The pond is empty...")
}
}
// => Duck Norris wins at 100 decibels!
Types
An opaque connection to a DuckDB database.
pub opaque type Connection
Errors that can occur during DuckDB operations.
pub type Error {
ConnectionFailed(reason: String)
QuerySyntaxError(message: String)
Timeout(duration_ms: Int)
TypeMismatch(expected: String, got: String)
UnsupportedParameterType(type_name: String)
DatabaseError(message: String)
}
Constructors
-
ConnectionFailed(reason: String)Connection to database failed.
-
QuerySyntaxError(message: String)SQL query has syntax errors.
-
Timeout(duration_ms: Int)Operation timed out.
-
TypeMismatch(expected: String, got: String)Type conversion failed.
-
UnsupportedParameterType(type_name: String)Unsupported parameter type in query.
-
DatabaseError(message: String)Generic error from DuckDB.
A value from a DuckDB result set.
pub type Value {
Null
Boolean(Bool)
TinyInt(Int)
SmallInt(Int)
Integer(Int)
BigInt(Int)
Float(Float)
Double(Float)
Decimal(String)
Text(String)
Blob(BitArray)
Timestamp(Int)
Date(Int)
Time(Int)
Interval(months: Int, days: Int, nanos: Int)
List(List(Value))
Array(List(Value))
Map(dict.Dict(String, Value))
Struct(dict.Dict(String, Value))
}
Constructors
Values
pub fn close(conn: Connection) -> Result(Nil, Error)
Closes a database connection.
Examples
let assert Ok(conn) = connect(":memory:")
let assert Ok(_) = close(conn)
pub fn connect(path: String) -> Result(Connection, Error)
Opens a connection to a DuckDB database.
Must call close() when done. Use with_connection() instead
for automatic cleanup.
Examples
connect(":memory:")
// => Ok(Connection(...))
connect("data.duckdb")
// => Ok(Connection(...))
pub fn field(value: Value, name: String) -> option.Option(Value)
Get a field value from a struct by field name.
Returns None if the value is not a Struct or the field does not exist.
Examples
let person = Struct(dict.from_list([#("name", Text("Alice")), #("age", Integer(30))]))
field(person, "name")
// => Some(Text("Alice"))
field(person, "unknown")
// => None
pub fn get(row: Row, index: Int) -> option.Option(Value)
Get a value from a row by column index.
Examples
let row = Row([Integer(1), Text("Alice")])
get(row, 0)
// => Some(Integer(1))
get(row, 5)
// => None
pub fn query(
conn: Connection,
sql: String,
) -> Result(DataFrame, Error)
Executes a SQL query and returns structured results.
The query runs on a dirty scheduler to avoid blocking the BEAM. Results are loaded into memory. For large datasets, use LIMIT/OFFSET or filter in SQL to reduce memory usage.
Examples
query(conn, "SELECT id, name FROM users WHERE active = true")
// => Ok(DataFrame(columns: ["id", "name"], rows: [...]))
// For large datasets, paginate:
query(conn, "SELECT * FROM users LIMIT 1000 OFFSET 0")
pub fn query_params(
conn: Connection,
sql: String,
params: List(Value),
) -> Result(DataFrame, Error)
Executes a parameterized SQL query with bound parameters to prevent SQL injection.
Examples
query_params(conn, "SELECT * FROM users WHERE id = ? AND age > ?", [
Integer(42),
Integer(18),
])
// => Ok(DataFrame(...))
Security
Always use this function when including user input in queries:
// UNSAFE - SQL injection risk
query(conn, "SELECT * FROM users WHERE name = '" <> user_input <> "'")
// SAFE - parameters are properly escaped
query_params(conn, "SELECT * FROM users WHERE name = ?", [Text(user_input)])
pub fn transaction(
conn: Connection,
callback: fn(Connection) -> Result(a, Error),
) -> Result(a, Error)
Executes operations within a transaction.
Commits on success, rolls back on error.
Examples
transaction(conn, fn(conn) {
use _ <- result.try(query(conn, "UPDATE accounts ..."))
query(conn, "SELECT * FROM accounts")
})
pub fn with_connection(
db_path: String,
callback: fn(Connection) -> Result(a, Error),
) -> Result(a, Error)
Executes operations with automatic connection cleanup.
Connection closes automatically on success or error.
Examples
use conn <- with_connection(":memory:")
query(conn, "SELECT 42")