karlseguin / pg.zig

Native PostgreSQL driver / client for Zig
MIT License
158 stars 9 forks source link

Consider exporting type pg.Iterator as a public type #18

Closed zigster64 closed 1 month ago

zigster64 commented 1 month ago

See my branch - export_iterator for 3 changes

I wont put up a PR, because its a tiny change, and formatting differences are huge (8 space tab stops on your code ?)

Situation :

I want to create a simple wrapper struct over an SQL statement that gets re-used in many spots. Do not want consumers of the SQL statement to have to fiddle with setting index values, or copypasting custom logic on a per row level.

This is easy enough to do ... until you hit a statement that includes an iterator result type. (because you need an instance of the row do be able to get an iterator ... so you cant easily define a receiver struct that includes an iterator field without have a row)

eg:

const CustomerList = struct {
    pub const Row = struct {
        name: []const u8 = "",
        count: i64 = 0,
        value: f64 = 0,
        unallocated: bool = false,

        pub fn aliases(self: Row, row: pg.Row) pg.Iterator([]const u8) {
            _ = self; // autofix
            return row.iterator([]const u8, 3);
        }
    };

    const select_customer_list =
        \\ SELECT
        \\      LOWER(customer_name) as name,
        \\      COUNT(*) as count,
        \\      SUM(amount) as value,
        \\      ARRAY_AGG(distinct customer_name) AS aliases
        \\ FROM purchase_order
        \\ GROUP BY LOWER(customer_name)
        \\ ORDER BY name
    ;

    pub fn query(self: *CustomerList, db: *pg.Conn) !pg.Result {
        _ = self; // autofix
        const res = db.query(select_customer_list, .{}) catch |err| {
            if (db.err) |pge| {
                logz.err().src(@src()).string("get list of customers from purchase orders", pge.message).log();
            }
            return err;
        };
        return res;
    }

    pub fn map(self: *CustomerList, row: pg.Row) CustomerList.Row {
        _ = self; // autofix
        const name = row.get([]const u8, 0);
        return .{
            .name = if (name.len < 1) Unallocated else name,
            .unallocated = name.len < 1,
            .count = row.get(i64, 1),
            .value = row.get(f64, 2),
        };
    }
};

Consumer use case

    {
        var customers = CustomerList{};
        var query = try customers.query(db);
        defer query.deinit();

        try template.tableStart(w);

        while (try query.next()) |row| {
            const customer = customers.map(row);

            try template.tableRow( .{
                .name = customer.name,
                .count = customer.count,
                .value = customer.value,
            }, w);

            var aliases = customer.aliases(row);   // get an iterator of aliases
            while (aliases.next()) |alias| {
                try template.AliasRow( .{ .alias = alias }, w);
            }
        }
    }

There might be a simple way of doing the same thing without exporting pg.Iterator, but I cant see it yet

karlseguin commented 1 month ago

I don't mind making stuff public :)

Tabs are objectively more accessible. It allows the width to be set by the user, independently of the font-size, which is important for those with poor eyesight. Supposedly, it's much better for users that use braille display. But don't bring it up with Andrew cuz he really hates this topic and feels very strongly that the issue here are editors and that this should be solved by editors.

I used zig fmt in httpz because it was more popular with more contributors.