Fix: use getDatumCString for null-terminated slice type
I'm not sure if this is a good way to do it.
For certain functions, for example, the in function on a custom type requires passing in the value as a cstring. This feels like a good way to abstract that specific functionality.
Unfortunately this change would be a breaking change. The reason is that we used to handle Text using that type, which we still might need to detoast.
The pgzx.datum module allows a type to be a converter like so:
struct CString {
const Type = []const u8,
pub fn fromNullableDatum(d: c.NullableDatum) !Type { ... }
pub fn toNullableDatum(v: Type) !c.NullableDatum { ... }
}
I'm actually thinking that we should add those named 'converters' and export them from the pgzx.datum module.
Capturing the function call info the converter should be usable as is. But if we want a more 'declarative' approach for custom functions we have spend some more thaught.
Unfortunately we can't use that converter directly with fmgr like so:
const pgzx = @import(...);
const pg = pgzx.c;
comptime {
pgzx.PG_FUNCTION_V1(my_func, "my_func");
}
fn my_func(str pgzx.datum.CString) !pgzx.datum.CString {
...
}
This is because CString != CString.Type. Maybe we need some help from the pgzx.fmgr.args module so we can have a 'container' type that captures custom conversions. Then my_func maybe could be written like so:
fn my_func(str pgzx.Arg(pgzx.datum.CString)) !pgzx.Value(pgzx.datum.CString) {
}
where
pub fn Arg(comptime conv anytype) type {
return struct {
value: conv.Type,
};
}
Another approach would be to make CString self convertible like so:
struct CString {
value: []const u8,
const Type = CString,
pub fn fromNullableDatum(d: c.NullableDatum) !Type { ... }
pub fn toNullableDatum(v: Type) !c.NullableDatum { ... }
}
No you should be able to write my_func like so:
fn my_func(str pgzx.datum.CString) !pgzx.datum.CString {
...
}
But instead of making CString self convertible we could use the pgzx.fmgr.args module to export self convertible "container type" like so:
args.zig:
pub const CString = Arg(datum.CString);
pub const CStringZ = Arg(datum.CStringZ);
...
That is we create the converters and export them from the Datum package. And next we make them available as args. Maybe moving aliasing args under pgzx.args. Now you can use:
fn my_func(str pgzx.arg.CString) !pgzx.arg.CString {
...
}
WDYT? Would an API like this help you?
Ok, yeah, I think the CString type could solve this case for when a C string is required. I can have a look to add that.
My thought with this idea was that if you wanted to deal with the bytea type, then you would be using the []u8, and the sentinel would be for the C string use-case. But I like the CString type idea. It seems very clear about what you will expect to happen.
Closing: When converting SPI arguments we now try to get the OID of the argument and use that specific decoding. We still allow users to capture the argument as a pg.Datum and the pgzx.datum module provides helper functions for different string encodings. We eventually might decide to introduce more custom types in the future.