#include "lib/stringinfo.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
+#include "nodes/miscnodes.h"
#include "port/pg_bitutils.h"
#include "utils/builtins.h"
#include "utils/date.h"
static RangeIOData *get_range_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
IOFuncSelector func);
static char range_parse_flags(const char *flags_str);
-static void range_parse(const char *string, char *flags, char **lbound_str,
- char **ubound_str);
+static bool range_parse(const char *string, char *flags, char **lbound_str,
+ char **ubound_str, Node *escontext);
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,
const char *ubound_str);
static char *range_bound_escape(const char *value);
char *input_str = PG_GETARG_CSTRING(0);
Oid rngtypoid = PG_GETARG_OID(1);
Oid typmod = PG_GETARG_INT32(2);
+ Node *escontext = fcinfo->context;
RangeType *range;
RangeIOData *cache;
char flags;
cache = get_range_io_data(fcinfo, rngtypoid, IOFunc_input);
/* 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 */
if (RANGE_HAS_LBOUND(flags))
- lower.val = InputFunctionCall(&cache->typioproc, lbound_str,
- cache->typioparam, typmod);
+ if (!InputFunctionCallSafe(&cache->typioproc, lbound_str,
+ cache->typioparam, typmod,
+ escontext, &lower.val))
+ PG_RETURN_NULL();
if (RANGE_HAS_UBOUND(flags))
- upper.val = InputFunctionCall(&cache->typioproc, ubound_str,
- cache->typioparam, typmod);
+ if (!InputFunctionCallSafe(&cache->typioproc, ubound_str,
+ cache->typioparam, typmod,
+ escontext, &upper.val))
+ PG_RETURN_NULL();
lower.infinite = (flags & RANGE_LB_INF) != 0;
lower.inclusive = (flags & RANGE_LB_INC) != 0;
upper.lower = false;
/* 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);
}
upper.lower = false;
/* 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);
}
upper.inclusive = false;
upper.lower = false;
- range = make_range(typcache, &lower, &upper, false);
+ range = make_range(typcache, &lower, &upper, false, NULL);
PG_RETURN_RANGE_P(range);
}
upper.inclusive = (flags & RANGE_UB_INC) != 0;
upper.lower = false;
- range = make_range(typcache, &lower, &upper, false);
+ range = make_range(typcache, &lower, &upper, false, NULL);
PG_RETURN_RANGE_P(range);
}
/* change upper/lower labels to avoid Assert failures */
boundA.lower = true;
boundB.lower = false;
- r = make_range(typcache, &boundA, &boundB, false);
+ r = make_range(typcache, &boundA, &boundB, false, NULL);
return RangeIsEmpty(r);
}
else if (cmp == 0)
{
lower2.inclusive = !lower2.inclusive;
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)
{
upper2.inclusive = !upper2.inclusive;
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");
else
result_upper = &upper2;
- return make_range(typcache, result_lower, result_upper, false);
+ return make_range(typcache, result_lower, result_upper, false, NULL);
}
Datum
else
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 */
upper2.inclusive = !upper2.inclusive;
upper2.lower = true;
- *output1 = make_range(typcache, &lower1, &lower2, false);
- *output2 = make_range(typcache, &upper2, &upper1, false);
+ *output1 = make_range(typcache, &lower1, &lower2, false, NULL);
+ *output2 = make_range(typcache, &upper2, &upper1, false, NULL);
return true;
}
int4range_canonical(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
+ Node *escontext = fcinfo->context;
TypeCacheEntry *typcache;
RangeBound lower;
RangeBound upper;
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;
}
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;
}
- PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper, false));
+ PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper,
+ false, escontext));
}
Datum
int8range_canonical(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
+ Node *escontext = fcinfo->context;
TypeCacheEntry *typcache;
RangeBound lower;
RangeBound upper;
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;
}
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;
}
- PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper, false));
+ PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper,
+ false, escontext));
}
Datum
daterange_canonical(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
+ Node *escontext = fcinfo->context;
TypeCacheEntry *typcache;
RangeBound lower;
RangeBound upper;
if (!lower.infinite && !DATE_NOT_FINITE(DatumGetDateADT(lower.val)) &&
!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;
}
if (!upper.infinite && !DATE_NOT_FINITE(DatumGetDateADT(upper.val)) &&
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;
}
- PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper, false));
+ PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper,
+ false, escontext));
}
/*
*/
RangeType *
range_serialize(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
- bool empty)
+ bool empty, struct Node *escontext)
{
RangeType *range;
int cmp;
/* error check: if lower bound value is above upper, it's wrong */
if (cmp > 0)
- ereport(ERROR,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("range lower bound must be less than or equal to range upper bound")));
*/
RangeType *
make_range(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
- bool empty)
+ bool empty, struct Node *escontext)
{
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 ... */
if (OidIsValid(typcache->rng_canonical_finfo.fn_oid) &&
!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;
}
upper.inclusive = false;
upper.lower = false;
- return make_range(typcache, &lower, &upper, true);
+ return make_range(typcache, &lower, &upper, true, NULL);
}
* Within a <string>, special characters (such as comma, parenthesis, or
* 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.
+ *
+ * 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,
- char **ubound_str)
+ char **ubound_str, Node *escontext)
{
const char *ptr = string;
bool infinite;
/* should have consumed everything */
if (*ptr != '\0')
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"",
string),
errdetail("Junk after \"empty\" key word.")));
- return;
+ return true;
}
if (*ptr == '[')
else if (*ptr == '(')
ptr++;
else
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"",
string),
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)
*flags |= RANGE_LB_INF;
if (*ptr == ',')
ptr++;
else
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"",
string),
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)
*flags |= RANGE_UB_INF;
else if (*ptr == ')')
ptr++;
else /* must be a comma */
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"",
string),
ptr++;
if (*ptr != '\0')
- ereport(ERROR,
+ ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"",
string),
errdetail("Junk after right parenthesis or bracket.")));
+
+ return true;
}
/*
* *infinite: set true if no bound, else false
*
* 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 *
range_parse_bound(const char *string, const char *ptr,
- char **bound_str, bool *infinite)
+ char **bound_str, bool *infinite, Node *escontext)
{
StringInfoData buf;
char ch = *ptr++;
if (ch == '\0')
- ereport(ERROR,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"",
string),
if (ch == '\\')
{
if (*ptr == '\0')
- ereport(ERROR,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("malformed range literal: \"%s\"",
string),