Commit 04da3232 authored by Tom Lane's avatar Tom Lane

Improve caching in range type I/O functions.

Cache the the element type's I/O info across calls, not only the range
type's info.  In passing, also clean up hash_range a bit more.
parent 37ee4b75
...@@ -15,29 +15,30 @@ ...@@ -15,29 +15,30 @@
#include "postgres.h" #include "postgres.h"
#include "access/hash.h" #include "access/hash.h"
#include "access/nbtree.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_range.h"
#include "catalog/pg_type.h"
#include "fmgr.h"
#include "lib/stringinfo.h" #include "lib/stringinfo.h"
#include "libpq/pqformat.h" #include "libpq/pqformat.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/date.h" #include "utils/date.h"
#include "utils/fmgroids.h"
#include "utils/int8.h" #include "utils/int8.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/rangetypes.h" #include "utils/rangetypes.h"
#include "utils/syscache.h"
#include "utils/timestamp.h" #include "utils/timestamp.h"
#include "utils/typcache.h"
#define RANGE_EMPTY_LITERAL "empty" #define RANGE_EMPTY_LITERAL "empty"
#define RANGE_DEFAULT_FLAGS "[)" /* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
TypeCacheEntry *typcache; /* range type's typcache entry */
Oid typiofunc; /* element type's I/O function */
Oid typioparam; /* element type's I/O parameter */
FmgrInfo proc; /* lookup result for typiofunc */
} RangeIOData;
static RangeIOData *get_range_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
IOFuncSelector func);
static char range_parse_flags(const char *flags_str); static char range_parse_flags(const char *flags_str);
static void range_parse(char *input_str, char *flags, char **lbound_str, static void range_parse(char *input_str, char *flags, char **lbound_str,
char **ubound_str); char **ubound_str);
...@@ -66,31 +67,25 @@ range_in(PG_FUNCTION_ARGS) ...@@ -66,31 +67,25 @@ range_in(PG_FUNCTION_ARGS)
Oid rngtypoid = PG_GETARG_OID(1); Oid rngtypoid = PG_GETARG_OID(1);
Oid typmod = PG_GETARG_INT32(2); Oid typmod = PG_GETARG_INT32(2);
RangeType *range; RangeType *range;
TypeCacheEntry *typcache; RangeIOData *cache;
char flags; char flags;
char *lbound_str; char *lbound_str;
char *ubound_str; char *ubound_str;
regproc subInput;
FmgrInfo subInputFn;
Oid ioParam;
RangeBound lower; RangeBound lower;
RangeBound upper; RangeBound upper;
typcache = range_get_typcache(fcinfo, rngtypoid); cache = get_range_io_data(fcinfo, rngtypoid, IOFunc_input);
/* parse */ /* parse */
range_parse(input_str, &flags, &lbound_str, &ubound_str); range_parse(input_str, &flags, &lbound_str, &ubound_str);
/* input */ /* call element type's input function */
getTypeInputInfo(typcache->rngelemtype->type_id, &subInput, &ioParam);
fmgr_info(subInput, &subInputFn);
if (RANGE_HAS_LBOUND(flags)) if (RANGE_HAS_LBOUND(flags))
lower.val = InputFunctionCall(&subInputFn, lbound_str, lower.val = InputFunctionCall(&cache->proc, lbound_str,
ioParam, typmod); cache->typioparam, typmod);
if (RANGE_HAS_UBOUND(flags)) if (RANGE_HAS_UBOUND(flags))
upper.val = InputFunctionCall(&subInputFn, ubound_str, upper.val = InputFunctionCall(&cache->proc, ubound_str,
ioParam, typmod); cache->typioparam, typmod);
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;
...@@ -100,7 +95,7 @@ range_in(PG_FUNCTION_ARGS) ...@@ -100,7 +95,7 @@ range_in(PG_FUNCTION_ARGS)
upper.lower = false; upper.lower = false;
/* serialize and canonicalize */ /* serialize and canonicalize */
range = make_range(typcache, &lower, &upper, flags & RANGE_EMPTY); range = make_range(cache->typcache, &lower, &upper, flags & RANGE_EMPTY);
PG_RETURN_RANGE(range); PG_RETURN_RANGE(range);
} }
...@@ -109,39 +104,26 @@ Datum ...@@ -109,39 +104,26 @@ Datum
range_out(PG_FUNCTION_ARGS) range_out(PG_FUNCTION_ARGS)
{ {
RangeType *range = PG_GETARG_RANGE(0); RangeType *range = PG_GETARG_RANGE(0);
TypeCacheEntry *typcache;
char *output_str; char *output_str;
regproc subOutput; RangeIOData *cache;
FmgrInfo subOutputFn; char flags;
bool isVarlena;
char flags = 0;
char *lbound_str = NULL; char *lbound_str = NULL;
char *ubound_str = NULL; char *ubound_str = NULL;
bool empty;
RangeBound lower; RangeBound lower;
RangeBound upper; RangeBound upper;
bool empty;
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(range)); cache = get_range_io_data(fcinfo, RangeTypeGetOid(range), IOFunc_output);
/* deserialize */ /* deserialize */
range_deserialize(typcache, range, &lower, &upper, &empty); range_deserialize(cache->typcache, range, &lower, &upper, &empty);
flags = range_get_flags(range);
if (empty)
flags |= RANGE_EMPTY;
flags |= lower.inclusive ? RANGE_LB_INC : 0;
flags |= lower.infinite ? RANGE_LB_INF : 0;
flags |= upper.inclusive ? RANGE_UB_INC : 0;
flags |= upper.infinite ? RANGE_UB_INF : 0;
/* output */
getTypeOutputInfo(typcache->rngelemtype->type_id, &subOutput, &isVarlena);
fmgr_info(subOutput, &subOutputFn);
/* call element type's output function */
if (RANGE_HAS_LBOUND(flags)) if (RANGE_HAS_LBOUND(flags))
lbound_str = OutputFunctionCall(&subOutputFn, lower.val); lbound_str = OutputFunctionCall(&cache->proc, lower.val);
if (RANGE_HAS_UBOUND(flags)) if (RANGE_HAS_UBOUND(flags))
ubound_str = OutputFunctionCall(&subOutputFn, upper.val); ubound_str = OutputFunctionCall(&cache->proc, upper.val);
/* deparse */ /* deparse */
output_str = range_deparse(flags, lbound_str, ubound_str); output_str = range_deparse(flags, lbound_str, ubound_str);
...@@ -150,11 +132,10 @@ range_out(PG_FUNCTION_ARGS) ...@@ -150,11 +132,10 @@ range_out(PG_FUNCTION_ARGS)
} }
/* /*
* Binary representation: The first byte is the flags, then 4 bytes are the * Binary representation: The first byte is the flags, then the lower bound
* range type Oid, then the lower bound (if present) then the upper bound (if * (if present), then the upper bound (if present). Each bound is represented
* present). Each bound is represented by a 4-byte length header and the binary * by a 4-byte length header and the binary representation of that bound (as
* representation of that bound (as returned by a call to the send function for * returned by a call to the send function for the subtype).
* the subtype).
*/ */
Datum Datum
...@@ -164,19 +145,27 @@ range_recv(PG_FUNCTION_ARGS) ...@@ -164,19 +145,27 @@ range_recv(PG_FUNCTION_ARGS)
Oid rngtypoid = PG_GETARG_OID(1); Oid rngtypoid = PG_GETARG_OID(1);
int32 typmod = PG_GETARG_INT32(2); int32 typmod = PG_GETARG_INT32(2);
RangeType *range; RangeType *range;
TypeCacheEntry *typcache; RangeIOData *cache;
Oid subrecv;
Oid ioparam;
char flags; char flags;
RangeBound lower; RangeBound lower;
RangeBound upper; RangeBound upper;
typcache = range_get_typcache(fcinfo, rngtypoid); cache = get_range_io_data(fcinfo, rngtypoid, IOFunc_receive);
/* receive the flags... */
flags = (unsigned char) pq_getmsgbyte(buf); flags = (unsigned char) pq_getmsgbyte(buf);
getTypeBinaryInputInfo(typcache->rngelemtype->type_id, &subrecv, &ioparam); /*
* mask out any unsupported flags, particularly RANGE_xB_NULL which would
* confuse following tests.
*/
flags &= (RANGE_EMPTY |
RANGE_LB_INC |
RANGE_LB_INF |
RANGE_UB_INC |
RANGE_UB_INF);
/* receive the bounds ... */
if (RANGE_HAS_LBOUND(flags)) if (RANGE_HAS_LBOUND(flags))
{ {
uint32 bound_len = pq_getmsgint(buf, 4); uint32 bound_len = pq_getmsgint(buf, 4);
...@@ -186,10 +175,10 @@ range_recv(PG_FUNCTION_ARGS) ...@@ -186,10 +175,10 @@ range_recv(PG_FUNCTION_ARGS)
initStringInfo(&bound_buf); initStringInfo(&bound_buf);
appendBinaryStringInfo(&bound_buf, bound_data, bound_len); appendBinaryStringInfo(&bound_buf, bound_data, bound_len);
lower.val = OidReceiveFunctionCall(subrecv, lower.val = ReceiveFunctionCall(&cache->proc,
&bound_buf, &bound_buf,
ioparam, cache->typioparam,
typmod); typmod);
pfree(bound_buf.data); pfree(bound_buf.data);
} }
else else
...@@ -204,10 +193,10 @@ range_recv(PG_FUNCTION_ARGS) ...@@ -204,10 +193,10 @@ range_recv(PG_FUNCTION_ARGS)
initStringInfo(&bound_buf); initStringInfo(&bound_buf);
appendBinaryStringInfo(&bound_buf, bound_data, bound_len); appendBinaryStringInfo(&bound_buf, bound_data, bound_len);
upper.val = OidReceiveFunctionCall(subrecv, upper.val = ReceiveFunctionCall(&cache->proc,
&bound_buf, &bound_buf,
ioparam, cache->typioparam,
typmod); typmod);
pfree(bound_buf.data); pfree(bound_buf.data);
} }
else else
...@@ -215,6 +204,7 @@ range_recv(PG_FUNCTION_ARGS) ...@@ -215,6 +204,7 @@ range_recv(PG_FUNCTION_ARGS)
pq_getmsgend(buf); pq_getmsgend(buf);
/* finish constructing RangeBound representation */
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;
lower.lower = true; lower.lower = true;
...@@ -223,7 +213,7 @@ range_recv(PG_FUNCTION_ARGS) ...@@ -223,7 +213,7 @@ range_recv(PG_FUNCTION_ARGS)
upper.lower = false; upper.lower = false;
/* serialize and canonicalize */ /* serialize and canonicalize */
range = make_range(typcache, &lower, &upper, flags & RANGE_EMPTY); range = make_range(cache->typcache, &lower, &upper, flags & RANGE_EMPTY);
PG_RETURN_RANGE(range); PG_RETURN_RANGE(range);
} }
...@@ -233,37 +223,27 @@ range_send(PG_FUNCTION_ARGS) ...@@ -233,37 +223,27 @@ range_send(PG_FUNCTION_ARGS)
{ {
RangeType *range = PG_GETARG_RANGE(0); RangeType *range = PG_GETARG_RANGE(0);
StringInfo buf = makeStringInfo(); StringInfo buf = makeStringInfo();
TypeCacheEntry *typcache; RangeIOData *cache;
char flags = 0; char flags;
RangeBound lower; RangeBound lower;
RangeBound upper; RangeBound upper;
bool empty; bool empty;
Oid subsend;
bool typIsVarlena;
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(range)); cache = get_range_io_data(fcinfo, RangeTypeGetOid(range), IOFunc_send);
getTypeBinaryOutputInfo(typcache->rngelemtype->type_id, /* deserialize */
&subsend, &typIsVarlena); range_deserialize(cache->typcache, range, &lower, &upper, &empty);
flags = range_get_flags(range);
/* construct output */
pq_begintypsend(buf); pq_begintypsend(buf);
range_deserialize(typcache, range, &lower, &upper, &empty);
if (empty)
flags |= RANGE_EMPTY;
flags |= lower.inclusive ? RANGE_LB_INC : 0;
flags |= lower.infinite ? RANGE_LB_INF : 0;
flags |= upper.inclusive ? RANGE_UB_INC : 0;
flags |= upper.infinite ? RANGE_UB_INF : 0;
pq_sendbyte(buf, flags); pq_sendbyte(buf, flags);
if (RANGE_HAS_LBOUND(flags)) if (RANGE_HAS_LBOUND(flags))
{ {
Datum bound = PointerGetDatum(OidSendFunctionCall(subsend, Datum bound = PointerGetDatum(SendFunctionCall(&cache->proc,
lower.val)); lower.val));
uint32 bound_len = VARSIZE(bound) - VARHDRSZ; uint32 bound_len = VARSIZE(bound) - VARHDRSZ;
char *bound_data = VARDATA(bound); char *bound_data = VARDATA(bound);
...@@ -273,8 +253,8 @@ range_send(PG_FUNCTION_ARGS) ...@@ -273,8 +253,8 @@ range_send(PG_FUNCTION_ARGS)
if (RANGE_HAS_UBOUND(flags)) if (RANGE_HAS_UBOUND(flags))
{ {
Datum bound = PointerGetDatum(OidSendFunctionCall(subsend, Datum bound = PointerGetDatum(SendFunctionCall(&cache->proc,
upper.val)); upper.val));
uint32 bound_len = VARSIZE(bound) - VARHDRSZ; uint32 bound_len = VARSIZE(bound) - VARHDRSZ;
char *bound_data = VARDATA(bound); char *bound_data = VARDATA(bound);
...@@ -285,6 +265,64 @@ range_send(PG_FUNCTION_ARGS) ...@@ -285,6 +265,64 @@ range_send(PG_FUNCTION_ARGS)
PG_RETURN_BYTEA_P(pq_endtypsend(buf)); PG_RETURN_BYTEA_P(pq_endtypsend(buf));
} }
/*
* get_range_io_data: get cached information needed for range type I/O
*
* The range I/O functions need a bit more cached info than other range
* functions, so they store a RangeIOData struct in fn_extra, not just a
* pointer to a type cache entry.
*/
static RangeIOData *
get_range_io_data(FunctionCallInfo fcinfo, Oid rngtypid, IOFuncSelector func)
{
RangeIOData *cache = (RangeIOData *) fcinfo->flinfo->fn_extra;
if (cache == NULL || cache->typcache->type_id != rngtypid)
{
int16 typlen;
bool typbyval;
char typalign;
char typdelim;
cache = (RangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(RangeIOData));
cache->typcache = lookup_type_cache(rngtypid, TYPECACHE_RANGE_INFO);
if (cache->typcache->rngelemtype == NULL)
elog(ERROR, "type %u is not a range type", rngtypid);
/* get_type_io_data does more than we need, but is convenient */
get_type_io_data(cache->typcache->rngelemtype->type_id,
func,
&typlen,
&typbyval,
&typalign,
&typdelim,
&cache->typioparam,
&cache->typiofunc);
if (!OidIsValid(cache->typiofunc))
{
/* this could only happen for receive or send */
if (func == IOFunc_receive)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("no binary input function available for type %s",
format_type_be(cache->typcache->rngelemtype->type_id))));
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("no binary output function available for type %s",
format_type_be(cache->typcache->rngelemtype->type_id))));
}
fmgr_info_cxt(cache->typiofunc, &cache->proc,
fcinfo->flinfo->fn_mcxt);
fcinfo->flinfo->fn_extra = (void *) cache;
}
return cache;
}
/* /*
*---------------------------------------------------------- *----------------------------------------------------------
...@@ -360,20 +398,17 @@ range_constructor2(PG_FUNCTION_ARGS) ...@@ -360,20 +398,17 @@ range_constructor2(PG_FUNCTION_ARGS)
TypeCacheEntry *typcache; TypeCacheEntry *typcache;
RangeBound lower; RangeBound lower;
RangeBound upper; RangeBound upper;
char flags;
typcache = range_get_typcache(fcinfo, rngtypid); typcache = range_get_typcache(fcinfo, rngtypid);
flags = range_parse_flags(RANGE_DEFAULT_FLAGS);
lower.val = PG_ARGISNULL(0) ? (Datum) 0 : arg1; lower.val = PG_ARGISNULL(0) ? (Datum) 0 : arg1;
lower.infinite = PG_ARGISNULL(0); lower.infinite = PG_ARGISNULL(0);
lower.inclusive = (flags & RANGE_LB_INC) != 0; lower.inclusive = true;
lower.lower = true; lower.lower = true;
upper.val = PG_ARGISNULL(1) ? (Datum) 0 : arg2; upper.val = PG_ARGISNULL(1) ? (Datum) 0 : arg2;
upper.infinite = PG_ARGISNULL(1); upper.infinite = PG_ARGISNULL(1);
upper.inclusive = (flags & RANGE_UB_INC) != 0; upper.inclusive = false;
upper.lower = false; upper.lower = false;
range = make_range(typcache, &lower, &upper, false); range = make_range(typcache, &lower, &upper, false);
...@@ -1085,66 +1120,54 @@ Datum ...@@ -1085,66 +1120,54 @@ Datum
hash_range(PG_FUNCTION_ARGS) hash_range(PG_FUNCTION_ARGS)
{ {
RangeType *r = PG_GETARG_RANGE(0); RangeType *r = PG_GETARG_RANGE(0);
uint32 result;
TypeCacheEntry *typcache; TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower; RangeBound lower;
RangeBound upper; RangeBound upper;
bool empty; bool empty;
char flags = 0; char flags;
uint32 lower_hash = 0; uint32 lower_hash;
uint32 upper_hash = 0; uint32 upper_hash;
uint32 result = 0;
TypeCacheEntry *subtypcache;
FunctionCallInfoData locfcinfo;
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r)); typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty); range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
if (empty)
flags |= RANGE_EMPTY;
flags |= lower.inclusive ? RANGE_LB_INC : 0;
flags |= lower.infinite ? RANGE_LB_INF : 0;
flags |= upper.inclusive ? RANGE_UB_INC : 0;
flags |= upper.infinite ? RANGE_UB_INF : 0;
/* /*
* Look up the element type's hash function, if not done already. * Look up the element type's hash function, if not done already.
*/ */
subtypcache = typcache->rngelemtype; scache = typcache->rngelemtype;
if (!OidIsValid(subtypcache->hash_proc_finfo.fn_oid)) if (!OidIsValid(scache->hash_proc_finfo.fn_oid))
{ {
subtypcache = lookup_type_cache(subtypcache->type_id, scache = lookup_type_cache(scache->type_id, TYPECACHE_HASH_PROC_FINFO);
TYPECACHE_HASH_PROC_FINFO); if (!OidIsValid(scache->hash_proc_finfo.fn_oid))
if (!OidIsValid(subtypcache->hash_proc_finfo.fn_oid))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION), (errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("could not identify a hash function for type %s", errmsg("could not identify a hash function for type %s",
format_type_be(subtypcache->type_id)))); format_type_be(scache->type_id))));
} }
/* /*
* Apply the hash function to each bound (the hash function shouldn't care * Apply the hash function to each bound.
* about the collation).
*/ */
InitFunctionCallInfoData(locfcinfo, &subtypcache->hash_proc_finfo, 1,
InvalidOid, NULL, NULL);
if (RANGE_HAS_LBOUND(flags)) if (RANGE_HAS_LBOUND(flags))
{ lower_hash = DatumGetUInt32(FunctionCall1Coll(&scache->hash_proc_finfo,
locfcinfo.arg[0] = lower.val; typcache->rng_collation,
locfcinfo.argnull[0] = false; lower.val));
locfcinfo.isnull = false; else
lower_hash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo)); lower_hash = 0;
}
if (RANGE_HAS_UBOUND(flags)) if (RANGE_HAS_UBOUND(flags))
{ upper_hash = DatumGetUInt32(FunctionCall1Coll(&scache->hash_proc_finfo,
locfcinfo.arg[0] = upper.val; typcache->rng_collation,
locfcinfo.argnull[0] = false; upper.val));
locfcinfo.isnull = false; else
upper_hash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo)); upper_hash = 0;
}
/* Merge hashes of flags and bounds */
result = hash_uint32((uint32) flags); result = hash_uint32((uint32) flags);
result ^= lower_hash; result ^= lower_hash;
result = (result << 1) | (result >> 31); result = (result << 1) | (result >> 31);
...@@ -1420,15 +1443,18 @@ range_serialize(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper, ...@@ -1420,15 +1443,18 @@ range_serialize(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
/* construct flags value */ /* construct flags value */
if (empty) if (empty)
flags |= RANGE_EMPTY; flags |= RANGE_EMPTY;
else if (range_cmp_bounds(typcache, lower, upper) > 0) else
ereport(ERROR, {
(errcode(ERRCODE_DATA_EXCEPTION), if (range_cmp_bounds(typcache, lower, upper) > 0)
errmsg("range lower bound must be less than or equal to range upper bound"))); ereport(ERROR,
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("range lower bound must be less than or equal to range upper bound")));
flags |= lower->inclusive ? RANGE_LB_INC : 0; flags |= lower->inclusive ? RANGE_LB_INC : 0;
flags |= lower->infinite ? RANGE_LB_INF : 0; flags |= lower->infinite ? RANGE_LB_INF : 0;
flags |= upper->inclusive ? RANGE_UB_INC : 0; flags |= upper->inclusive ? RANGE_UB_INC : 0;
flags |= upper->infinite ? RANGE_UB_INF : 0; flags |= upper->infinite ? RANGE_UB_INF : 0;
}
/* Count space for varlena header and range type's OID */ /* Count space for varlena header and range type's OID */
msize = sizeof(RangeType); msize = sizeof(RangeType);
...@@ -1731,7 +1757,7 @@ range_parse(char *string, char *flags, char **lbound_str, ...@@ -1731,7 +1757,7 @@ range_parse(char *string, char *flags, char **lbound_str,
*flags = 0; *flags = 0;
/* consume whitespace */ /* consume whitespace */
while (*ptr != '\0' && isspace(*ptr)) while (*ptr != '\0' && isspace((unsigned char) *ptr))
ptr++; ptr++;
/* check for empty range */ /* check for empty range */
...@@ -1743,7 +1769,7 @@ range_parse(char *string, char *flags, char **lbound_str, ...@@ -1743,7 +1769,7 @@ range_parse(char *string, char *flags, char **lbound_str,
ptr += strlen(RANGE_EMPTY_LITERAL); ptr += strlen(RANGE_EMPTY_LITERAL);
/* the rest should be whitespace */ /* the rest should be whitespace */
while (*ptr != '\0' && isspace(*ptr)) while (*ptr != '\0' && isspace((unsigned char) *ptr))
ptr++; ptr++;
/* should have consumed everything */ /* should have consumed everything */
...@@ -1812,7 +1838,7 @@ range_parse(char *string, char *flags, char **lbound_str, ...@@ -1812,7 +1838,7 @@ range_parse(char *string, char *flags, char **lbound_str,
} }
/* consume whitespace */ /* consume whitespace */
while (*ptr != '\0' && isspace(*ptr)) while (*ptr != '\0' && isspace((unsigned char) *ptr))
ptr++; ptr++;
if (*ptr != '\0') if (*ptr != '\0')
...@@ -1885,16 +1911,21 @@ range_parse_bound(char *string, char *ptr, char **bound_str, bool *infinite) ...@@ -1885,16 +1911,21 @@ range_parse_bound(char *string, char *ptr, char **bound_str, bool *infinite)
return ptr; return ptr;
} }
/*
* Convert a deserialized range value to text form
*
* Result is a palloc'd string
*/
static char * static char *
range_deparse(char flags, char *lbound_str, char *ubound_str) range_deparse(char flags, char *lbound_str, char *ubound_str)
{ {
StringInfoData buf; StringInfoData buf;
initStringInfo(&buf);
if (flags & RANGE_EMPTY) if (flags & RANGE_EMPTY)
return pstrdup(RANGE_EMPTY_LITERAL); return pstrdup(RANGE_EMPTY_LITERAL);
initStringInfo(&buf);
appendStringInfoString(&buf, (flags & RANGE_LB_INC) ? "[" : "("); appendStringInfoString(&buf, (flags & RANGE_LB_INC) ? "[" : "(");
if (RANGE_HAS_LBOUND(flags)) if (RANGE_HAS_LBOUND(flags))
...@@ -1910,6 +1941,11 @@ range_deparse(char flags, char *lbound_str, char *ubound_str) ...@@ -1910,6 +1941,11 @@ range_deparse(char flags, char *lbound_str, char *ubound_str)
return buf.data; return buf.data;
} }
/*
* Helper for range_deparse: quote a bound value as needed
*
* Result is a palloc'd string
*/
static char * static char *
range_bound_escape(char *value) range_bound_escape(char *value)
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment