027 db transaction pattern

✓ Passing This code compiles and runs correctly.

Code

// TEST: Database transaction pattern with layered obligations
// STATUS: IMPLEMENTED
//
// Real-world pattern: connect → query → (commit OR rollback) → disconnect
//
// Two obligations that must be satisfied:
//   1. connected! on Connection - must call disconnect
//   2. transaction! on Transaction - must call commit OR rollback
//
// This tests that:
// - Multiple obligations can coexist
// - Each branch (then/else) must handle the transaction obligation
// - Connection obligation auto-discharged at terminal in both branches

~import "$std/control"
~import "$app/db"

const query_succeeded = true;

~app.db:connect(url: "postgres://localhost/test")
| connected c |>
    app.db:query(conn: c.conn, sql: "SELECT * FROM users")
    | started tx |>
        std.control:if(query_succeeded)
        | then |>
            app.db:commit(tx: tx.tx)
            | committed |>
                app.db:disconnect(conn: c.conn)
                | disconnected |> _
        | else |>
            app.db:rollback(tx: tx.tx)
            | rolledback |>
                app.db:disconnect(conn: c.conn)
                | disconnected |> _

pub fn main() void {}
input.kz

Imported Files

// Library module: db
// Database operations with layered cleanup obligations
//
// Pattern: connect → query → (commit OR rollback) → disconnect
// Two obligations:
//   1. connected! on Connection - must call disconnect
//   2. transaction! on Transaction - must call commit or rollback

const std = @import("std");

const Connection = struct {
    id: i32,
};

const Transaction = struct {
    conn: *Connection,
};

// Connect to database - creates connected! obligation
~pub event connect { url: []const u8 }
| connected { conn: *Connection[connected!] }

~proc connect {
    std.debug.print("Connecting to: {s}\n", .{url});
    const allocator = std.heap.page_allocator;
    const c = allocator.create(Connection) catch unreachable;
    c.* = Connection{ .id = 1 };
    return .{ .connected = .{ .conn = c } };
}

// Start a query/transaction - creates transaction! obligation
~pub event query { conn: *Connection[connected!], sql: []const u8 }
| started { tx: *Transaction[transaction!] }

~proc query {
    std.debug.print("Query: {s}\n", .{sql});
    const allocator = std.heap.page_allocator;
    const t = allocator.create(Transaction) catch unreachable;
    t.* = Transaction{ .conn = conn };
    return .{ .started = .{ .tx = t } };
}

// Commit - CONSUMES transaction! obligation
~pub event commit { tx: *Transaction[!transaction] }
| committed {}

~proc commit {
    std.debug.print("Committing transaction\n", .{});
    return .{ .committed = .{} };
}

// Rollback - CONSUMES transaction! obligation (alternative to commit)
~pub event rollback { tx: *Transaction[!transaction] }
| rolledback {}

~proc rollback {
    std.debug.print("Rolling back transaction\n", .{});
    return .{ .rolledback = .{} };
}

// Disconnect - CONSUMES connected! obligation
~pub event disconnect { conn: *Connection[!connected] }
| disconnected {}

~proc disconnect {
    std.debug.print("Disconnecting\n", .{});
    return .{ .disconnected = .{} };
}
db.kz