1
0
mirror of https://github.com/postgres/postgres.git synced 2025-04-27 22:56:53 +03:00

Convert range_in and multirange_in to report errors softly.

This is mostly straightforward, except that if the range type
has a canonical function, that might throw an error during range
input.  (Such errors probably only occur for edge cases: in the
in-core canonical functions, it happens only if a bound has the
maximum valid value for the underlying type.)  Hence, this patch
extends the soft-error regime to allow canonical functions to
return errors softly as well.  Extensions implementing range
canonical functions will need modification anyway because of the
API change for range_serialize(); while at it, they might want
to do something similar to what's been done here in the in-core
canonical functions.

Discussion: https://postgr.es/m/3284599.1671075185@sss.pgh.pa.us
This commit is contained in:
Tom Lane 2022-12-15 12:18:36 -05:00
parent 75f49221c2
commit d35a1af468
12 changed files with 289 additions and 66 deletions

View File

@ -120,6 +120,7 @@ multirange_in(PG_FUNCTION_ARGS)
char *input_str = PG_GETARG_CSTRING(0); char *input_str = PG_GETARG_CSTRING(0);
Oid mltrngtypoid = PG_GETARG_OID(1); Oid mltrngtypoid = PG_GETARG_OID(1);
Oid typmod = PG_GETARG_INT32(2); Oid typmod = PG_GETARG_INT32(2);
Node *escontext = fcinfo->context;
TypeCacheEntry *rangetyp; TypeCacheEntry *rangetyp;
int32 ranges_seen = 0; int32 ranges_seen = 0;
int32 range_count = 0; int32 range_count = 0;
@ -133,6 +134,7 @@ multirange_in(PG_FUNCTION_ARGS)
const char *range_str_begin = NULL; const char *range_str_begin = NULL;
int32 range_str_len; int32 range_str_len;
char *range_str; char *range_str;
Datum range_datum;
cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input); cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
rangetyp = cache->typcache->rngtype; rangetyp = cache->typcache->rngtype;
@ -144,7 +146,7 @@ multirange_in(PG_FUNCTION_ARGS)
if (*ptr == '{') if (*ptr == '{')
ptr++; ptr++;
else else
ereport(ERROR, ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed multirange literal: \"%s\"", errmsg("malformed multirange literal: \"%s\"",
input_str), input_str),
@ -157,7 +159,7 @@ multirange_in(PG_FUNCTION_ARGS)
char ch = *ptr; char ch = *ptr;
if (ch == '\0') if (ch == '\0')
ereport(ERROR, ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed multirange literal: \"%s\"", errmsg("malformed multirange literal: \"%s\"",
input_str), input_str),
@ -186,7 +188,7 @@ multirange_in(PG_FUNCTION_ARGS)
parse_state = MULTIRANGE_AFTER_RANGE; parse_state = MULTIRANGE_AFTER_RANGE;
} }
else else
ereport(ERROR, ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed multirange literal: \"%s\"", errmsg("malformed multirange literal: \"%s\"",
input_str), input_str),
@ -204,10 +206,14 @@ multirange_in(PG_FUNCTION_ARGS)
repalloc(ranges, range_capacity * sizeof(RangeType *)); repalloc(ranges, range_capacity * sizeof(RangeType *));
} }
ranges_seen++; ranges_seen++;
range = DatumGetRangeTypeP(InputFunctionCall(&cache->typioproc, if (!InputFunctionCallSafe(&cache->typioproc,
range_str, range_str,
cache->typioparam, cache->typioparam,
typmod)); typmod,
escontext,
&range_datum))
PG_RETURN_NULL();
range = DatumGetRangeTypeP(range_datum);
if (!RangeIsEmpty(range)) if (!RangeIsEmpty(range))
ranges[range_count++] = range; ranges[range_count++] = range;
parse_state = MULTIRANGE_AFTER_RANGE; parse_state = MULTIRANGE_AFTER_RANGE;
@ -256,7 +262,7 @@ multirange_in(PG_FUNCTION_ARGS)
else if (ch == '}') else if (ch == '}')
parse_state = MULTIRANGE_FINISHED; parse_state = MULTIRANGE_FINISHED;
else else
ereport(ERROR, ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed multirange literal: \"%s\"", errmsg("malformed multirange literal: \"%s\"",
input_str), input_str),
@ -280,7 +286,7 @@ multirange_in(PG_FUNCTION_ARGS)
ptr++; ptr++;
if (*ptr != '\0') if (*ptr != '\0')
ereport(ERROR, ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed multirange literal: \"%s\"", errmsg("malformed multirange literal: \"%s\"",
input_str), input_str),
@ -807,7 +813,7 @@ multirange_get_union_range(TypeCacheEntry *rangetyp,
multirange_get_bounds(rangetyp, mr, 0, &lower, &tmp); multirange_get_bounds(rangetyp, mr, 0, &lower, &tmp);
multirange_get_bounds(rangetyp, mr, mr->rangeCount - 1, &tmp, &upper); multirange_get_bounds(rangetyp, mr, mr->rangeCount - 1, &tmp, &upper);
return make_range(rangetyp, &lower, &upper, false); return make_range(rangetyp, &lower, &upper, false, NULL);
} }
@ -2696,7 +2702,8 @@ range_merge_from_multirange(PG_FUNCTION_ARGS)
multirange_get_bounds(typcache->rngtype, mr, mr->rangeCount - 1, multirange_get_bounds(typcache->rngtype, mr, mr->rangeCount - 1,
&lastLower, &lastUpper); &lastLower, &lastUpper);
result = make_range(typcache->rngtype, &firstLower, &lastUpper, false); result = make_range(typcache->rngtype, &firstLower, &lastUpper,
false, NULL);
} }
PG_RETURN_RANGE_P(result); PG_RETURN_RANGE_P(result);

View File

@ -221,7 +221,8 @@ multirangesel(PG_FUNCTION_ARGS)
upper.val = ((Const *) other)->constvalue; upper.val = ((Const *) other)->constvalue;
upper.infinite = false; upper.infinite = false;
upper.lower = false; upper.lower = false;
constrange = range_serialize(typcache->rngtype, &lower, &upper, false); constrange = range_serialize(typcache->rngtype, &lower, &upper,
false, NULL);
constmultirange = make_multirange(typcache->type_id, typcache->rngtype, constmultirange = make_multirange(typcache->type_id, typcache->rngtype,
1, &constrange); 1, &constrange);
} }

View File

@ -35,6 +35,7 @@
#include "lib/stringinfo.h" #include "lib/stringinfo.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/miscnodes.h"
#include "port/pg_bitutils.h" #include "port/pg_bitutils.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/date.h" #include "utils/date.h"
@ -55,10 +56,11 @@ typedef struct RangeIOData
static RangeIOData *get_range_io_data(FunctionCallInfo fcinfo, Oid rngtypid, static RangeIOData *get_range_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
IOFuncSelector func); IOFuncSelector func);
static char range_parse_flags(const char *flags_str); static char range_parse_flags(const char *flags_str);
static void range_parse(const char *string, char *flags, char **lbound_str, static bool range_parse(const char *string, char *flags, char **lbound_str,
char **ubound_str); char **ubound_str, Node *escontext);
static const char *range_parse_bound(const char *string, const char *ptr, static const char *range_parse_bound(const char *string, const char *ptr,
char **bound_str, bool *infinite); char **bound_str, bool *infinite,
Node *escontext);
static char *range_deparse(char flags, const char *lbound_str, static char *range_deparse(char flags, const char *lbound_str,
const char *ubound_str); const char *ubound_str);
static char *range_bound_escape(const char *value); static char *range_bound_escape(const char *value);
@ -80,6 +82,7 @@ range_in(PG_FUNCTION_ARGS)
char *input_str = PG_GETARG_CSTRING(0); char *input_str = PG_GETARG_CSTRING(0);
Oid rngtypoid = PG_GETARG_OID(1); Oid rngtypoid = PG_GETARG_OID(1);
Oid typmod = PG_GETARG_INT32(2); Oid typmod = PG_GETARG_INT32(2);
Node *escontext = fcinfo->context;
RangeType *range; RangeType *range;
RangeIOData *cache; RangeIOData *cache;
char flags; char flags;
@ -93,15 +96,20 @@ range_in(PG_FUNCTION_ARGS)
cache = get_range_io_data(fcinfo, rngtypoid, IOFunc_input); cache = get_range_io_data(fcinfo, rngtypoid, IOFunc_input);
/* parse */ /* parse */
range_parse(input_str, &flags, &lbound_str, &ubound_str); if (!range_parse(input_str, &flags, &lbound_str, &ubound_str, escontext))
PG_RETURN_NULL();
/* call element type's input function */ /* call element type's input function */
if (RANGE_HAS_LBOUND(flags)) if (RANGE_HAS_LBOUND(flags))
lower.val = InputFunctionCall(&cache->typioproc, lbound_str, if (!InputFunctionCallSafe(&cache->typioproc, lbound_str,
cache->typioparam, typmod); cache->typioparam, typmod,
escontext, &lower.val))
PG_RETURN_NULL();
if (RANGE_HAS_UBOUND(flags)) if (RANGE_HAS_UBOUND(flags))
upper.val = InputFunctionCall(&cache->typioproc, ubound_str, if (!InputFunctionCallSafe(&cache->typioproc, ubound_str,
cache->typioparam, typmod); cache->typioparam, typmod,
escontext, &upper.val))
PG_RETURN_NULL();
lower.infinite = (flags & RANGE_LB_INF) != 0; lower.infinite = (flags & RANGE_LB_INF) != 0;
lower.inclusive = (flags & RANGE_LB_INC) != 0; lower.inclusive = (flags & RANGE_LB_INC) != 0;
@ -111,7 +119,8 @@ range_in(PG_FUNCTION_ARGS)
upper.lower = false; upper.lower = false;
/* serialize and canonicalize */ /* serialize and canonicalize */
range = make_range(cache->typcache, &lower, &upper, flags & RANGE_EMPTY); range = make_range(cache->typcache, &lower, &upper,
flags & RANGE_EMPTY, escontext);
PG_RETURN_RANGE_P(range); PG_RETURN_RANGE_P(range);
} }
@ -234,7 +243,8 @@ range_recv(PG_FUNCTION_ARGS)
upper.lower = false; upper.lower = false;
/* serialize and canonicalize */ /* serialize and canonicalize */
range = make_range(cache->typcache, &lower, &upper, flags & RANGE_EMPTY); range = make_range(cache->typcache, &lower, &upper,
flags & RANGE_EMPTY, NULL);
PG_RETURN_RANGE_P(range); PG_RETURN_RANGE_P(range);
} }
@ -378,7 +388,7 @@ range_constructor2(PG_FUNCTION_ARGS)
upper.inclusive = false; upper.inclusive = false;
upper.lower = false; upper.lower = false;
range = make_range(typcache, &lower, &upper, false); range = make_range(typcache, &lower, &upper, false, NULL);
PG_RETURN_RANGE_P(range); PG_RETURN_RANGE_P(range);
} }
@ -415,7 +425,7 @@ range_constructor3(PG_FUNCTION_ARGS)
upper.inclusive = (flags & RANGE_UB_INC) != 0; upper.inclusive = (flags & RANGE_UB_INC) != 0;
upper.lower = false; upper.lower = false;
range = make_range(typcache, &lower, &upper, false); range = make_range(typcache, &lower, &upper, false, NULL);
PG_RETURN_RANGE_P(range); PG_RETURN_RANGE_P(range);
} }
@ -766,7 +776,7 @@ bounds_adjacent(TypeCacheEntry *typcache, RangeBound boundA, RangeBound boundB)
/* change upper/lower labels to avoid Assert failures */ /* change upper/lower labels to avoid Assert failures */
boundA.lower = true; boundA.lower = true;
boundB.lower = false; boundB.lower = false;
r = make_range(typcache, &boundA, &boundB, false); r = make_range(typcache, &boundA, &boundB, false, NULL);
return RangeIsEmpty(r); return RangeIsEmpty(r);
} }
else if (cmp == 0) else if (cmp == 0)
@ -1012,14 +1022,14 @@ range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
{ {
lower2.inclusive = !lower2.inclusive; lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */ lower2.lower = false; /* it will become the upper bound */
return make_range(typcache, &lower1, &lower2, false); return make_range(typcache, &lower1, &lower2, false, NULL);
} }
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0) if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{ {
upper2.inclusive = !upper2.inclusive; upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */ upper2.lower = true; /* it will become the lower bound */
return make_range(typcache, &upper2, &upper1, false); return make_range(typcache, &upper2, &upper1, false, NULL);
} }
elog(ERROR, "unexpected case in range_minus"); elog(ERROR, "unexpected case in range_minus");
@ -1073,7 +1083,7 @@ range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
else else
result_upper = &upper2; result_upper = &upper2;
return make_range(typcache, result_lower, result_upper, false); return make_range(typcache, result_lower, result_upper, false, NULL);
} }
Datum Datum
@ -1149,7 +1159,7 @@ range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const Ra
else else
result_upper = &upper2; result_upper = &upper2;
return make_range(typcache, result_lower, result_upper, false); return make_range(typcache, result_lower, result_upper, false, NULL);
} }
/* range, range -> range, range functions */ /* range, range -> range, range functions */
@ -1187,8 +1197,8 @@ range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeT
upper2.inclusive = !upper2.inclusive; upper2.inclusive = !upper2.inclusive;
upper2.lower = true; upper2.lower = true;
*output1 = make_range(typcache, &lower1, &lower2, false); *output1 = make_range(typcache, &lower1, &lower2, false, NULL);
*output2 = make_range(typcache, &upper2, &upper1, false); *output2 = make_range(typcache, &upper2, &upper1, false, NULL);
return true; return true;
} }
@ -1446,6 +1456,7 @@ Datum
int4range_canonical(PG_FUNCTION_ARGS) int4range_canonical(PG_FUNCTION_ARGS)
{ {
RangeType *r = PG_GETARG_RANGE_P(0); RangeType *r = PG_GETARG_RANGE_P(0);
Node *escontext = fcinfo->context;
TypeCacheEntry *typcache; TypeCacheEntry *typcache;
RangeBound lower; RangeBound lower;
RangeBound upper; RangeBound upper;
@ -1460,23 +1471,39 @@ int4range_canonical(PG_FUNCTION_ARGS)
if (!lower.infinite && !lower.inclusive) if (!lower.infinite && !lower.inclusive)
{ {
lower.val = DirectFunctionCall2(int4pl, lower.val, Int32GetDatum(1)); int32 bnd = DatumGetInt32(lower.val);
/* Handle possible overflow manually */
if (unlikely(bnd == PG_INT32_MAX))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
lower.val = Int32GetDatum(bnd + 1);
lower.inclusive = true; lower.inclusive = true;
} }
if (!upper.infinite && upper.inclusive) if (!upper.infinite && upper.inclusive)
{ {
upper.val = DirectFunctionCall2(int4pl, upper.val, Int32GetDatum(1)); int32 bnd = DatumGetInt32(upper.val);
/* Handle possible overflow manually */
if (unlikely(bnd == PG_INT32_MAX))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
upper.val = Int32GetDatum(bnd + 1);
upper.inclusive = false; upper.inclusive = false;
} }
PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper, false)); PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper,
false, escontext));
} }
Datum Datum
int8range_canonical(PG_FUNCTION_ARGS) int8range_canonical(PG_FUNCTION_ARGS)
{ {
RangeType *r = PG_GETARG_RANGE_P(0); RangeType *r = PG_GETARG_RANGE_P(0);
Node *escontext = fcinfo->context;
TypeCacheEntry *typcache; TypeCacheEntry *typcache;
RangeBound lower; RangeBound lower;
RangeBound upper; RangeBound upper;
@ -1491,23 +1518,39 @@ int8range_canonical(PG_FUNCTION_ARGS)
if (!lower.infinite && !lower.inclusive) if (!lower.infinite && !lower.inclusive)
{ {
lower.val = DirectFunctionCall2(int8pl, lower.val, Int64GetDatum(1)); int64 bnd = DatumGetInt64(lower.val);
/* Handle possible overflow manually */
if (unlikely(bnd == PG_INT64_MAX))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range")));
lower.val = Int64GetDatum(bnd + 1);
lower.inclusive = true; lower.inclusive = true;
} }
if (!upper.infinite && upper.inclusive) if (!upper.infinite && upper.inclusive)
{ {
upper.val = DirectFunctionCall2(int8pl, upper.val, Int64GetDatum(1)); int64 bnd = DatumGetInt64(upper.val);
/* Handle possible overflow manually */
if (unlikely(bnd == PG_INT64_MAX))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("bigint out of range")));
upper.val = Int64GetDatum(bnd + 1);
upper.inclusive = false; upper.inclusive = false;
} }
PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper, false)); PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper,
false, escontext));
} }
Datum Datum
daterange_canonical(PG_FUNCTION_ARGS) daterange_canonical(PG_FUNCTION_ARGS)
{ {
RangeType *r = PG_GETARG_RANGE_P(0); RangeType *r = PG_GETARG_RANGE_P(0);
Node *escontext = fcinfo->context;
TypeCacheEntry *typcache; TypeCacheEntry *typcache;
RangeBound lower; RangeBound lower;
RangeBound upper; RangeBound upper;
@ -1523,18 +1566,35 @@ daterange_canonical(PG_FUNCTION_ARGS)
if (!lower.infinite && !DATE_NOT_FINITE(DatumGetDateADT(lower.val)) && if (!lower.infinite && !DATE_NOT_FINITE(DatumGetDateADT(lower.val)) &&
!lower.inclusive) !lower.inclusive)
{ {
lower.val = DirectFunctionCall2(date_pli, lower.val, Int32GetDatum(1)); DateADT bnd = DatumGetDateADT(lower.val);
/* Check for overflow -- note we already eliminated PG_INT32_MAX */
bnd++;
if (unlikely(!IS_VALID_DATE(bnd)))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range")));
lower.val = DateADTGetDatum(bnd);
lower.inclusive = true; lower.inclusive = true;
} }
if (!upper.infinite && !DATE_NOT_FINITE(DatumGetDateADT(upper.val)) && if (!upper.infinite && !DATE_NOT_FINITE(DatumGetDateADT(upper.val)) &&
upper.inclusive) upper.inclusive)
{ {
upper.val = DirectFunctionCall2(date_pli, upper.val, Int32GetDatum(1)); DateADT bnd = DatumGetDateADT(upper.val);
/* Check for overflow -- note we already eliminated PG_INT32_MAX */
bnd++;
if (unlikely(!IS_VALID_DATE(bnd)))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range")));
upper.val = DateADTGetDatum(bnd);
upper.inclusive = false; upper.inclusive = false;
} }
PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper, false)); PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper,
false, escontext));
} }
/* /*
@ -1657,7 +1717,7 @@ range_get_typcache(FunctionCallInfo fcinfo, Oid rngtypid)
*/ */
RangeType * RangeType *
range_serialize(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper, range_serialize(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
bool empty) bool empty, struct Node *escontext)
{ {
RangeType *range; RangeType *range;
int cmp; int cmp;
@ -1684,7 +1744,7 @@ range_serialize(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
/* error check: if lower bound value is above upper, it's wrong */ /* error check: if lower bound value is above upper, it's wrong */
if (cmp > 0) if (cmp > 0)
ereport(ERROR, ereturn(escontext, NULL,
(errcode(ERRCODE_DATA_EXCEPTION), (errcode(ERRCODE_DATA_EXCEPTION),
errmsg("range lower bound must be less than or equal to range upper bound"))); errmsg("range lower bound must be less than or equal to range upper bound")));
@ -1882,17 +1942,41 @@ range_set_contain_empty(RangeType *range)
*/ */
RangeType * RangeType *
make_range(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper, make_range(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
bool empty) bool empty, struct Node *escontext)
{ {
RangeType *range; RangeType *range;
range = range_serialize(typcache, lower, upper, empty); range = range_serialize(typcache, lower, upper, empty, escontext);
if (SOFT_ERROR_OCCURRED(escontext))
return NULL;
/* no need to call canonical on empty ranges ... */ /* no need to call canonical on empty ranges ... */
if (OidIsValid(typcache->rng_canonical_finfo.fn_oid) && if (OidIsValid(typcache->rng_canonical_finfo.fn_oid) &&
!RangeIsEmpty(range)) !RangeIsEmpty(range))
range = DatumGetRangeTypeP(FunctionCall1(&typcache->rng_canonical_finfo, {
RangeTypePGetDatum(range))); /* Do this the hard way so that we can pass escontext */
LOCAL_FCINFO(fcinfo, 1);
Datum result;
InitFunctionCallInfoData(*fcinfo, &typcache->rng_canonical_finfo, 1,
InvalidOid, escontext, NULL);
fcinfo->args[0].value = RangeTypePGetDatum(range);
fcinfo->args[0].isnull = false;
result = FunctionCallInvoke(fcinfo);
if (SOFT_ERROR_OCCURRED(escontext))
return NULL;
/* Should not get a null result if there was no error */
if (fcinfo->isnull)
elog(ERROR, "function %u returned NULL",
typcache->rng_canonical_finfo.fn_oid);
range = DatumGetRangeTypeP(result);
}
return range; return range;
} }
@ -2085,7 +2169,7 @@ make_empty_range(TypeCacheEntry *typcache)
upper.inclusive = false; upper.inclusive = false;
upper.lower = false; upper.lower = false;
return make_range(typcache, &lower, &upper, true); return make_range(typcache, &lower, &upper, true, NULL);
} }
@ -2170,10 +2254,13 @@ range_parse_flags(const char *flags_str)
* Within a <string>, special characters (such as comma, parenthesis, or * Within a <string>, special characters (such as comma, parenthesis, or
* brackets) can be enclosed in double-quotes or escaped with backslash. Within * brackets) can be enclosed in double-quotes or escaped with backslash. Within
* double-quotes, a double-quote can be escaped with double-quote or backslash. * double-quotes, a double-quote can be escaped with double-quote or backslash.
*
* Returns true on success, false on failure (but failures will return only if
* escontext is an ErrorSaveContext).
*/ */
static void static bool
range_parse(const char *string, char *flags, char **lbound_str, range_parse(const char *string, char *flags, char **lbound_str,
char **ubound_str) char **ubound_str, Node *escontext)
{ {
const char *ptr = string; const char *ptr = string;
bool infinite; bool infinite;
@ -2200,13 +2287,13 @@ range_parse(const char *string, char *flags, char **lbound_str,
/* should have consumed everything */ /* should have consumed everything */
if (*ptr != '\0') if (*ptr != '\0')
ereport(ERROR, ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"", errmsg("malformed range literal: \"%s\"",
string), string),
errdetail("Junk after \"empty\" key word."))); errdetail("Junk after \"empty\" key word.")));
return; return true;
} }
if (*ptr == '[') if (*ptr == '[')
@ -2217,26 +2304,30 @@ range_parse(const char *string, char *flags, char **lbound_str,
else if (*ptr == '(') else if (*ptr == '(')
ptr++; ptr++;
else else
ereport(ERROR, ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"", errmsg("malformed range literal: \"%s\"",
string), string),
errdetail("Missing left parenthesis or bracket."))); errdetail("Missing left parenthesis or bracket.")));
ptr = range_parse_bound(string, ptr, lbound_str, &infinite); ptr = range_parse_bound(string, ptr, lbound_str, &infinite, escontext);
if (ptr == NULL)
return false;
if (infinite) if (infinite)
*flags |= RANGE_LB_INF; *flags |= RANGE_LB_INF;
if (*ptr == ',') if (*ptr == ',')
ptr++; ptr++;
else else
ereport(ERROR, ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"", errmsg("malformed range literal: \"%s\"",
string), string),
errdetail("Missing comma after lower bound."))); errdetail("Missing comma after lower bound.")));
ptr = range_parse_bound(string, ptr, ubound_str, &infinite); ptr = range_parse_bound(string, ptr, ubound_str, &infinite, escontext);
if (ptr == NULL)
return false;
if (infinite) if (infinite)
*flags |= RANGE_UB_INF; *flags |= RANGE_UB_INF;
@ -2248,7 +2339,7 @@ range_parse(const char *string, char *flags, char **lbound_str,
else if (*ptr == ')') else if (*ptr == ')')
ptr++; ptr++;
else /* must be a comma */ else /* must be a comma */
ereport(ERROR, ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"", errmsg("malformed range literal: \"%s\"",
string), string),
@ -2259,11 +2350,13 @@ range_parse(const char *string, char *flags, char **lbound_str,
ptr++; ptr++;
if (*ptr != '\0') if (*ptr != '\0')
ereport(ERROR, ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"", errmsg("malformed range literal: \"%s\"",
string), string),
errdetail("Junk after right parenthesis or bracket."))); errdetail("Junk after right parenthesis or bracket.")));
return true;
} }
/* /*
@ -2279,10 +2372,11 @@ range_parse(const char *string, char *flags, char **lbound_str,
* *infinite: set true if no bound, else false * *infinite: set true if no bound, else false
* *
* The return value is the scan ptr, advanced past the bound string. * The return value is the scan ptr, advanced past the bound string.
* However, if escontext is an ErrorSaveContext, we return NULL on failure.
*/ */
static const char * static const char *
range_parse_bound(const char *string, const char *ptr, range_parse_bound(const char *string, const char *ptr,
char **bound_str, bool *infinite) char **bound_str, bool *infinite, Node *escontext)
{ {
StringInfoData buf; StringInfoData buf;
@ -2303,7 +2397,7 @@ range_parse_bound(const char *string, const char *ptr,
char ch = *ptr++; char ch = *ptr++;
if (ch == '\0') if (ch == '\0')
ereport(ERROR, ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"", errmsg("malformed range literal: \"%s\"",
string), string),
@ -2311,7 +2405,7 @@ range_parse_bound(const char *string, const char *ptr,
if (ch == '\\') if (ch == '\\')
{ {
if (*ptr == '\0') if (*ptr == '\0')
ereport(ERROR, ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"", errmsg("malformed range literal: \"%s\"",
string), string),

View File

@ -876,7 +876,7 @@ range_super_union(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
((flags2 & RANGE_CONTAIN_EMPTY) || !(flags1 & RANGE_CONTAIN_EMPTY))) ((flags2 & RANGE_CONTAIN_EMPTY) || !(flags1 & RANGE_CONTAIN_EMPTY)))
return r2; return r2;
result = make_range(typcache, result_lower, result_upper, false); result = make_range(typcache, result_lower, result_upper, false, NULL);
if ((flags1 & RANGE_CONTAIN_EMPTY) || (flags2 & RANGE_CONTAIN_EMPTY)) if ((flags1 & RANGE_CONTAIN_EMPTY) || (flags2 & RANGE_CONTAIN_EMPTY))
range_set_contain_empty(result); range_set_contain_empty(result);

View File

@ -190,7 +190,7 @@ rangesel(PG_FUNCTION_ARGS)
upper.val = ((Const *) other)->constvalue; upper.val = ((Const *) other)->constvalue;
upper.infinite = false; upper.infinite = false;
upper.lower = false; upper.lower = false;
constrange = range_serialize(typcache, &lower, &upper, false); constrange = range_serialize(typcache, &lower, &upper, false, NULL);
} }
} }
else if (operator == OID_RANGE_ELEM_CONTAINED_OP) else if (operator == OID_RANGE_ELEM_CONTAINED_OP)

View File

@ -265,7 +265,7 @@ spg_range_quad_picksplit(PG_FUNCTION_ARGS)
/* Construct "centroid" range from medians of lower and upper bounds */ /* Construct "centroid" range from medians of lower and upper bounds */
centroid = range_serialize(typcache, &lowerBounds[nonEmptyCount / 2], centroid = range_serialize(typcache, &lowerBounds[nonEmptyCount / 2],
&upperBounds[nonEmptyCount / 2], false); &upperBounds[nonEmptyCount / 2], false, NULL);
out->hasPrefix = true; out->hasPrefix = true;
out->prefixDatum = RangeTypePGetDatum(centroid); out->prefixDatum = RangeTypePGetDatum(centroid);

View File

@ -311,7 +311,8 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
bound_hist_values[i] = PointerGetDatum(range_serialize(typcache, bound_hist_values[i] = PointerGetDatum(range_serialize(typcache,
&lowers[pos], &lowers[pos],
&uppers[pos], &uppers[pos],
false)); false,
NULL));
pos += delta; pos += delta;
posfrac += deltafrac; posfrac += deltafrac;
if (posfrac >= (num_hist - 1)) if (posfrac >= (num_hist - 1))

View File

@ -143,14 +143,16 @@ extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const Range
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo, extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
Oid rngtypid); Oid rngtypid);
extern RangeType *range_serialize(TypeCacheEntry *typcache, RangeBound *lower, extern RangeType *range_serialize(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty); RangeBound *upper, bool empty,
struct Node *escontext);
extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range, extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper, RangeBound *lower, RangeBound *upper,
bool *empty); bool *empty);
extern char range_get_flags(const RangeType *range); extern char range_get_flags(const RangeType *range);
extern void range_set_contain_empty(RangeType *range); extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower, extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty); RangeBound *upper, bool empty,
struct Node *escontext);
extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1, extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2); const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1, extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,

View File

@ -274,6 +274,37 @@ select '{(a,a)}'::textmultirange;
{} {}
(1 row) (1 row)
-- Also try it with non-error-throwing API
select pg_input_is_valid('{[1,2], [4,5]}', 'int4multirange');
pg_input_is_valid
-------------------
t
(1 row)
select pg_input_is_valid('{[1,2], [4,5]', 'int4multirange');
pg_input_is_valid
-------------------
f
(1 row)
select pg_input_error_message('{[1,2], [4,5]', 'int4multirange');
pg_input_error_message
-----------------------------------------------
malformed multirange literal: "{[1,2], [4,5]"
(1 row)
select pg_input_is_valid('{[1,2], [4,zed]}', 'int4multirange');
pg_input_is_valid
-------------------
f
(1 row)
select pg_input_error_message('{[1,2], [4,zed]}', 'int4multirange');
pg_input_error_message
----------------------------------------------
invalid input syntax for type integer: "zed"
(1 row)
-- --
-- test the constructor -- test the constructor
--- ---

View File

@ -175,6 +175,73 @@ select '(a,a)'::textrange;
empty empty
(1 row) (1 row)
-- Also try it with non-error-throwing API
select pg_input_is_valid('(1,4)', 'int4range');
pg_input_is_valid
-------------------
t
(1 row)
select pg_input_is_valid('(1,4', 'int4range');
pg_input_is_valid
-------------------
f
(1 row)
select pg_input_error_message('(1,4', 'int4range');
pg_input_error_message
---------------------------------
malformed range literal: "(1,4"
(1 row)
select pg_input_is_valid('(4,1)', 'int4range');
pg_input_is_valid
-------------------
f
(1 row)
select pg_input_error_message('(4,1)', 'int4range');
pg_input_error_message
-------------------------------------------------------------------
range lower bound must be less than or equal to range upper bound
(1 row)
select pg_input_is_valid('(4,zed)', 'int4range');
pg_input_is_valid
-------------------
f
(1 row)
select pg_input_error_message('(4,zed)', 'int4range');
pg_input_error_message
----------------------------------------------
invalid input syntax for type integer: "zed"
(1 row)
select pg_input_is_valid('[1,2147483647]', 'int4range');
pg_input_is_valid
-------------------
f
(1 row)
select pg_input_error_message('[1,2147483647]', 'int4range');
pg_input_error_message
------------------------
integer out of range
(1 row)
select pg_input_is_valid('[2000-01-01,5874897-12-31]', 'daterange');
pg_input_is_valid
-------------------
f
(1 row)
select pg_input_error_message('[2000-01-01,5874897-12-31]', 'daterange');
pg_input_error_message
------------------------
date out of range
(1 row)
-- --
-- create some test data and test the operators -- create some test data and test the operators
-- --

View File

@ -58,6 +58,13 @@ select '{[a,a)}'::textmultirange;
select '{(a,a]}'::textmultirange; select '{(a,a]}'::textmultirange;
select '{(a,a)}'::textmultirange; select '{(a,a)}'::textmultirange;
-- Also try it with non-error-throwing API
select pg_input_is_valid('{[1,2], [4,5]}', 'int4multirange');
select pg_input_is_valid('{[1,2], [4,5]', 'int4multirange');
select pg_input_error_message('{[1,2], [4,5]', 'int4multirange');
select pg_input_is_valid('{[1,2], [4,zed]}', 'int4multirange');
select pg_input_error_message('{[1,2], [4,zed]}', 'int4multirange');
-- --
-- test the constructor -- test the constructor
--- ---

View File

@ -40,6 +40,19 @@ select '[a,a)'::textrange;
select '(a,a]'::textrange; select '(a,a]'::textrange;
select '(a,a)'::textrange; select '(a,a)'::textrange;
-- Also try it with non-error-throwing API
select pg_input_is_valid('(1,4)', 'int4range');
select pg_input_is_valid('(1,4', 'int4range');
select pg_input_error_message('(1,4', 'int4range');
select pg_input_is_valid('(4,1)', 'int4range');
select pg_input_error_message('(4,1)', 'int4range');
select pg_input_is_valid('(4,zed)', 'int4range');
select pg_input_error_message('(4,zed)', 'int4range');
select pg_input_is_valid('[1,2147483647]', 'int4range');
select pg_input_error_message('[1,2147483647]', 'int4range');
select pg_input_is_valid('[2000-01-01,5874897-12-31]', 'daterange');
select pg_input_error_message('[2000-01-01,5874897-12-31]', 'daterange');
-- --
-- create some test data and test the operators -- create some test data and test the operators
-- --