DBI Database Interface
A Perl DBI-like interface for database operations in Strada.
Overview
DBI provides a unified interface for working with databases in Strada. It supports multiple database backends:
SQLite
Lightweight, file-based database. No server required.
Link with: -lsqlite3
MySQL / MariaDB
Popular open-source relational database.
Link with: -lmysqlclient
PostgreSQL
Advanced open-source object-relational database.
Link with: -lpq
Quick Start
use lib "lib";
use DBI;
# Connect to SQLite
my scalar $dbh = DBI::connect("dbi:SQLite:myapp.db", "", "");
# Create table
DBI::exec($dbh, "CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT
)", []);
# Insert with placeholders (safe from SQL injection)
DBI::exec($dbh, "INSERT INTO users (name, email) VALUES (?, ?)",
["Alice", "alice@example.com"]);
# Query all rows
my scalar $users = DBI::selectall_hashref($dbh, "SELECT * FROM users", []);
foreach my scalar $user (@{$users}) {
say($user->{"name"} . " <" . $user->{"email"} . ">");
}
# Disconnect
DBI::disconnect($dbh);
DSN Formats
The Data Source Name (DSN) specifies which database to connect to:
| Database | DSN Format | Example |
|---|---|---|
| SQLite | dbi:SQLite:filename |
dbi:SQLite:app.db |
| SQLite (path) | dbi:SQLite:dbname=/path/to/db |
dbi:SQLite:dbname=/var/data/app.db |
| MySQL | dbi:mysql:database=name;host=host;port=port |
dbi:mysql:database=myapp;host=localhost;port=3306 |
| PostgreSQL | dbi:Pg:dbname=name;host=host;port=port |
dbi:Pg:dbname=myapp;host=localhost;port=5432 |
Connection Functions
connect()
Connect to a database. Returns a database handle or undef on failure.
my scalar $dbh = DBI::connect($dsn, $username, $password);
# SQLite (no username/password needed)
my scalar $dbh = DBI::connect("dbi:SQLite:app.db", "", "");
# MySQL
my scalar $dbh = DBI::connect(
"dbi:mysql:database=myapp;host=localhost",
"root", "password");
connect_attrs()
Connect with additional attributes:
my scalar $dbh = DBI::connect_attrs($dsn, $user, $pass, {
"AutoCommit" => 1, # Auto-commit after each statement
"PrintError" => 1, # Print errors to stderr
"RaiseError" => 1 # Throw exceptions on errors
});
disconnect()
Close the database connection:
DBI::disconnect($dbh);
ping()
Check if connection is still alive:
if (DBI::ping($dbh)) {
say("Connection is alive");
}
Query Execution
exec()
Execute a SQL statement with parameters. Returns the number of affected rows.
# Insert
DBI::exec($dbh, "INSERT INTO users (name) VALUES (?)", ["Bob"]);
# Update
my int $rows = DBI::exec($dbh,
"UPDATE users SET active = ? WHERE id = ?", [1, 42]);
say("Updated " . $rows . " rows");
# Delete
DBI::exec($dbh, "DELETE FROM sessions WHERE expired = ?", [1]);
do_sql()
Execute SQL without parameters (for DDL statements):
DBI::do_sql($dbh, "CREATE TABLE posts (id INTEGER PRIMARY KEY, title TEXT)");
DBI::do_sql($dbh, "DROP TABLE IF EXISTS temp_data");
prepare() / execute()
For repeated queries, prepare once and execute multiple times:
my scalar $sth = DBI::prepare($dbh, "INSERT INTO logs (msg) VALUES (?)");
foreach my str $msg (@messages) {
DBI::execute($sth, [$msg]);
}
DBI::finish($sth);
Fetching Results
selectall_hashref()
Execute query and return all rows as an array of hash references. This is the most common way to fetch results.
my scalar $users = DBI::selectall_hashref($dbh,
"SELECT * FROM users WHERE active = ?", [1]);
foreach my scalar $user (@{$users}) {
say($user->{"id"} . ": " . $user->{"name"});
}
selectrow_hashref()
Execute query and return first row as a hash reference:
my scalar $user = DBI::selectrow_hashref($dbh,
"SELECT * FROM users WHERE id = ?", [42]);
if (defined($user)) {
say("Found: " . $user->{"name"});
} else {
say("User not found");
}
selectcol()
Execute query and return first column of first row (useful for COUNT, MAX, etc.):
my int $count = DBI::selectcol($dbh, "SELECT COUNT(*) FROM users", []);
say("Total users: " . $count);
my str $name = DBI::selectcol($dbh,
"SELECT name FROM users WHERE id = ?", [1]);
selectall_arrayref()
Execute query and return all rows as an array of array references:
my scalar $rows = DBI::selectall_arrayref($dbh,
"SELECT id, name FROM users");
foreach my scalar $row (@{$rows}) {
say($row->[0] . ": " . $row->[1]); # id: name
}
Row-by-Row Fetching
For large result sets, fetch one row at a time:
my scalar $sth = DBI::prepare($dbh, "SELECT * FROM large_table");
DBI::execute($sth, []);
while (my scalar $row = DBI::fetchrow_hashref($sth)) {
say($row->{"name"});
}
DBI::finish($sth);
Insert and Get ID
insert_get_id()
Insert a row and return the auto-generated primary key:
my int $id = DBI::insert_get_id($dbh,
"INSERT INTO users (name, email) VALUES (?, ?)",
["Charlie", "charlie@example.com"]);
say("Created user with ID: " . $id);
Transactions
Use transactions to group multiple operations atomically:
# Begin transaction
DBI::begin_work($dbh);
try {
# Transfer money between accounts
DBI::exec($dbh,
"UPDATE accounts SET balance = balance - ? WHERE id = ?",
[100, $from_id]);
DBI::exec($dbh,
"UPDATE accounts SET balance = balance + ? WHERE id = ?",
[100, $to_id]);
# Commit if successful
DBI::commit($dbh);
say("Transfer complete");
} catch ($e) {
# Rollback on error
DBI::rollback($dbh);
say("Transfer failed: " . $e);
}
Error Handling
RaiseError Mode
Enable exceptions on database errors:
DBI::set_raise_error($dbh, 1);
try {
DBI::exec($dbh, "INSERT INTO users (name) VALUES (?)", [$name]);
} catch ($e) {
say("Database error: " . $e);
}
Manual Error Checking
my str $error = DBI::errstr($dbh);
if (length($error) > 0) {
say("Error: " . $error);
}
my int $errcode = DBI::err($dbh);
Utility Functions
quote()
Quote a string value for safe use in SQL (when placeholders aren't available):
my str $safe = DBI::quote($dbh, "O'Brien");
# Returns: 'O''Brien'
column_names()
Get column names from a prepared statement:
my scalar $sth = DBI::prepare($dbh, "SELECT * FROM users");
DBI::execute($sth, []);
my scalar $cols = DBI::column_names($sth);
# $cols = ["id", "name", "email", ...]
rows()
Get number of rows affected by last statement:
my int $affected = DBI::rows($sth);
Complete Example
use lib "lib";
use DBI;
func main() int {
# Connect to database
my scalar $dbh = DBI::connect("dbi:SQLite:blog.db", "", "");
if (!defined($dbh)) {
say("Failed to connect");
return 1;
}
# Create tables
DBI::do_sql($dbh, "CREATE TABLE IF NOT EXISTS posts (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
body TEXT,
created_at INTEGER
)");
# Insert a post
my int $id = DBI::insert_get_id($dbh,
"INSERT INTO posts (title, body, created_at) VALUES (?, ?, ?)",
["Hello World", "My first blog post!", core::time()]);
say("Created post ID: " . $id);
# Query all posts
my scalar $posts = DBI::selectall_hashref($dbh,
"SELECT * FROM posts ORDER BY created_at DESC", []);
say("All posts:");
foreach my scalar $post (@{$posts}) {
say(" [" . $post->{"id"} . "] " . $post->{"title"});
}
# Count posts
my int $count = DBI::selectcol($dbh, "SELECT COUNT(*) FROM posts", []);
say("Total posts: " . $count);
# Disconnect
DBI::disconnect($dbh);
return 0;
}
Function Reference
| Category | Function | Description |
|---|---|---|
| Connection | connect($dsn, $user, $pass) | Connect to database |
connect_attrs($dsn, $user, $pass, $attrs) | Connect with attributes | |
disconnect($dbh) | Close connection | |
ping($dbh) | Check if connected | |
| Execution | exec($dbh, $sql, $params) | Execute with params |
do_sql($dbh, $sql) | Execute without params | |
prepare($dbh, $sql) | Prepare statement | |
execute($sth, $params) | Execute prepared | |
| Fetching | selectall_hashref($dbh, $sql, $params) | All rows as hashes |
selectrow_hashref($dbh, $sql, $params) | First row as hash | |
selectcol($dbh, $sql, $params) | First column value | |
fetchrow_hashref($sth) | Next row as hash | |
fetchrow_array($sth) | Next row as array | |
| Transactions | begin_work($dbh) | Begin transaction |
commit($dbh) | Commit transaction | |
rollback($dbh) | Rollback transaction | |
| Utility | insert_get_id($dbh, $sql, $params) | Insert and get ID |
quote($dbh, $value) | Quote string value | |
errstr($dbh) | Last error message | |
err($dbh) | Last error code | |
rows($sth) | Affected row count |