pgtypes: Cleaner error reporting

This commit is contained in:
Yorhel 2025-02-10 09:46:11 +01:00
parent d5f593387a
commit b66610e25a

View file

@ -38,7 +38,9 @@ typedef struct {
#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)
#define RERR(msg, ...) fu_confess("Error parsing value for type '%s' (oid %u): "msg, ctx->name, ctx->oid __VA_OPT__(,) __VA_ARGS__)
#define SERR(msg, ...) fu_confess("Error converting Perl value '%s' to type '%s' (oid %u): "msg, SvPV_nolen(val), ctx->name, ctx->oid __VA_OPT__(,) __VA_ARGS__)
#define RLEN(l) if (l != len) RERR("expected %d bytes but got %d", 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
@ -48,8 +50,7 @@ typedef struct {
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); \
if (nv < IV_MIN || nv > IV_MAX || fabs(nv - floor(nv)) > 0.0000000001) SERR("expected integer");\
iv = SvIV(val); \
} else if (SvPOK(val)) {\
STRLEN sl; \
@ -57,9 +58,9 @@ typedef struct {
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)
else SERR("expected integer");\
} else SERR("expected integer");\
if (iv < min || iv > max) SERR("integer out of range")
RECVFN(bool) {
@ -129,14 +130,13 @@ RECVFN(char) {
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);
if (len != 1) SERR("expected 1-byte string");
fustr_write(out, buf, len);
}
/* Works for many text-based column types, including receiving any value in the text format */
RECVFN(text) {
if (!is_c9strict_utf8_string((const U8*)buf, len))
fu_confess("Received invalid UTF-8 for type '%s' (oid %u)", ctx->name, ctx->oid);
if (!is_c9strict_utf8_string((const U8*)buf, len)) RERR("invalid UTF-8");
return newSVpvn_utf8(buf, len, 1);
}
@ -152,7 +152,7 @@ RECVFN(float4) {
}
SENDFN(float4) {
if (!looks_like_number(val)) fu_confess("Type '%s' (oid %u) expects a number", ctx->name, ctx->oid);
if (!looks_like_number(val)) SERR("expected a number");
fustr_writebeT(float, 32, out, SvNV(val));
}
@ -162,7 +162,7 @@ RECVFN(float8) {
}
SENDFN(float8) {
if (!looks_like_number(val)) fu_confess("Type '%s' (oid %u) expects a number", ctx->name, ctx->oid);
if (!looks_like_number(val)) SERR("expected a number");
fustr_writebeT(double, 64, out, SvNV(val));
}
@ -173,8 +173,8 @@ RECVFN(json) {
.depth = 512
};
SV *sv = fujson_parse(aTHX_ &json);
if (sv == NULL) fu_confess("Received invalid JSON for type '%s' (oid %u)", ctx->name, ctx->oid);
if (json.buf != json.end) fu_confess("Received invalid JSON for type '%s' (oid %u)", ctx->name, ctx->oid);
if (sv == NULL) RERR("invalid JSON");
if (json.buf != json.end) RERR("trailing garbage");
return sv;
}
@ -184,7 +184,7 @@ SENDFN(json) {
}
RECVFN(jsonb) {
if (len <= 1 || *buf != 1) fu_confess("Unexpected format for type '%s' (oid %u)", ctx->name, ctx->oid);
if (len <= 1 || *buf != 1) RERR("invalid JSONB");
return fupg_recv_json(aTHX_ ctx, buf+1, len-1);
}
@ -194,7 +194,7 @@ SENDFN(jsonb) {
}
RECVFN(jsonpath) {
if (len <= 1 || *buf != 1) fu_confess("Unexpected format for type '%s' (oid %u)", ctx->name, ctx->oid);
if (len <= 1 || *buf != 1) RERR("invalid jsonpath");
return fupg_recv_text(aTHX_ ctx, buf+1, len-1);
}
@ -232,16 +232,15 @@ static SV *fupg_recv_array_elem(pTHX_ const fupg_recv *elem, const char *header,
}
RECVFN(array) {
if (len < 12) fu_confess("Invalid array format for type '%s' (oid %u)", ctx->name, ctx->oid);
if (len < 12) RERR("input data too short");
U32 ndim = fu_frombeU(32, buf);
// buf+4 is hasnull, can safely ignore
Oid elemtype = fu_frombeU(32, buf+8);
if (elemtype != ctx->arrayelem->oid)
fu_confess("Array type '%s' (oid %u) expected elements of type %u but got %u", ctx->name, ctx->oid, ctx->arrayelem->oid, elemtype);
if (elemtype != ctx->arrayelem->oid) RERR("invalid element type, expected %u but got %u", ctx->arrayelem->oid, elemtype);
if (ndim == 0) return newRV_noinc((SV *)newAV());
if (ndim > ARRAY_MAXDIM) fu_confess("Array value of type '%s' (oid %u) has too many dimensions (%d)", ctx->name, ctx->oid, ndim);
if ((U32)len < 12 + ndim*8) fu_confess("Invalid array format for type '%s' (oid %u)", ctx->name, ctx->oid);
if (ndim > ARRAY_MAXDIM) RERR("too many dimensions");
if ((U32)len < 12 + ndim*8) RERR("input data too short");
const char *header = buf + 12;
const char *data = header + ndim * 8;
@ -293,15 +292,15 @@ SENDFN(array) {
v = SvRV(v);
SvGETMAGIC(v);
if (SvTYPE(v) != SVt_PVAV) break;
if (ndim >= ARRAY_MAXDIM) fu_confess("Maximum depth exceeded while attempting to send array of type '%s' (oid %u)", ctx->name, ctx->oid);
if (ndim >= ARRAY_MAXDIM) SERR("too many dimensions");
dims[ndim] = av_count((AV*)v);
if (ndim > 0 && dims[ndim] == 0) fu_confess("Empty dimension while attempting to send array of type '%s' (oid %u)", ctx->name, ctx->oid);
if (ndim > 0 && dims[ndim] == 0) SERR("nested arrays may not be empty");
ndim++;
SV **sv = av_fetch((AV*)v, 0, 0);
if (!sv || !*sv) break;
v = *sv;
}
if (ndim == 0) fu_confess("Type '%s' (oid %u) expects an array", ctx->name, ctx->oid);
if (ndim == 0) SERR("expected an array");
if (dims[0] == 0) ndim = 0;
/* Write header */