diff --git a/c/pgtypes.c b/c/pgtypes.c index 5b858be..d3eb862 100644 --- a/c/pgtypes.c +++ b/c/pgtypes.c @@ -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 */