|
|
|
|
@@ -35,6 +35,7 @@
|
|
|
|
|
#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"
|
|
|
|
|
@@ -55,10 +56,11 @@ typedef struct RangeIOData
|
|
|
|
|
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);
|
|
|
|
|
@@ -80,6 +82,7 @@ range_in(PG_FUNCTION_ARGS)
|
|
|
|
|
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;
|
|
|
|
|
@@ -93,15 +96,20 @@ range_in(PG_FUNCTION_ARGS)
|
|
|
|
|
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;
|
|
|
|
|
@@ -111,7 +119,8 @@ range_in(PG_FUNCTION_ARGS)
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
@@ -234,7 +243,8 @@ range_recv(PG_FUNCTION_ARGS)
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
@@ -378,7 +388,7 @@ range_constructor2(PG_FUNCTION_ARGS)
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
@@ -415,7 +425,7 @@ range_constructor3(PG_FUNCTION_ARGS)
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
@@ -766,7 +776,7 @@ bounds_adjacent(TypeCacheEntry *typcache, RangeBound boundA, RangeBound boundB)
|
|
|
|
|
/* 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)
|
|
|
|
|
@@ -1012,14 +1022,14 @@ range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
|
|
|
|
|
{
|
|
|
|
|
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");
|
|
|
|
|
@@ -1073,7 +1083,7 @@ range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
|
|
|
|
|
else
|
|
|
|
|
result_upper = &upper2;
|
|
|
|
|
|
|
|
|
|
return make_range(typcache, result_lower, result_upper, false);
|
|
|
|
|
return make_range(typcache, result_lower, result_upper, false, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
@@ -1149,7 +1159,7 @@ range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const Ra
|
|
|
|
|
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 */
|
|
|
|
|
@@ -1187,8 +1197,8 @@ range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeT
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1446,6 +1456,7 @@ Datum
|
|
|
|
|
int4range_canonical(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
RangeType *r = PG_GETARG_RANGE_P(0);
|
|
|
|
|
Node *escontext = fcinfo->context;
|
|
|
|
|
TypeCacheEntry *typcache;
|
|
|
|
|
RangeBound lower;
|
|
|
|
|
RangeBound upper;
|
|
|
|
|
@@ -1460,23 +1471,39 @@ int4range_canonical(PG_FUNCTION_ARGS)
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
@@ -1491,23 +1518,39 @@ int8range_canonical(PG_FUNCTION_ARGS)
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
@@ -1523,18 +1566,35 @@ daterange_canonical(PG_FUNCTION_ARGS)
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
@@ -1657,7 +1717,7 @@ range_get_typcache(FunctionCallInfo fcinfo, Oid rngtypid)
|
|
|
|
|
*/
|
|
|
|
|
RangeType *
|
|
|
|
|
range_serialize(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
|
|
|
|
|
bool empty)
|
|
|
|
|
bool empty, struct Node *escontext)
|
|
|
|
|
{
|
|
|
|
|
RangeType *range;
|
|
|
|
|
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 */
|
|
|
|
|
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")));
|
|
|
|
|
|
|
|
|
|
@@ -1882,17 +1942,41 @@ range_set_contain_empty(RangeType *range)
|
|
|
|
|
*/
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
@@ -2085,7 +2169,7 @@ make_empty_range(TypeCacheEntry *typcache)
|
|
|
|
|
upper.inclusive = 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
|
|
|
|
|
* 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;
|
|
|
|
|
@@ -2200,13 +2287,13 @@ range_parse(const char *string, char *flags, char **lbound_str,
|
|
|
|
|
|
|
|
|
|
/* 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 == '[')
|
|
|
|
|
@@ -2217,26 +2304,30 @@ range_parse(const char *string, char *flags, char **lbound_str,
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
@@ -2248,7 +2339,7 @@ range_parse(const char *string, char *flags, char **lbound_str,
|
|
|
|
|
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),
|
|
|
|
|
@@ -2259,11 +2350,13 @@ range_parse(const char *string, char *flags, char **lbound_str,
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
@@ -2279,10 +2372,11 @@ range_parse(const char *string, char *flags, char **lbound_str,
|
|
|
|
|
* *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;
|
|
|
|
|
|
|
|
|
|
@@ -2303,7 +2397,7 @@ range_parse_bound(const char *string, const char *ptr,
|
|
|
|
|
char ch = *ptr++;
|
|
|
|
|
|
|
|
|
|
if (ch == '\0')
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
ereturn(escontext, NULL,
|
|
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
|
|
|
errmsg("malformed range literal: \"%s\"",
|
|
|
|
|
string),
|
|
|
|
|
@@ -2311,7 +2405,7 @@ range_parse_bound(const char *string, const char *ptr,
|
|
|
|
|
if (ch == '\\')
|
|
|
|
|
{
|
|
|
|
|
if (*ptr == '\0')
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
ereturn(escontext, NULL,
|
|
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
|
|
|
errmsg("malformed range literal: \"%s\"",
|
|
|
|
|
string),
|
|
|
|
|
|