138 lines
4 KiB
C
138 lines
4 KiB
C
typedef struct fupg_send fupg_send;
|
|
typedef struct fupg_recv fupg_recv;
|
|
|
|
/* Send function, takes a Perl value and should write the binary encoded
|
|
* format into the given fustr. */
|
|
typedef void (*fupg_send_fn)(pTHX_ const fupg_send *, SV *, fustr *);
|
|
|
|
/* Receive function, takes a binary string and should return a Perl value.
|
|
* libpq guarantees that the given buffer is aligned to MAXIMUM_ALIGNOF.
|
|
*/
|
|
typedef SV *(*fupg_recv_fn)(pTHX_ const fupg_recv *, const char *, int);
|
|
|
|
struct fupg_send {
|
|
Oid oid;
|
|
const char *name;
|
|
fupg_send_fn fn;
|
|
};
|
|
|
|
struct fupg_recv {
|
|
Oid oid;
|
|
const char *name;
|
|
fupg_recv_fn fn;
|
|
};
|
|
|
|
typedef struct {
|
|
Oid oid;
|
|
char name[16]; /* Postgres has a 64 byte limit on names, but this is sufficient for the core types listed here */
|
|
fupg_send_fn send;
|
|
fupg_recv_fn recv;
|
|
} fupg_core_type;
|
|
|
|
|
|
|
|
#define RECVFN(name) static SV *fupg_recv_##name(pTHX_ const fupg_recv *ctx __attribute__((unused)), const char *buf, int len)
|
|
#define SENDFN(name) static void fupg_send_##name(pTHX_ const fupg_send *ctx __attribute__((unused)), SV *val, fustr *out)
|
|
#define RLEN(l) if (l != len) fu_confess("Invalid length for type '%s' (oid %u), expected %d but got %d", ctx->name, ctx->oid, l, len)
|
|
|
|
/* Perl likes to play loose with SV-to-integer conversions, but that's not
|
|
* very fun when trying to store values in a database. Text-based bind
|
|
* parameters get stricter validation by Postgres, so let's emulate some of
|
|
* that for binary parameters as well. */
|
|
#define SIV(min, max) IV iv;\
|
|
if (SvIOK(val)) iv = SvIV(val); \
|
|
else if (SvNOK(val)) { \
|
|
NV nv = SvNV(val); \
|
|
if (nv < IV_MIN || nv > IV_MAX || fabs(nv - floor(nv)) > 0.0000000001) \
|
|
fu_confess("Type '%s' (oid %u) expects an integer but got a floating point", ctx->name, ctx->oid); \
|
|
iv = SvIV(val); \
|
|
} else if (SvPOK(val)) {\
|
|
STRLEN sl; \
|
|
UV uv; \
|
|
char *s = SvPV(val, sl); \
|
|
if (*s == '-' && grok_atoUV(s+1, &uv, NULL) && uv <= ((UV)IV_MAX)+1) iv = SvIV(val);\
|
|
else if (grok_atoUV(s, &uv, NULL) && uv <= IV_MAX) iv = SvIV(val);\
|
|
else fu_confess("Type '%s' (oid %u) expects an integer", ctx->name, ctx->oid); \
|
|
} else fu_confess("Type '%s' (oid %u) expects an integer", ctx->name, ctx->oid);\
|
|
if (iv < min || iv > max) fu_confess("Integer %"IVdf" out of range for type '%s' (oid %u)", iv, ctx->name, ctx->oid)
|
|
|
|
|
|
RECVFN(textfmt) {
|
|
return newSVpvn_utf8(buf, len, 1);
|
|
}
|
|
|
|
RECVFN(bool) {
|
|
RLEN(1);
|
|
return *buf ? &PL_sv_yes : &PL_sv_no;
|
|
}
|
|
|
|
SENDFN(bool) {
|
|
fustr_write_ch(out, SvTRUE(val) ? 1 : 0);
|
|
}
|
|
|
|
RECVFN(int2) {
|
|
RLEN(2);
|
|
return newSViv((I16)__builtin_bswap16(*((U16 *)buf)));
|
|
}
|
|
|
|
SENDFN(int2) {
|
|
SIV(-32768, 32767);
|
|
U16 v = __builtin_bswap16((U16)iv);
|
|
fustr_write(out, (const char *)&v, 2);
|
|
}
|
|
|
|
RECVFN(int4) {
|
|
RLEN(4);
|
|
return newSViv((I32)__builtin_bswap32(*((U32 *)buf)));
|
|
}
|
|
|
|
SENDFN(int4) {
|
|
SIV(-2147483648, 2147483647);
|
|
U32 v = __builtin_bswap32((U32)iv);
|
|
fustr_write(out, (const char *)&v, 4);
|
|
}
|
|
|
|
RECVFN(int8) {
|
|
RLEN(8);
|
|
return newSViv((I64)__builtin_bswap64(*((U64 *)buf)));
|
|
}
|
|
|
|
SENDFN(int8) {
|
|
SIV(IV_MIN, IV_MAX);
|
|
U64 v = __builtin_bswap64((U64)SvIV(val));
|
|
fustr_write(out, (const char *)&v, 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. */
|
|
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) },
|
|
};
|
|
/* TODO: A LOT MORE TYPES */
|
|
|
|
#undef R
|
|
|
|
#define FUPG_CORE_TYPES (sizeof(fupg_core_types) / sizeof(fupg_core_type))
|
|
|
|
|
|
static const fupg_core_type *fupg_core_type_byoid(Oid oid) {
|
|
int i, b = 0, e = FUPG_CORE_TYPES-1;
|
|
while (b <= e) {
|
|
i = b + (e - b)/2;
|
|
if (fupg_core_types[i].oid == oid) return fupg_core_types+i;
|
|
if (fupg_core_types[i].oid < oid) b = i+1;
|
|
else e = i-1;
|
|
}
|
|
return NULL;
|
|
}
|