pg: Add send/recv support for a few more easy types

This commit is contained in:
Yorhel 2025-02-08 14:03:35 +01:00
parent 30b457d2b8
commit 7f1c48e0cf
4 changed files with 222 additions and 25 deletions

View file

@ -547,5 +547,4 @@ static void fupg_st_destroy(fupg_st *st) {
/* TODO: $st->alla, allh, flat, kvv, kva, kvh */
/* TODO: Prepared statement caching */
/* TODO: Binary format bind parameters */
/* TODO: Custom type handling */

View file

@ -103,25 +103,196 @@ SENDFN(int8) {
fustr_write(out, (const char *)&v, 8);
}
RECVFN(uint4) {
RLEN(4);
return newSViv(__builtin_bswap32(*((U32 *)buf)));
}
SENDFN(uint4) {
SIV(0, UINT32_MAX);
U32 v = __builtin_bswap32((U32)iv);
fustr_write(out, (const char *)&v, 4);
}
RECVFN(bytea) {
return newSVpvn(buf, len);
}
SENDFN(bytea) {
STRLEN len;
const char *buf = SvPVbyte(val, len);
fustr_write(out, buf, len);
}
RECVFN(char) {
RLEN(1);
return newSVpvn(buf, len);
}
SENDFN(char) {
STRLEN len;
const char *buf = SvPVbyte(val, len);
if (len != 1) fu_confess("Type '%s' (oid %u) expects a 1-byte string", ctx->name, ctx->oid);
fustr_write(out, buf, len);
}
/* Works for many text-based column types.
* Assumes client_encoding=utf8, will create a mess otherwise */
RECVFN(text) {
return newSVpvn_utf8(buf, len, 1);
}
SENDFN(text) {
STRLEN len;
const char *buf = SvPVutf8(val, len);
fustr_write(out, buf, len);
}
RECVFN(float4) {
RLEN(4);
U32 uv = __builtin_bswap32(*((U32 *)buf));
float r;
memcpy(&r, &uv, 4);
return newSVnv(r);
}
SENDFN(float4) {
if (!looks_like_number(val)) fu_confess("Type '%s' (oid %u) expects a number", ctx->name, ctx->oid);
float r = SvNV(val);
U32 uv;
memcpy(&uv, &r, 4);
uv = __builtin_bswap32(uv);
fustr_write(out, (const char *)&uv, 4);
}
RECVFN(float8) {
RLEN(8);
U64 uv = __builtin_bswap64(*((U64 *)buf));
double r;
memcpy(&r, &uv, 8);
return newSVnv(r);
}
SENDFN(float8) {
if (!looks_like_number(val)) fu_confess("Type '%s' (oid %u) expects a number", ctx->name, ctx->oid);
double r = SvNV(val);
U64 uv;
memcpy(&uv, &r, 8);
uv = __builtin_bswap64(uv);
fustr_write(out, (const char *)&uv, 8);
}
#undef RLEN
#undef RECVFN
#undef SENDFN
#define R(name) fupg_recv_##name
#define S(name) fupg_send_##name
/* Sorted by oid to support binary search. */
/* List of types we handle directly in this module.
Ideally, this includes everything returned by:
select oid, typname, typreceive, typsend
from pg_type
where typtype = 'b'
and typnamespace = 'pg_catalog'::regnamespace
and typinput != 'array_in'::regproc
order by oid
Plus hopefully a bunch of common extension types.
Arrays, records and enums can be handled with generic code.
TODO: pre-seed this list with common array types.
The "reg#" types are a bit funny: the Postgres devs obviously realized that
writing JOINs is cumbersome, so they hacked together a numeric identifier
type that automatically resolves to a string when formatted as text, or
performs a lookup in the database when parsing text. In the text format, you
don't get to see the numeric identifier, but sadly that conversion is not
performed in the byte format so we're dealing with numbers instead. Oh well.
Not worth writing custom lookup code for, users will have to adapt.
Ordered by oid to support binary search.
(name is only used when formatting error messages, for now) */
#define CORETYPES \
B( 16, "bool", bool )\
B( 17, "bytea", bytea )\
B( 18, "char", char )\
B( 19, "name", text )\
B( 20, "int8", int8 )\
B( 21, "int2", int2 )\
/* 22 int2vector */ \
B( 23, "int4", int4 )\
B( 24, "regproc", uint4 )\
B( 25, "text", text )\
B( 26, "oid", uint4 )\
/* 27 tid: u32 block, u16 offset; represent as hash? */ \
B( 28, "xid", uint4 )\
B( 29, "cid", uint4 )\
/* 30 oidvector */ \
/* 114 json */ \
B( 142, "xml", text )\
B( 194, "pg_node_tree", text ) /* can't be used as a bind param */\
/* 600 point */\
/* 601 lseg */\
/* 602 path */\
/* 603 box */\
/* 604 polygon */\
/* 628 line */\
/* 650 cidr */\
B( 700, "float4", float4)\
B( 701, "float8", float8)\
/* 718 circle */\
/* 774 macaddr8 */\
/* 790 money */\
/* 829 macaddr */\
/* 869 inet */\
/* 1033 aclitem, does not support binary send/recv */\
B( 1042, "bpchar", text )\
B( 1043, "varchar", text )\
/* 1082 date */\
/* 1083 time */\
/* 1114 timestamp */\
/* 1184 timestamptz */\
/* 1186 interval */\
/* 1266 timetz */\
/* 1560 bit */\
/* 1562 varbit */\
/* 1700 numeric */\
B( 1790, "refcursor", text )\
B( 2202, "regprocedure", uint4 )\
B( 2203, "regoper", uint4 )\
B( 2204, "regoperator", uint4 )\
B( 2205, "regclass", uint4 )\
B( 2206, "regtype", uint4 )\
/* 2950 uuid */\
/* 2970 txid_snapshot */\
/* 3220 pg_lsn */\
/* 3361 pg_ndistinct */\
/* 3402 pg_dependencies */\
/* 3614 tsvector */\
/* 3615 tsquery */\
/* 3642 gtsvector, does not support binary send/recv */\
B( 3734, "regconfig", uint4 )\
B( 3769, "regdictionary", uint4 )\
/* 3802 jsonb */\
/* 4072 jsonpath */\
B( 4089, "regnamespace", uint4 )\
B( 4096, "regrole", uint4 )\
B( 4191, "regcollation", uint4 )\
/* 4600 pg_brin_bloom_summary */\
/* 4601 pg_brin_minmax_multi_summary */\
/* 5017 pg_mcv_list */\
/* 5038 pg_snapshot */\
/* 5069 xid8 */
static const fupg_core_type fupg_core_types[] = {
{ 16, "bool", S(bool), R(bool) },
{ 20, "int8", S(int8), R(int8) },
{ 21, "int2", S(int2), R(int2) },
{ 23, "int4", S(int4), R(int4) },
#define B(oid, name, fun) { oid, name"\0", fupg_send_##fun, fupg_recv_##fun },
CORETYPES
#undef B
};
/* TODO: A LOT MORE TYPES */
#undef R
#undef CORETYPES
#define FUPG_CORE_TYPES (sizeof(fupg_core_types) / sizeof(fupg_core_type))