feat: add PostgreSQL driver with Postgres effect
Implements full PostgreSQL support through the Postgres effect: - connect(connStr): Connect to PostgreSQL database - close(conn): Close connection - execute(conn, sql): Execute INSERT/UPDATE/DELETE, return affected rows - query(conn, sql): Execute SELECT, return all rows as records - queryOne(conn, sql): Execute SELECT, return first row as Option - beginTx(conn): Start transaction - commit(conn): Commit transaction - rollback(conn): Rollback transaction Includes: - Connection tracking with connection IDs - Row mapping to Lux records with field access - Transaction support - Example: examples/postgres_demo.lux - Documentation in docs/guide/11-databases.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
185
examples/postgres_demo.lux
Normal file
185
examples/postgres_demo.lux
Normal file
@@ -0,0 +1,185 @@
|
||||
// PostgreSQL Database Example
|
||||
//
|
||||
// Demonstrates the Postgres effect for database operations.
|
||||
//
|
||||
// Prerequisites:
|
||||
// - PostgreSQL server running locally
|
||||
// - Database 'testdb' created
|
||||
// - User 'testuser' with password 'testpass'
|
||||
//
|
||||
// To set up:
|
||||
// createdb testdb
|
||||
// psql testdb -c "CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT, email TEXT);"
|
||||
//
|
||||
// Run with: lux examples/postgres_demo.lux
|
||||
|
||||
// ============================================================
|
||||
// Helper Functions
|
||||
// ============================================================
|
||||
|
||||
fn jsonStr(key: String, value: String): String =
|
||||
"\"" + key + "\":\"" + value + "\""
|
||||
|
||||
fn jsonNum(key: String, value: Int): String =
|
||||
"\"" + key + "\":" + toString(value)
|
||||
|
||||
fn jsonObj(content: String): String =
|
||||
"{" + content + "}"
|
||||
|
||||
// ============================================================
|
||||
// Database Operations
|
||||
// ============================================================
|
||||
|
||||
// Insert a user
|
||||
fn insertUser(connId: Int, name: String, email: String): Int with {Console, Postgres} = {
|
||||
let sql = "INSERT INTO users (name, email) VALUES ('" + name + "', '" + email + "') RETURNING id"
|
||||
Console.print("Inserting user: " + name)
|
||||
match Postgres.queryOne(connId, sql) {
|
||||
Some(row) => {
|
||||
Console.print(" Inserted with ID: " + toString(row.id))
|
||||
row.id
|
||||
},
|
||||
None => {
|
||||
Console.print(" Insert failed")
|
||||
-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get all users
|
||||
fn getUsers(connId: Int): Unit with {Console, Postgres} = {
|
||||
Console.print("Fetching all users...")
|
||||
let rows = Postgres.query(connId, "SELECT id, name, email FROM users ORDER BY id")
|
||||
Console.print(" Found " + toString(List.length(rows)) + " users:")
|
||||
List.forEach(rows, fn(row: { id: Int, name: String, email: String }): Unit with {Console} => {
|
||||
Console.print(" - " + toString(row.id) + ": " + row.name + " <" + row.email + ">")
|
||||
})
|
||||
}
|
||||
|
||||
// Get user by ID
|
||||
fn getUserById(connId: Int, id: Int): Unit with {Console, Postgres} = {
|
||||
let sql = "SELECT id, name, email FROM users WHERE id = " + toString(id)
|
||||
Console.print("Looking up user " + toString(id) + "...")
|
||||
match Postgres.queryOne(connId, sql) {
|
||||
Some(row) => Console.print(" Found: " + row.name + " <" + row.email + ">"),
|
||||
None => Console.print(" User not found")
|
||||
}
|
||||
}
|
||||
|
||||
// Update user email
|
||||
fn updateUserEmail(connId: Int, id: Int, newEmail: String): Unit with {Console, Postgres} = {
|
||||
let sql = "UPDATE users SET email = '" + newEmail + "' WHERE id = " + toString(id)
|
||||
Console.print("Updating user " + toString(id) + " email to " + newEmail)
|
||||
let affected = Postgres.execute(connId, sql)
|
||||
Console.print(" Rows affected: " + toString(affected))
|
||||
}
|
||||
|
||||
// Delete user
|
||||
fn deleteUser(connId: Int, id: Int): Unit with {Console, Postgres} = {
|
||||
let sql = "DELETE FROM users WHERE id = " + toString(id)
|
||||
Console.print("Deleting user " + toString(id))
|
||||
let affected = Postgres.execute(connId, sql)
|
||||
Console.print(" Rows affected: " + toString(affected))
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Transaction Example
|
||||
// ============================================================
|
||||
|
||||
fn transactionDemo(connId: Int): Unit with {Console, Postgres} = {
|
||||
Console.print("")
|
||||
Console.print("=== Transaction Demo ===")
|
||||
|
||||
// Start transaction
|
||||
Console.print("Beginning transaction...")
|
||||
Postgres.beginTx(connId)
|
||||
|
||||
// Make some changes
|
||||
insertUser(connId, "TxUser1", "tx1@example.com")
|
||||
insertUser(connId, "TxUser2", "tx2@example.com")
|
||||
|
||||
// Show users before commit
|
||||
Console.print("Users before commit:")
|
||||
getUsers(connId)
|
||||
|
||||
// Commit the transaction
|
||||
Console.print("Committing transaction...")
|
||||
Postgres.commit(connId)
|
||||
|
||||
Console.print("Transaction committed!")
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Main
|
||||
// ============================================================
|
||||
|
||||
fn main(): Unit with {Console, Postgres} = {
|
||||
Console.print("========================================")
|
||||
Console.print(" PostgreSQL Demo")
|
||||
Console.print("========================================")
|
||||
Console.print("")
|
||||
|
||||
// Connect to database
|
||||
Console.print("Connecting to PostgreSQL...")
|
||||
let connStr = "host=localhost user=testuser password=testpass dbname=testdb"
|
||||
let connId = Postgres.connect(connStr)
|
||||
Console.print("Connected! Connection ID: " + toString(connId))
|
||||
Console.print("")
|
||||
|
||||
// Create table if not exists
|
||||
Console.print("Creating users table...")
|
||||
Postgres.execute(connId, "CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT NOT NULL)")
|
||||
Console.print("")
|
||||
|
||||
// Clear table for demo
|
||||
Console.print("Clearing existing data...")
|
||||
Postgres.execute(connId, "DELETE FROM users")
|
||||
Console.print("")
|
||||
|
||||
// Insert some users
|
||||
Console.print("=== Inserting Users ===")
|
||||
let id1 = insertUser(connId, "Alice", "alice@example.com")
|
||||
let id2 = insertUser(connId, "Bob", "bob@example.com")
|
||||
let id3 = insertUser(connId, "Charlie", "charlie@example.com")
|
||||
Console.print("")
|
||||
|
||||
// Query all users
|
||||
Console.print("=== All Users ===")
|
||||
getUsers(connId)
|
||||
Console.print("")
|
||||
|
||||
// Query single user
|
||||
Console.print("=== Single User Lookup ===")
|
||||
getUserById(connId, id2)
|
||||
Console.print("")
|
||||
|
||||
// Update user
|
||||
Console.print("=== Update User ===")
|
||||
updateUserEmail(connId, id2, "bob.new@example.com")
|
||||
getUserById(connId, id2)
|
||||
Console.print("")
|
||||
|
||||
// Delete user
|
||||
Console.print("=== Delete User ===")
|
||||
deleteUser(connId, id3)
|
||||
getUsers(connId)
|
||||
Console.print("")
|
||||
|
||||
// Transaction demo
|
||||
transactionDemo(connId)
|
||||
Console.print("")
|
||||
|
||||
// Final state
|
||||
Console.print("=== Final State ===")
|
||||
getUsers(connId)
|
||||
Console.print("")
|
||||
|
||||
// Close connection
|
||||
Console.print("Closing connection...")
|
||||
Postgres.close(connId)
|
||||
Console.print("Done!")
|
||||
}
|
||||
|
||||
// Note: This will fail if PostgreSQL is not running
|
||||
// To test the syntax only, you can comment out the last line
|
||||
let output = run main() with {}
|
||||
Reference in New Issue
Block a user