Closed richard-powers closed 5 months ago
Nice !
One small observation - the Result object has a ptr to an internal arena allocator that is lifecycled over the stamement execution.
It would be possible to drop the Allocator param, let the result dupe values using its own arena, and then everything gets cleaned up automatically when the statement is complete (no need to free all those dupes anymore at the callsite)
Will try that out here and see if it works the way I think it does
EDIT - I lie ! It is generally true that the allocator is an arena that the stmt creates. However, there are variants of calling query that let you pass in your own allocator when the query is run, and that is used instead.
Means that you can still drop the alloc param and use the one on the result. If you call query() normally, then the dupes will free when the stmt ends and frees the arena. If you call queryOpts({alloc = myalloc}), then yeah .. the caller is repsonsible for cleaning up the dupes. Still a win !
I'm going to tweak this in a follow up commit.
I guess the fancier follow up would be
1 - Supporting arrays
2 - Adding a map
option to ToOpts
which can either be ordinal
or name
. ordinal
would be the current logic. name
would map based on the field names, using getCol
instead of get
It’s not unusual to have fields in the target struct that you want to skip as well, if they are not in the row
there is a discussion on discord zig-help about providing meta data in the target struct to assist mapping (sort of like go struct tags)
There's now a .{.map = .name}
option. The default is .{.map = .ordinal}
. When using .name
, the column_names = true
option must be specified in the queryOpts
or rowOpts
functions.
Columns with no field equivalent are ignored. Fields with no column equivalent are set to their default value. If there is no default value, to
will return an error.FieldColumnMismatch
.
There's overhead to using .{.map = .name}
, since the name -> index has to be looked up. When done in a loop (when iterating through a result), it's better to create a mapper and iterate through it:
var result = try conn.queryOpts("select....", .{}, .{.column_names = true});
defer result.deinit();
// this takes the same `dupe` and `allocator` opts as `row.to`
var mapper = result.mapper(User, .{});
while (try mapper.next()) |user| {
// you have a user
}
I've been using this function to copy row data into an actual struct, and thought it may be useful to have in the library.
Feel free to modify or request modifications, this is just pulled directly from an internal work project.