Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * rangetypes.c
4 : : * I/O functions, operators, and support functions for range types.
5 : : *
6 : : * The stored (serialized) format of a range value is:
7 : : *
8 : : * 4 bytes: varlena header
9 : : * 4 bytes: range type's OID
10 : : * Lower boundary value, if any, aligned according to subtype's typalign
11 : : * Upper boundary value, if any, aligned according to subtype's typalign
12 : : * 1 byte for flags
13 : : *
14 : : * This representation is chosen to avoid needing any padding before the
15 : : * lower boundary value, even when it requires double alignment. We can
16 : : * expect that the varlena header is presented to us on a suitably aligned
17 : : * boundary (possibly after detoasting), and then the lower boundary is too.
18 : : * Note that this means we can't work with a packed (short varlena header)
19 : : * value; we must detoast it first.
20 : : *
21 : : *
22 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
23 : : * Portions Copyright (c) 1994, Regents of the University of California
24 : : *
25 : : *
26 : : * IDENTIFICATION
27 : : * src/backend/utils/adt/rangetypes.c
28 : : *
29 : : *-------------------------------------------------------------------------
30 : : */
31 : : #include "postgres.h"
32 : :
33 : : #include "common/hashfn.h"
34 : : #include "libpq/pqformat.h"
35 : : #include "miscadmin.h"
36 : : #include "nodes/makefuncs.h"
37 : : #include "nodes/miscnodes.h"
38 : : #include "nodes/supportnodes.h"
39 : : #include "optimizer/clauses.h"
40 : : #include "optimizer/cost.h"
41 : : #include "optimizer/optimizer.h"
42 : : #include "utils/builtins.h"
43 : : #include "utils/date.h"
44 : : #include "utils/lsyscache.h"
45 : : #include "utils/rangetypes.h"
46 : : #include "utils/sortsupport.h"
47 : : #include "utils/timestamp.h"
48 : : #include "varatt.h"
49 : :
50 : :
51 : : /* fn_extra cache entry for one of the range I/O functions */
52 : : typedef struct RangeIOData
53 : : {
54 : : TypeCacheEntry *typcache; /* range type's typcache entry */
55 : : FmgrInfo typioproc; /* element type's I/O function */
56 : : Oid typioparam; /* element type's I/O parameter */
57 : : } RangeIOData;
58 : :
59 : :
60 : : static RangeIOData *get_range_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
61 : : IOFuncSelector func);
62 : : static int range_fast_cmp(Datum a, Datum b, SortSupport ssup);
63 : : static char range_parse_flags(const char *flags_str);
64 : : static bool range_parse(const char *string, char *flags, char **lbound_str,
65 : : char **ubound_str, Node *escontext);
66 : : static const char *range_parse_bound(const char *string, const char *ptr,
67 : : char **bound_str, bool *infinite,
68 : : Node *escontext);
69 : : static char *range_deparse(char flags, const char *lbound_str,
70 : : const char *ubound_str);
71 : : static char *range_bound_escape(const char *value);
72 : : static Size datum_compute_size(Size data_length, Datum val, bool typbyval,
73 : : char typalign, int16 typlen, char typstorage);
74 : : static Pointer datum_write(Pointer ptr, Datum datum, bool typbyval,
75 : : char typalign, int16 typlen, char typstorage);
76 : : static Node *find_simplified_clause(PlannerInfo *root,
77 : : Expr *rangeExpr, Expr *elemExpr);
78 : : static Expr *build_bound_expr(Expr *elemExpr, Datum val,
79 : : bool isLowerBound, bool isInclusive,
80 : : TypeCacheEntry *typeCache,
81 : : Oid opfamily, Oid rng_collation);
82 : :
83 : :
84 : : /*
85 : : *----------------------------------------------------------
86 : : * I/O FUNCTIONS
87 : : *----------------------------------------------------------
88 : : */
89 : :
90 : : Datum
5108 heikki.linnakangas@i 91 :CBC 3658 : range_in(PG_FUNCTION_ARGS)
92 : : {
5097 bruce@momjian.us 93 : 3658 : char *input_str = PG_GETARG_CSTRING(0);
94 : 3658 : Oid rngtypoid = PG_GETARG_OID(1);
95 : 3658 : Oid typmod = PG_GETARG_INT32(2);
1048 tgl@sss.pgh.pa.us 96 : 3658 : Node *escontext = fcinfo->context;
97 : : RangeType *range;
98 : : RangeIOData *cache;
99 : : char flags;
100 : : char *lbound_str;
101 : : char *ubound_str;
102 : : RangeBound lower;
103 : : RangeBound upper;
104 : :
3676 noah@leadboat.com 105 : 3658 : check_stack_depth(); /* recurses when subtype is a range type */
106 : :
5096 tgl@sss.pgh.pa.us 107 : 3658 : cache = get_range_io_data(fcinfo, rngtypoid, IOFunc_input);
108 : :
109 : : /* parse */
1048 110 [ + + ]: 3658 : if (!range_parse(input_str, &flags, &lbound_str, &ubound_str, escontext))
111 : 9 : PG_RETURN_NULL();
112 : :
113 : : /* call element type's input function */
5108 heikki.linnakangas@i 114 [ + + ]: 3610 : if (RANGE_HAS_LBOUND(flags))
1048 tgl@sss.pgh.pa.us 115 [ - + ]: 3244 : if (!InputFunctionCallSafe(&cache->typioproc, lbound_str,
116 : : cache->typioparam, typmod,
117 : : escontext, &lower.val))
1048 tgl@sss.pgh.pa.us 118 :UBC 0 : PG_RETURN_NULL();
5108 heikki.linnakangas@i 119 [ + + ]:CBC 3610 : if (RANGE_HAS_UBOUND(flags))
1048 tgl@sss.pgh.pa.us 120 [ + + ]: 3196 : if (!InputFunctionCallSafe(&cache->typioproc, ubound_str,
121 : : cache->typioparam, typmod,
122 : : escontext, &upper.val))
123 : 12 : PG_RETURN_NULL();
124 : :
5096 125 : 3598 : lower.infinite = (flags & RANGE_LB_INF) != 0;
126 : 3598 : lower.inclusive = (flags & RANGE_LB_INC) != 0;
127 : 3598 : lower.lower = true;
128 : 3598 : upper.infinite = (flags & RANGE_UB_INF) != 0;
129 : 3598 : upper.inclusive = (flags & RANGE_UB_INC) != 0;
130 : 3598 : upper.lower = false;
131 : :
132 : : /* serialize and canonicalize */
1048 133 : 3598 : range = make_range(cache->typcache, &lower, &upper,
134 : 3598 : flags & RANGE_EMPTY, escontext);
135 : :
2962 136 : 3589 : PG_RETURN_RANGE_P(range);
137 : : }
138 : :
139 : : Datum
5108 heikki.linnakangas@i 140 : 54128 : range_out(PG_FUNCTION_ARGS)
141 : : {
2962 tgl@sss.pgh.pa.us 142 : 54128 : RangeType *range = PG_GETARG_RANGE_P(0);
143 : : char *output_str;
144 : : RangeIOData *cache;
145 : : char flags;
5108 heikki.linnakangas@i 146 : 54128 : char *lbound_str = NULL;
147 : 54128 : char *ubound_str = NULL;
148 : : RangeBound lower;
149 : : RangeBound upper;
150 : : bool empty;
151 : :
3676 noah@leadboat.com 152 : 54128 : check_stack_depth(); /* recurses when subtype is a range type */
153 : :
5096 tgl@sss.pgh.pa.us 154 : 54128 : cache = get_range_io_data(fcinfo, RangeTypeGetOid(range), IOFunc_output);
155 : :
156 : : /* deserialize */
157 : 54128 : range_deserialize(cache->typcache, range, &lower, &upper, &empty);
158 : 54128 : flags = range_get_flags(range);
159 : :
160 : : /* call element type's output function */
5108 heikki.linnakangas@i 161 [ + + ]: 54128 : if (RANGE_HAS_LBOUND(flags))
2063 alvherre@alvh.no-ip. 162 : 44418 : lbound_str = OutputFunctionCall(&cache->typioproc, lower.val);
5108 heikki.linnakangas@i 163 [ + + ]: 54128 : if (RANGE_HAS_UBOUND(flags))
2063 alvherre@alvh.no-ip. 164 : 44349 : ubound_str = OutputFunctionCall(&cache->typioproc, upper.val);
165 : :
166 : : /* construct result string */
5108 heikki.linnakangas@i 167 : 54128 : output_str = range_deparse(flags, lbound_str, ubound_str);
168 : :
169 : 54128 : PG_RETURN_CSTRING(output_str);
170 : : }
171 : :
172 : : /*
173 : : * Binary representation: The first byte is the flags, then the lower bound
174 : : * (if present), then the upper bound (if present). Each bound is represented
175 : : * by a 4-byte length header and the binary representation of that bound (as
176 : : * returned by a call to the send function for the subtype).
177 : : */
178 : :
179 : : Datum
5108 heikki.linnakangas@i 180 :UBC 0 : range_recv(PG_FUNCTION_ARGS)
181 : : {
5097 bruce@momjian.us 182 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
5096 tgl@sss.pgh.pa.us 183 : 0 : Oid rngtypoid = PG_GETARG_OID(1);
5097 bruce@momjian.us 184 : 0 : int32 typmod = PG_GETARG_INT32(2);
185 : : RangeType *range;
186 : : RangeIOData *cache;
187 : : char flags;
188 : : RangeBound lower;
189 : : RangeBound upper;
190 : :
3676 noah@leadboat.com 191 : 0 : check_stack_depth(); /* recurses when subtype is a range type */
192 : :
5096 tgl@sss.pgh.pa.us 193 : 0 : cache = get_range_io_data(fcinfo, rngtypoid, IOFunc_receive);
194 : :
195 : : /* receive the flags... */
196 : 0 : flags = (unsigned char) pq_getmsgbyte(buf);
197 : :
198 : : /*
199 : : * Mask out any unsupported flags, particularly RANGE_xB_NULL which would
200 : : * confuse following tests. Note that range_serialize will take care of
201 : : * cleaning up any inconsistencies in the remaining flags.
202 : : */
203 : 0 : flags &= (RANGE_EMPTY |
204 : : RANGE_LB_INC |
205 : : RANGE_LB_INF |
206 : : RANGE_UB_INC |
207 : : RANGE_UB_INF);
208 : :
209 : : /* receive the bounds ... */
5108 heikki.linnakangas@i 210 [ # # ]: 0 : if (RANGE_HAS_LBOUND(flags))
211 : : {
5097 bruce@momjian.us 212 : 0 : uint32 bound_len = pq_getmsgint(buf, 4);
213 : 0 : const char *bound_data = pq_getmsgbytes(buf, bound_len);
214 : : StringInfoData bound_buf;
215 : :
5108 heikki.linnakangas@i 216 : 0 : initStringInfo(&bound_buf);
217 : 0 : appendBinaryStringInfo(&bound_buf, bound_data, bound_len);
218 : :
2063 alvherre@alvh.no-ip. 219 : 0 : lower.val = ReceiveFunctionCall(&cache->typioproc,
220 : : &bound_buf,
221 : : cache->typioparam,
222 : : typmod);
5108 heikki.linnakangas@i 223 : 0 : pfree(bound_buf.data);
224 : : }
225 : : else
226 : 0 : lower.val = (Datum) 0;
227 : :
228 [ # # ]: 0 : if (RANGE_HAS_UBOUND(flags))
229 : : {
5097 bruce@momjian.us 230 : 0 : uint32 bound_len = pq_getmsgint(buf, 4);
231 : 0 : const char *bound_data = pq_getmsgbytes(buf, bound_len);
232 : : StringInfoData bound_buf;
233 : :
5108 heikki.linnakangas@i 234 : 0 : initStringInfo(&bound_buf);
235 : 0 : appendBinaryStringInfo(&bound_buf, bound_data, bound_len);
236 : :
2063 alvherre@alvh.no-ip. 237 : 0 : upper.val = ReceiveFunctionCall(&cache->typioproc,
238 : : &bound_buf,
239 : : cache->typioparam,
240 : : typmod);
5108 heikki.linnakangas@i 241 : 0 : pfree(bound_buf.data);
242 : : }
243 : : else
244 : 0 : upper.val = (Datum) 0;
245 : :
246 : 0 : pq_getmsgend(buf);
247 : :
248 : : /* finish constructing RangeBound representation */
5097 bruce@momjian.us 249 : 0 : lower.infinite = (flags & RANGE_LB_INF) != 0;
5108 heikki.linnakangas@i 250 : 0 : lower.inclusive = (flags & RANGE_LB_INC) != 0;
5097 bruce@momjian.us 251 : 0 : lower.lower = true;
252 : 0 : upper.infinite = (flags & RANGE_UB_INF) != 0;
5108 heikki.linnakangas@i 253 : 0 : upper.inclusive = (flags & RANGE_UB_INC) != 0;
5097 bruce@momjian.us 254 : 0 : upper.lower = false;
255 : :
256 : : /* serialize and canonicalize */
1048 tgl@sss.pgh.pa.us 257 : 0 : range = make_range(cache->typcache, &lower, &upper,
258 : 0 : flags & RANGE_EMPTY, NULL);
259 : :
2962 260 : 0 : PG_RETURN_RANGE_P(range);
261 : : }
262 : :
263 : : Datum
5108 heikki.linnakangas@i 264 : 0 : range_send(PG_FUNCTION_ARGS)
265 : : {
2962 tgl@sss.pgh.pa.us 266 : 0 : RangeType *range = PG_GETARG_RANGE_P(0);
5097 bruce@momjian.us 267 : 0 : StringInfo buf = makeStringInfo();
268 : : RangeIOData *cache;
269 : : char flags;
270 : : RangeBound lower;
271 : : RangeBound upper;
272 : : bool empty;
273 : :
3676 noah@leadboat.com 274 : 0 : check_stack_depth(); /* recurses when subtype is a range type */
275 : :
5096 tgl@sss.pgh.pa.us 276 : 0 : cache = get_range_io_data(fcinfo, RangeTypeGetOid(range), IOFunc_send);
277 : :
278 : : /* deserialize */
279 : 0 : range_deserialize(cache->typcache, range, &lower, &upper, &empty);
280 : 0 : flags = range_get_flags(range);
281 : :
282 : : /* construct output */
5108 heikki.linnakangas@i 283 : 0 : pq_begintypsend(buf);
284 : :
285 : 0 : pq_sendbyte(buf, flags);
286 : :
287 [ # # ]: 0 : if (RANGE_HAS_LBOUND(flags))
288 : : {
84 peter@eisentraut.org 289 :UNC 0 : bytea *bound = SendFunctionCall(&cache->typioproc, lower.val);
5097 bruce@momjian.us 290 :UBC 0 : uint32 bound_len = VARSIZE(bound) - VARHDRSZ;
291 : 0 : char *bound_data = VARDATA(bound);
292 : :
2939 andres@anarazel.de 293 : 0 : pq_sendint32(buf, bound_len);
5108 heikki.linnakangas@i 294 : 0 : pq_sendbytes(buf, bound_data, bound_len);
295 : : }
296 : :
297 [ # # ]: 0 : if (RANGE_HAS_UBOUND(flags))
298 : : {
84 peter@eisentraut.org 299 :UNC 0 : bytea *bound = SendFunctionCall(&cache->typioproc, upper.val);
5097 bruce@momjian.us 300 :UBC 0 : uint32 bound_len = VARSIZE(bound) - VARHDRSZ;
301 : 0 : char *bound_data = VARDATA(bound);
302 : :
2939 andres@anarazel.de 303 : 0 : pq_sendint32(buf, bound_len);
5108 heikki.linnakangas@i 304 : 0 : pq_sendbytes(buf, bound_data, bound_len);
305 : : }
306 : :
307 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(buf));
308 : : }
309 : :
310 : : /*
311 : : * get_range_io_data: get cached information needed for range type I/O
312 : : *
313 : : * The range I/O functions need a bit more cached info than other range
314 : : * functions, so they store a RangeIOData struct in fn_extra, not just a
315 : : * pointer to a type cache entry.
316 : : */
317 : : static RangeIOData *
5096 tgl@sss.pgh.pa.us 318 :CBC 57786 : get_range_io_data(FunctionCallInfo fcinfo, Oid rngtypid, IOFuncSelector func)
319 : : {
320 : 57786 : RangeIOData *cache = (RangeIOData *) fcinfo->flinfo->fn_extra;
321 : :
322 [ + + - + ]: 57786 : if (cache == NULL || cache->typcache->type_id != rngtypid)
323 : : {
324 : : int16 typlen;
325 : : bool typbyval;
326 : : char typalign;
327 : : char typdelim;
328 : : Oid typiofunc;
329 : :
330 : 5137 : cache = (RangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
331 : : sizeof(RangeIOData));
332 : 5137 : cache->typcache = lookup_type_cache(rngtypid, TYPECACHE_RANGE_INFO);
333 [ - + ]: 5137 : if (cache->typcache->rngelemtype == NULL)
5096 tgl@sss.pgh.pa.us 334 [ # # ]:UBC 0 : elog(ERROR, "type %u is not a range type", rngtypid);
335 : :
336 : : /* get_type_io_data does more than we need, but is convenient */
5096 tgl@sss.pgh.pa.us 337 :CBC 5137 : get_type_io_data(cache->typcache->rngelemtype->type_id,
338 : : func,
339 : : &typlen,
340 : : &typbyval,
341 : : &typalign,
342 : : &typdelim,
343 : : &cache->typioparam,
344 : : &typiofunc);
345 : :
2063 alvherre@alvh.no-ip. 346 [ - + ]: 5137 : if (!OidIsValid(typiofunc))
347 : : {
348 : : /* this could only happen for receive or send */
5096 tgl@sss.pgh.pa.us 349 [ # # ]:UBC 0 : if (func == IOFunc_receive)
350 [ # # ]: 0 : ereport(ERROR,
351 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
352 : : errmsg("no binary input function available for type %s",
353 : : format_type_be(cache->typcache->rngelemtype->type_id))));
354 : : else
355 [ # # ]: 0 : ereport(ERROR,
356 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
357 : : errmsg("no binary output function available for type %s",
358 : : format_type_be(cache->typcache->rngelemtype->type_id))));
359 : : }
2063 alvherre@alvh.no-ip. 360 :CBC 5137 : fmgr_info_cxt(typiofunc, &cache->typioproc,
5096 tgl@sss.pgh.pa.us 361 : 5137 : fcinfo->flinfo->fn_mcxt);
362 : :
334 peter@eisentraut.org 363 : 5137 : fcinfo->flinfo->fn_extra = cache;
364 : : }
365 : :
5096 tgl@sss.pgh.pa.us 366 : 57786 : return cache;
367 : : }
368 : :
369 : :
370 : : /*
371 : : *----------------------------------------------------------
372 : : * GENERIC FUNCTIONS
373 : : *----------------------------------------------------------
374 : : */
375 : :
376 : : /* Construct standard-form range value from two arguments */
377 : : Datum
5108 heikki.linnakangas@i 378 : 54888 : range_constructor2(PG_FUNCTION_ARGS)
379 : : {
5097 bruce@momjian.us 380 : 54888 : Datum arg1 = PG_GETARG_DATUM(0);
381 : 54888 : Datum arg2 = PG_GETARG_DATUM(1);
382 : 54888 : Oid rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
383 : : RangeType *range;
384 : : TypeCacheEntry *typcache;
385 : : RangeBound lower;
386 : : RangeBound upper;
387 : :
5096 tgl@sss.pgh.pa.us 388 : 54888 : typcache = range_get_typcache(fcinfo, rngtypid);
389 : :
5097 bruce@momjian.us 390 [ + + ]: 54888 : lower.val = PG_ARGISNULL(0) ? (Datum) 0 : arg1;
391 : 54888 : lower.infinite = PG_ARGISNULL(0);
5096 tgl@sss.pgh.pa.us 392 : 54888 : lower.inclusive = true;
5097 bruce@momjian.us 393 : 54888 : lower.lower = true;
394 : :
395 [ + + ]: 54888 : upper.val = PG_ARGISNULL(1) ? (Datum) 0 : arg2;
396 : 54888 : upper.infinite = PG_ARGISNULL(1);
5096 tgl@sss.pgh.pa.us 397 : 54888 : upper.inclusive = false;
5097 bruce@momjian.us 398 : 54888 : upper.lower = false;
399 : :
1048 tgl@sss.pgh.pa.us 400 : 54888 : range = make_range(typcache, &lower, &upper, false, NULL);
401 : :
2962 402 : 54870 : PG_RETURN_RANGE_P(range);
403 : : }
404 : :
405 : : /* Construct general range value from three arguments */
406 : : Datum
5108 heikki.linnakangas@i 407 : 2583 : range_constructor3(PG_FUNCTION_ARGS)
408 : : {
5097 bruce@momjian.us 409 : 2583 : Datum arg1 = PG_GETARG_DATUM(0);
410 : 2583 : Datum arg2 = PG_GETARG_DATUM(1);
411 : 2583 : Oid rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
412 : : RangeType *range;
413 : : TypeCacheEntry *typcache;
414 : : RangeBound lower;
415 : : RangeBound upper;
416 : : char flags;
417 : :
5096 tgl@sss.pgh.pa.us 418 : 2583 : typcache = range_get_typcache(fcinfo, rngtypid);
419 : :
5108 heikki.linnakangas@i 420 [ - + ]: 2583 : if (PG_ARGISNULL(2))
5108 heikki.linnakangas@i 421 [ # # ]:UBC 0 : ereport(ERROR,
422 : : (errcode(ERRCODE_DATA_EXCEPTION),
423 : : errmsg("range constructor flags argument must not be null")));
424 : :
3152 noah@leadboat.com 425 :CBC 2583 : flags = range_parse_flags(text_to_cstring(PG_GETARG_TEXT_PP(2)));
426 : :
5097 bruce@momjian.us 427 [ + + ]: 2583 : lower.val = PG_ARGISNULL(0) ? (Datum) 0 : arg1;
428 : 2583 : lower.infinite = PG_ARGISNULL(0);
5096 tgl@sss.pgh.pa.us 429 : 2583 : lower.inclusive = (flags & RANGE_LB_INC) != 0;
5097 bruce@momjian.us 430 : 2583 : lower.lower = true;
431 : :
432 [ + + ]: 2583 : upper.val = PG_ARGISNULL(1) ? (Datum) 0 : arg2;
433 : 2583 : upper.infinite = PG_ARGISNULL(1);
5096 tgl@sss.pgh.pa.us 434 : 2583 : upper.inclusive = (flags & RANGE_UB_INC) != 0;
5097 bruce@momjian.us 435 : 2583 : upper.lower = false;
436 : :
1048 tgl@sss.pgh.pa.us 437 : 2583 : range = make_range(typcache, &lower, &upper, false, NULL);
438 : :
2962 439 : 2583 : PG_RETURN_RANGE_P(range);
440 : : }
441 : :
442 : :
443 : : /* range -> subtype functions */
444 : :
445 : : /* extract lower bound value */
446 : : Datum
5108 heikki.linnakangas@i 447 : 129 : range_lower(PG_FUNCTION_ARGS)
448 : : {
2962 tgl@sss.pgh.pa.us 449 : 129 : RangeType *r1 = PG_GETARG_RANGE_P(0);
450 : : TypeCacheEntry *typcache;
451 : : RangeBound lower;
452 : : RangeBound upper;
453 : : bool empty;
454 : :
5096 455 : 129 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
456 : :
457 : 129 : range_deserialize(typcache, r1, &lower, &upper, &empty);
458 : :
459 : : /* Return NULL if there's no finite lower bound */
5097 460 [ + + + + ]: 129 : if (empty || lower.infinite)
461 : 18 : PG_RETURN_NULL();
462 : :
5108 heikki.linnakangas@i 463 : 111 : PG_RETURN_DATUM(lower.val);
464 : : }
465 : :
466 : : /* extract upper bound value */
467 : : Datum
468 : 114 : range_upper(PG_FUNCTION_ARGS)
469 : : {
2962 tgl@sss.pgh.pa.us 470 : 114 : RangeType *r1 = PG_GETARG_RANGE_P(0);
471 : : TypeCacheEntry *typcache;
472 : : RangeBound lower;
473 : : RangeBound upper;
474 : : bool empty;
475 : :
5096 476 : 114 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
477 : :
478 : 114 : range_deserialize(typcache, r1, &lower, &upper, &empty);
479 : :
480 : : /* Return NULL if there's no finite upper bound */
5097 481 [ + + + + ]: 114 : if (empty || upper.infinite)
482 : 18 : PG_RETURN_NULL();
483 : :
5108 heikki.linnakangas@i 484 : 96 : PG_RETURN_DATUM(upper.val);
485 : : }
486 : :
487 : :
488 : : /* range -> bool functions */
489 : :
490 : : /* is range empty? */
491 : : Datum
492 : 1098 : range_empty(PG_FUNCTION_ARGS)
493 : : {
2962 tgl@sss.pgh.pa.us 494 : 1098 : RangeType *r1 = PG_GETARG_RANGE_P(0);
5096 495 : 1098 : char flags = range_get_flags(r1);
496 : :
497 : 1098 : PG_RETURN_BOOL(flags & RANGE_EMPTY);
498 : : }
499 : :
500 : : /* is lower bound inclusive? */
501 : : Datum
5108 heikki.linnakangas@i 502 : 36 : range_lower_inc(PG_FUNCTION_ARGS)
503 : : {
2962 tgl@sss.pgh.pa.us 504 : 36 : RangeType *r1 = PG_GETARG_RANGE_P(0);
5096 505 : 36 : char flags = range_get_flags(r1);
506 : :
507 : 36 : PG_RETURN_BOOL(flags & RANGE_LB_INC);
508 : : }
509 : :
510 : : /* is upper bound inclusive? */
511 : : Datum
5108 heikki.linnakangas@i 512 : 36 : range_upper_inc(PG_FUNCTION_ARGS)
513 : : {
2962 tgl@sss.pgh.pa.us 514 : 36 : RangeType *r1 = PG_GETARG_RANGE_P(0);
5096 515 : 36 : char flags = range_get_flags(r1);
516 : :
517 : 36 : PG_RETURN_BOOL(flags & RANGE_UB_INC);
518 : : }
519 : :
520 : : /* is lower bound infinite? */
521 : : Datum
5108 heikki.linnakangas@i 522 : 36 : range_lower_inf(PG_FUNCTION_ARGS)
523 : : {
2962 tgl@sss.pgh.pa.us 524 : 36 : RangeType *r1 = PG_GETARG_RANGE_P(0);
5096 525 : 36 : char flags = range_get_flags(r1);
526 : :
527 : 36 : PG_RETURN_BOOL(flags & RANGE_LB_INF);
528 : : }
529 : :
530 : : /* is upper bound infinite? */
531 : : Datum
5108 heikki.linnakangas@i 532 : 36 : range_upper_inf(PG_FUNCTION_ARGS)
533 : : {
2962 tgl@sss.pgh.pa.us 534 : 36 : RangeType *r1 = PG_GETARG_RANGE_P(0);
5096 535 : 36 : char flags = range_get_flags(r1);
536 : :
537 : 36 : PG_RETURN_BOOL(flags & RANGE_UB_INF);
538 : : }
539 : :
540 : :
541 : : /* range, element -> bool functions */
542 : :
543 : : /* contains? */
544 : : Datum
5089 545 : 38100 : range_contains_elem(PG_FUNCTION_ARGS)
546 : : {
2962 547 : 38100 : RangeType *r = PG_GETARG_RANGE_P(0);
5089 548 : 38100 : Datum val = PG_GETARG_DATUM(1);
549 : : TypeCacheEntry *typcache;
550 : :
551 : 38100 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
552 : :
553 : 38100 : PG_RETURN_BOOL(range_contains_elem_internal(typcache, r, val));
554 : : }
555 : :
556 : : /* contained by? */
557 : : Datum
558 : 42 : elem_contained_by_range(PG_FUNCTION_ARGS)
559 : : {
560 : 42 : Datum val = PG_GETARG_DATUM(0);
2962 561 : 42 : RangeType *r = PG_GETARG_RANGE_P(1);
562 : : TypeCacheEntry *typcache;
563 : :
5089 564 : 42 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
565 : :
566 : 42 : PG_RETURN_BOOL(range_contains_elem_internal(typcache, r, val));
567 : : }
568 : :
569 : :
570 : : /* range, range -> bool functions */
571 : :
572 : : /* equality (internal version) */
573 : : bool
2189 peter@eisentraut.org 574 : 79507 : range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
575 : : {
576 : : RangeBound lower1,
577 : : lower2;
578 : : RangeBound upper1,
579 : : upper2;
580 : : bool empty1,
581 : : empty2;
582 : :
583 : : /* Different types should be prevented by ANYRANGE matching rules */
5096 tgl@sss.pgh.pa.us 584 [ - + ]: 79507 : if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
5108 heikki.linnakangas@i 585 [ # # ]:UBC 0 : elog(ERROR, "range types do not match");
586 : :
5096 tgl@sss.pgh.pa.us 587 :CBC 79507 : range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
588 : 79507 : range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
589 : :
5108 heikki.linnakangas@i 590 [ + + + + ]: 79507 : if (empty1 && empty2)
4850 591 : 3783 : return true;
5108 592 [ + + ]: 75724 : if (empty1 != empty2)
4850 593 : 6756 : return false;
594 : :
5096 tgl@sss.pgh.pa.us 595 [ + + ]: 68968 : if (range_cmp_bounds(typcache, &lower1, &lower2) != 0)
4850 heikki.linnakangas@i 596 : 40407 : return false;
597 : :
5096 tgl@sss.pgh.pa.us 598 [ + + ]: 28561 : if (range_cmp_bounds(typcache, &upper1, &upper2) != 0)
4850 heikki.linnakangas@i 599 : 16695 : return false;
600 : :
601 : 11866 : return true;
602 : : }
603 : :
604 : : /* equality */
605 : : Datum
606 : 39617 : range_eq(PG_FUNCTION_ARGS)
607 : : {
2962 tgl@sss.pgh.pa.us 608 : 39617 : RangeType *r1 = PG_GETARG_RANGE_P(0);
609 : 39617 : RangeType *r2 = PG_GETARG_RANGE_P(1);
610 : : TypeCacheEntry *typcache;
611 : :
4850 heikki.linnakangas@i 612 : 39617 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
613 : :
614 : 39617 : PG_RETURN_BOOL(range_eq_internal(typcache, r1, r2));
615 : : }
616 : :
617 : : /* inequality (internal version) */
618 : : bool
2189 peter@eisentraut.org 619 :UBC 0 : range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
620 : : {
4850 heikki.linnakangas@i 621 : 0 : return (!range_eq_internal(typcache, r1, r2));
622 : : }
623 : :
624 : : /* inequality */
625 : : Datum
5108 626 : 0 : range_ne(PG_FUNCTION_ARGS)
627 : : {
2962 tgl@sss.pgh.pa.us 628 : 0 : RangeType *r1 = PG_GETARG_RANGE_P(0);
629 : 0 : RangeType *r2 = PG_GETARG_RANGE_P(1);
630 : : TypeCacheEntry *typcache;
631 : :
4850 heikki.linnakangas@i 632 : 0 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
633 : :
634 : 0 : PG_RETURN_BOOL(range_ne_internal(typcache, r1, r2));
635 : : }
636 : :
637 : : /* contains? */
638 : : Datum
5108 heikki.linnakangas@i 639 :CBC 77235 : range_contains(PG_FUNCTION_ARGS)
640 : : {
2962 tgl@sss.pgh.pa.us 641 : 77235 : RangeType *r1 = PG_GETARG_RANGE_P(0);
642 : 77235 : RangeType *r2 = PG_GETARG_RANGE_P(1);
643 : : TypeCacheEntry *typcache;
644 : :
5096 645 : 77235 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
646 : :
647 : 77235 : PG_RETURN_BOOL(range_contains_internal(typcache, r1, r2));
648 : : }
649 : :
650 : : /* contained by? */
651 : : Datum
5108 heikki.linnakangas@i 652 : 38466 : range_contained_by(PG_FUNCTION_ARGS)
653 : : {
2962 tgl@sss.pgh.pa.us 654 : 38466 : RangeType *r1 = PG_GETARG_RANGE_P(0);
655 : 38466 : RangeType *r2 = PG_GETARG_RANGE_P(1);
656 : : TypeCacheEntry *typcache;
657 : :
5096 658 : 38466 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
659 : :
4850 heikki.linnakangas@i 660 : 38466 : PG_RETURN_BOOL(range_contained_by_internal(typcache, r1, r2));
661 : : }
662 : :
663 : : /* strictly left of? (internal version) */
664 : : bool
2189 peter@eisentraut.org 665 : 61312 : range_before_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
666 : : {
667 : : RangeBound lower1,
668 : : lower2;
669 : : RangeBound upper1,
670 : : upper2;
671 : : bool empty1,
672 : : empty2;
673 : :
674 : : /* Different types should be prevented by ANYRANGE matching rules */
5096 tgl@sss.pgh.pa.us 675 [ - + ]: 61312 : if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
5108 heikki.linnakangas@i 676 [ # # ]:UBC 0 : elog(ERROR, "range types do not match");
677 : :
5096 tgl@sss.pgh.pa.us 678 :CBC 61312 : range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
679 : 61312 : range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
680 : :
681 : : /* An empty range is neither before nor after any other range */
5108 heikki.linnakangas@i 682 [ + + + + ]: 61312 : if (empty1 || empty2)
4850 683 : 7455 : return false;
684 : :
685 : 53857 : return (range_cmp_bounds(typcache, &upper1, &lower2) < 0);
686 : : }
687 : :
688 : : /* strictly left of? */
689 : : Datum
690 : 39459 : range_before(PG_FUNCTION_ARGS)
691 : : {
2962 tgl@sss.pgh.pa.us 692 : 39459 : RangeType *r1 = PG_GETARG_RANGE_P(0);
693 : 39459 : RangeType *r2 = PG_GETARG_RANGE_P(1);
694 : : TypeCacheEntry *typcache;
695 : :
4850 heikki.linnakangas@i 696 : 39459 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
697 : :
698 : 39459 : PG_RETURN_BOOL(range_before_internal(typcache, r1, r2));
699 : : }
700 : :
701 : : /* strictly right of? (internal version) */
702 : : bool
2189 peter@eisentraut.org 703 : 99616 : range_after_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
704 : : {
705 : : RangeBound lower1,
706 : : lower2;
707 : : RangeBound upper1,
708 : : upper2;
709 : : bool empty1,
710 : : empty2;
711 : :
712 : : /* Different types should be prevented by ANYRANGE matching rules */
5096 tgl@sss.pgh.pa.us 713 [ - + ]: 99616 : if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
5108 heikki.linnakangas@i 714 [ # # ]:UBC 0 : elog(ERROR, "range types do not match");
715 : :
5096 tgl@sss.pgh.pa.us 716 :CBC 99616 : range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
717 : 99616 : range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
718 : :
719 : : /* An empty range is neither before nor after any other range */
5108 heikki.linnakangas@i 720 [ + + + + ]: 99616 : if (empty1 || empty2)
4850 721 : 7155 : return false;
722 : :
723 : 92461 : return (range_cmp_bounds(typcache, &lower1, &upper2) > 0);
724 : : }
725 : :
726 : : /* strictly right of? */
727 : : Datum
728 : 39153 : range_after(PG_FUNCTION_ARGS)
729 : : {
2962 tgl@sss.pgh.pa.us 730 : 39153 : RangeType *r1 = PG_GETARG_RANGE_P(0);
731 : 39153 : RangeType *r2 = PG_GETARG_RANGE_P(1);
732 : : TypeCacheEntry *typcache;
733 : :
4850 heikki.linnakangas@i 734 : 39153 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
735 : :
736 : 39153 : PG_RETURN_BOOL(range_after_internal(typcache, r1, r2));
737 : : }
738 : :
739 : : /*
740 : : * Check if two bounds A and B are "adjacent", where A is an upper bound and B
741 : : * is a lower bound. For the bounds to be adjacent, each subtype value must
742 : : * satisfy strictly one of the bounds: there are no values which satisfy both
743 : : * bounds (i.e. less than A and greater than B); and there are no values which
744 : : * satisfy neither bound (i.e. greater than A and less than B).
745 : : *
746 : : * For discrete ranges, we rely on the canonicalization function to see if A..B
747 : : * normalizes to empty. (If there is no canonicalization function, it's
748 : : * impossible for such a range to normalize to empty, so we needn't bother to
749 : : * try.)
750 : : *
751 : : * If A == B, the ranges are adjacent only if the bounds have different
752 : : * inclusive flags (i.e., exactly one of the ranges includes the common
753 : : * boundary point).
754 : : *
755 : : * And if A > B then the ranges are not adjacent in this order.
756 : : */
757 : : bool
4617 758 : 236077 : bounds_adjacent(TypeCacheEntry *typcache, RangeBound boundA, RangeBound boundB)
759 : : {
760 : : int cmp;
761 : :
762 [ + - - + ]: 236077 : Assert(!boundA.lower && boundB.lower);
763 : :
764 : 236077 : cmp = range_cmp_bound_values(typcache, &boundA, &boundB);
765 [ + + ]: 236077 : if (cmp < 0)
766 : : {
767 : : RangeType *r;
768 : :
769 : : /*
770 : : * Bounds do not overlap; see if there are points in between.
771 : : */
772 : :
773 : : /* in a continuous subtype, there are assumed to be points between */
774 [ + + ]: 72384 : if (!OidIsValid(typcache->rng_canonical_finfo.fn_oid))
775 : 408 : return false;
776 : :
777 : : /*
778 : : * The bounds are of a discrete range type; so make a range A..B and
779 : : * see if it's empty.
780 : : */
781 : :
782 : : /* flip the inclusion flags */
783 : 71976 : boundA.inclusive = !boundA.inclusive;
784 : 71976 : boundB.inclusive = !boundB.inclusive;
785 : : /* change upper/lower labels to avoid Assert failures */
786 : 71976 : boundA.lower = true;
787 : 71976 : boundB.lower = false;
1048 tgl@sss.pgh.pa.us 788 : 71976 : r = make_range(typcache, &boundA, &boundB, false, NULL);
4617 heikki.linnakangas@i 789 : 71976 : return RangeIsEmpty(r);
790 : : }
791 [ + + ]: 163693 : else if (cmp == 0)
792 : 946 : return boundA.inclusive != boundB.inclusive;
793 : : else
4535 bruce@momjian.us 794 : 162747 : return false; /* bounds overlap */
795 : : }
796 : :
797 : : /* adjacent to (but not overlapping)? (internal version) */
798 : : bool
2189 peter@eisentraut.org 799 : 71406 : range_adjacent_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
800 : : {
801 : : RangeBound lower1,
802 : : lower2;
803 : : RangeBound upper1,
804 : : upper2;
805 : : bool empty1,
806 : : empty2;
807 : :
808 : : /* Different types should be prevented by ANYRANGE matching rules */
5096 tgl@sss.pgh.pa.us 809 [ - + ]: 71406 : if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
5108 heikki.linnakangas@i 810 [ # # ]:UBC 0 : elog(ERROR, "range types do not match");
811 : :
5096 tgl@sss.pgh.pa.us 812 :CBC 71406 : range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
813 : 71406 : range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
814 : :
815 : : /* An empty range is not adjacent to any other range */
5108 heikki.linnakangas@i 816 [ + + - + ]: 71406 : if (empty1 || empty2)
4850 817 : 6000 : return false;
818 : :
819 : : /*
820 : : * Given two ranges A..B and C..D, the ranges are adjacent if and only if
821 : : * B is adjacent to C, or D is adjacent to A.
822 : : */
4617 823 [ + + + + ]: 130058 : return (bounds_adjacent(typcache, upper1, lower2) ||
824 : 64652 : bounds_adjacent(typcache, upper2, lower1));
825 : : }
826 : :
827 : : /* adjacent to (but not overlapping)? */
828 : : Datum
4850 829 : 37218 : range_adjacent(PG_FUNCTION_ARGS)
830 : : {
2962 tgl@sss.pgh.pa.us 831 : 37218 : RangeType *r1 = PG_GETARG_RANGE_P(0);
832 : 37218 : RangeType *r2 = PG_GETARG_RANGE_P(1);
833 : : TypeCacheEntry *typcache;
834 : :
4850 heikki.linnakangas@i 835 : 37218 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
836 : :
837 : 37218 : PG_RETURN_BOOL(range_adjacent_internal(typcache, r1, r2));
838 : : }
839 : :
840 : : /* overlaps? (internal version) */
841 : : bool
2189 peter@eisentraut.org 842 : 48718 : range_overlaps_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
843 : : {
844 : : RangeBound lower1,
845 : : lower2;
846 : : RangeBound upper1,
847 : : upper2;
848 : : bool empty1,
849 : : empty2;
850 : :
851 : : /* Different types should be prevented by ANYRANGE matching rules */
5096 tgl@sss.pgh.pa.us 852 [ - + ]: 48718 : if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
5108 heikki.linnakangas@i 853 [ # # ]:UBC 0 : elog(ERROR, "range types do not match");
854 : :
5096 tgl@sss.pgh.pa.us 855 :CBC 48718 : range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
856 : 48718 : range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
857 : :
858 : : /* An empty range does not overlap any other range */
5108 heikki.linnakangas@i 859 [ + + + + ]: 48718 : if (empty1 || empty2)
4850 860 : 7052 : return false;
861 : :
5096 tgl@sss.pgh.pa.us 862 [ + + + + ]: 80054 : if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0 &&
863 : 38388 : range_cmp_bounds(typcache, &lower1, &upper2) <= 0)
4850 heikki.linnakangas@i 864 : 2596 : return true;
865 : :
5096 tgl@sss.pgh.pa.us 866 [ + + + + ]: 42348 : if (range_cmp_bounds(typcache, &lower2, &lower1) >= 0 &&
867 : 3278 : range_cmp_bounds(typcache, &lower2, &upper1) <= 0)
4850 heikki.linnakangas@i 868 : 2993 : return true;
869 : :
870 : 36077 : return false;
871 : : }
872 : :
873 : : /* overlaps? */
874 : : Datum
875 : 38716 : range_overlaps(PG_FUNCTION_ARGS)
876 : : {
2962 tgl@sss.pgh.pa.us 877 : 38716 : RangeType *r1 = PG_GETARG_RANGE_P(0);
878 : 38716 : RangeType *r2 = PG_GETARG_RANGE_P(1);
879 : : TypeCacheEntry *typcache;
880 : :
4850 heikki.linnakangas@i 881 : 38716 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
882 : :
883 : 38716 : PG_RETURN_BOOL(range_overlaps_internal(typcache, r1, r2));
884 : : }
885 : :
886 : : /* does not extend to right of? (internal version) */
887 : : bool
2189 peter@eisentraut.org 888 : 65669 : range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
889 : : {
890 : : RangeBound lower1,
891 : : lower2;
892 : : RangeBound upper1,
893 : : upper2;
894 : : bool empty1,
895 : : empty2;
896 : :
897 : : /* Different types should be prevented by ANYRANGE matching rules */
5096 tgl@sss.pgh.pa.us 898 [ - + ]: 65669 : if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
5108 heikki.linnakangas@i 899 [ # # ]:UBC 0 : elog(ERROR, "range types do not match");
900 : :
5096 tgl@sss.pgh.pa.us 901 :CBC 65669 : range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
902 : 65669 : range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
903 : :
904 : : /* An empty range is neither before nor after any other range */
5108 heikki.linnakangas@i 905 [ + + - + ]: 65669 : if (empty1 || empty2)
4850 906 : 6573 : return false;
907 : :
5096 tgl@sss.pgh.pa.us 908 [ + + ]: 59096 : if (range_cmp_bounds(typcache, &upper1, &upper2) <= 0)
4850 heikki.linnakangas@i 909 : 20311 : return true;
910 : :
911 : 38785 : return false;
912 : : }
913 : :
914 : : /* does not extend to right of? */
915 : : Datum
916 : 38253 : range_overleft(PG_FUNCTION_ARGS)
917 : : {
2962 tgl@sss.pgh.pa.us 918 : 38253 : RangeType *r1 = PG_GETARG_RANGE_P(0);
919 : 38253 : RangeType *r2 = PG_GETARG_RANGE_P(1);
920 : : TypeCacheEntry *typcache;
921 : :
4850 heikki.linnakangas@i 922 : 38253 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
923 : :
924 : 38253 : PG_RETURN_BOOL(range_overleft_internal(typcache, r1, r2));
925 : : }
926 : :
927 : : /* does not extend to left of? (internal version) */
928 : : bool
2189 peter@eisentraut.org 929 : 109001 : range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
930 : : {
931 : : RangeBound lower1,
932 : : lower2;
933 : : RangeBound upper1,
934 : : upper2;
935 : : bool empty1,
936 : : empty2;
937 : :
938 : : /* Different types should be prevented by ANYRANGE matching rules */
5096 tgl@sss.pgh.pa.us 939 [ - + ]: 109001 : if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
5108 heikki.linnakangas@i 940 [ # # ]:UBC 0 : elog(ERROR, "range types do not match");
941 : :
5096 tgl@sss.pgh.pa.us 942 :CBC 109001 : range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
943 : 109001 : range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
944 : :
945 : : /* An empty range is neither before nor after any other range */
5108 heikki.linnakangas@i 946 [ + + - + ]: 109001 : if (empty1 || empty2)
4091 tgl@sss.pgh.pa.us 947 : 6573 : return false;
948 : :
5096 949 [ + + ]: 102428 : if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
4091 950 : 95477 : return true;
951 : :
952 : 6951 : return false;
953 : : }
954 : :
955 : : /* does not extend to left of? */
956 : : Datum
4850 heikki.linnakangas@i 957 : 38250 : range_overright(PG_FUNCTION_ARGS)
958 : : {
2962 tgl@sss.pgh.pa.us 959 : 38250 : RangeType *r1 = PG_GETARG_RANGE_P(0);
960 : 38250 : RangeType *r2 = PG_GETARG_RANGE_P(1);
961 : : TypeCacheEntry *typcache;
962 : :
4850 heikki.linnakangas@i 963 : 38250 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
964 : :
965 : 38250 : PG_RETURN_BOOL(range_overright_internal(typcache, r1, r2));
966 : : }
967 : :
968 : :
969 : : /* range, range -> range functions */
970 : :
971 : : /* set difference */
972 : : Datum
5108 973 : 15 : range_minus(PG_FUNCTION_ARGS)
974 : : {
2962 tgl@sss.pgh.pa.us 975 : 15 : RangeType *r1 = PG_GETARG_RANGE_P(0);
976 : 15 : RangeType *r2 = PG_GETARG_RANGE_P(1);
977 : : RangeType *ret;
978 : : TypeCacheEntry *typcache;
979 : :
980 : : /* Different types should be prevented by ANYRANGE matching rules */
1773 akorotkov@postgresql 981 [ - + ]: 15 : if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
1773 akorotkov@postgresql 982 [ # # ]:UBC 0 : elog(ERROR, "range types do not match");
983 : :
1773 akorotkov@postgresql 984 :CBC 15 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
985 : :
986 : 15 : ret = range_minus_internal(typcache, r1, r2);
987 [ + - ]: 15 : if (ret)
988 : 15 : PG_RETURN_RANGE_P(ret);
989 : : else
1773 akorotkov@postgresql 990 :UBC 0 : PG_RETURN_NULL();
991 : : }
992 : :
993 : : RangeType *
1773 akorotkov@postgresql 994 :CBC 48 : range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
995 : : {
996 : : RangeBound lower1,
997 : : lower2;
998 : : RangeBound upper1,
999 : : upper2;
1000 : : bool empty1,
1001 : : empty2;
1002 : : int cmp_l1l2,
1003 : : cmp_l1u2,
1004 : : cmp_u1l2,
1005 : : cmp_u1u2;
1006 : :
5096 tgl@sss.pgh.pa.us 1007 : 48 : range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
1008 : 48 : range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
1009 : :
1010 : : /* if either is empty, r1 is the correct answer */
5108 heikki.linnakangas@i 1011 [ + - - + ]: 48 : if (empty1 || empty2)
1773 akorotkov@postgresql 1012 :UBC 0 : return r1;
1013 : :
5096 tgl@sss.pgh.pa.us 1014 :CBC 48 : cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
1015 : 48 : cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
1016 : 48 : cmp_u1l2 = range_cmp_bounds(typcache, &upper1, &lower2);
1017 : 48 : cmp_u1u2 = range_cmp_bounds(typcache, &upper1, &upper2);
1018 : :
5108 heikki.linnakangas@i 1019 [ + + - + ]: 48 : if (cmp_l1l2 < 0 && cmp_u1u2 > 0)
5108 heikki.linnakangas@i 1020 [ # # ]:UBC 0 : ereport(ERROR,
1021 : : (errcode(ERRCODE_DATA_EXCEPTION),
1022 : : errmsg("result of range difference would not be contiguous")));
1023 : :
5108 heikki.linnakangas@i 1024 [ + - + + ]:CBC 48 : if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
1773 akorotkov@postgresql 1025 : 6 : return r1;
1026 : :
5108 heikki.linnakangas@i 1027 [ + + + + ]: 42 : if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
1773 akorotkov@postgresql 1028 : 21 : return make_empty_range(typcache);
1029 : :
5108 heikki.linnakangas@i 1030 [ + + + - : 21 : if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
+ + ]
1031 : : {
1032 : 12 : lower2.inclusive = !lower2.inclusive;
5097 bruce@momjian.us 1033 : 12 : lower2.lower = false; /* it will become the upper bound */
1048 tgl@sss.pgh.pa.us 1034 : 12 : return make_range(typcache, &lower1, &lower2, false, NULL);
1035 : : }
1036 : :
5108 heikki.linnakangas@i 1037 [ + - + - : 9 : if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
+ - ]
1038 : : {
1039 : 9 : upper2.inclusive = !upper2.inclusive;
5097 bruce@momjian.us 1040 : 9 : upper2.lower = true; /* it will become the lower bound */
1048 tgl@sss.pgh.pa.us 1041 : 9 : return make_range(typcache, &upper2, &upper1, false, NULL);
1042 : : }
1043 : :
5097 tgl@sss.pgh.pa.us 1044 [ # # ]:UBC 0 : elog(ERROR, "unexpected case in range_minus");
1045 : : return NULL;
1046 : : }
1047 : :
1048 : : /*
1049 : : * Set union. If strict is true, it is an error that the two input ranges
1050 : : * are not adjacent or overlapping.
1051 : : */
1052 : : RangeType *
3829 alvherre@alvh.no-ip. 1053 :CBC 787 : range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
1054 : : bool strict)
1055 : : {
1056 : : RangeBound lower1,
1057 : : lower2;
1058 : : RangeBound upper1,
1059 : : upper2;
1060 : : bool empty1,
1061 : : empty2;
1062 : : RangeBound *result_lower;
1063 : : RangeBound *result_upper;
1064 : :
1065 : : /* Different types should be prevented by ANYRANGE matching rules */
5096 tgl@sss.pgh.pa.us 1066 [ - + ]: 787 : if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
5097 tgl@sss.pgh.pa.us 1067 [ # # ]:UBC 0 : elog(ERROR, "range types do not match");
1068 : :
5096 tgl@sss.pgh.pa.us 1069 :CBC 787 : range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
1070 : 787 : range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
1071 : :
1072 : : /* if either is empty, the other is the correct answer */
5108 heikki.linnakangas@i 1073 [ + + ]: 787 : if (empty1)
3829 alvherre@alvh.no-ip. 1074 : 3 : return r2;
5108 heikki.linnakangas@i 1075 [ - + ]: 784 : if (empty2)
3829 alvherre@alvh.no-ip. 1076 :UBC 0 : return r1;
1077 : :
3829 alvherre@alvh.no-ip. 1078 [ + + ]:CBC 784 : if (strict &&
81 peter@eisentraut.org 1079 [ + + ]:GNC 69 : !range_overlaps_internal(typcache, r1, r2) &&
1080 [ + + ]: 6 : !range_adjacent_internal(typcache, r1, r2))
5108 heikki.linnakangas@i 1081 [ + - ]:CBC 3 : ereport(ERROR,
1082 : : (errcode(ERRCODE_DATA_EXCEPTION),
1083 : : errmsg("result of range union would not be contiguous")));
1084 : :
5096 tgl@sss.pgh.pa.us 1085 [ + + ]: 781 : if (range_cmp_bounds(typcache, &lower1, &lower2) < 0)
5108 heikki.linnakangas@i 1086 : 763 : result_lower = &lower1;
1087 : : else
1088 : 18 : result_lower = &lower2;
1089 : :
5096 tgl@sss.pgh.pa.us 1090 [ + + ]: 781 : if (range_cmp_bounds(typcache, &upper1, &upper2) > 0)
5108 heikki.linnakangas@i 1091 : 24 : result_upper = &upper1;
1092 : : else
1093 : 757 : result_upper = &upper2;
1094 : :
1048 tgl@sss.pgh.pa.us 1095 : 781 : return make_range(typcache, result_lower, result_upper, false, NULL);
1096 : : }
1097 : :
1098 : : Datum
3829 alvherre@alvh.no-ip. 1099 : 9 : range_union(PG_FUNCTION_ARGS)
1100 : : {
2962 tgl@sss.pgh.pa.us 1101 : 9 : RangeType *r1 = PG_GETARG_RANGE_P(0);
1102 : 9 : RangeType *r2 = PG_GETARG_RANGE_P(1);
1103 : : TypeCacheEntry *typcache;
1104 : :
3829 alvherre@alvh.no-ip. 1105 : 9 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
1106 : :
2962 tgl@sss.pgh.pa.us 1107 : 9 : PG_RETURN_RANGE_P(range_union_internal(typcache, r1, r2, true));
1108 : : }
1109 : :
1110 : : /*
1111 : : * range merge: like set union, except also allow and account for non-adjacent
1112 : : * input ranges.
1113 : : */
1114 : : Datum
3829 alvherre@alvh.no-ip. 1115 : 15 : range_merge(PG_FUNCTION_ARGS)
1116 : : {
2962 tgl@sss.pgh.pa.us 1117 : 15 : RangeType *r1 = PG_GETARG_RANGE_P(0);
1118 : 15 : RangeType *r2 = PG_GETARG_RANGE_P(1);
1119 : : TypeCacheEntry *typcache;
1120 : :
3829 alvherre@alvh.no-ip. 1121 : 15 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
1122 : :
2962 tgl@sss.pgh.pa.us 1123 : 15 : PG_RETURN_RANGE_P(range_union_internal(typcache, r1, r2, false));
1124 : : }
1125 : :
1126 : : /* set intersection */
1127 : : Datum
5108 heikki.linnakangas@i 1128 : 71 : range_intersect(PG_FUNCTION_ARGS)
1129 : : {
2962 tgl@sss.pgh.pa.us 1130 : 71 : RangeType *r1 = PG_GETARG_RANGE_P(0);
1131 : 71 : RangeType *r2 = PG_GETARG_RANGE_P(1);
1132 : : TypeCacheEntry *typcache;
1133 : :
1134 : : /* Different types should be prevented by ANYRANGE matching rules */
1773 akorotkov@postgresql 1135 [ - + ]: 71 : if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
1773 akorotkov@postgresql 1136 [ # # ]:UBC 0 : elog(ERROR, "range types do not match");
1137 : :
1773 akorotkov@postgresql 1138 :CBC 71 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
1139 : :
1140 : 71 : PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
1141 : : }
1142 : :
1143 : : RangeType *
1144 : 221 : range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
1145 : : {
1146 : : RangeBound lower1,
1147 : : lower2;
1148 : : RangeBound upper1,
1149 : : upper2;
1150 : : bool empty1,
1151 : : empty2;
1152 : : RangeBound *result_lower;
1153 : : RangeBound *result_upper;
1154 : :
5096 tgl@sss.pgh.pa.us 1155 : 221 : range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
1156 : 221 : range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
1157 : :
1773 akorotkov@postgresql 1158 [ + + + - : 221 : if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+ + ]
1159 : 15 : return make_empty_range(typcache);
1160 : :
5096 tgl@sss.pgh.pa.us 1161 [ + + ]: 206 : if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
5108 heikki.linnakangas@i 1162 : 151 : result_lower = &lower1;
1163 : : else
1164 : 55 : result_lower = &lower2;
1165 : :
5096 tgl@sss.pgh.pa.us 1166 [ + + ]: 206 : if (range_cmp_bounds(typcache, &upper1, &upper2) <= 0)
5108 heikki.linnakangas@i 1167 : 157 : result_upper = &upper1;
1168 : : else
1169 : 49 : result_upper = &upper2;
1170 : :
1048 tgl@sss.pgh.pa.us 1171 : 206 : return make_range(typcache, result_lower, result_upper, false, NULL);
1172 : : }
1173 : :
1174 : : /* range, range -> range, range functions */
1175 : :
1176 : : /*
1177 : : * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
1178 : : * ranges on both sides, then return true and set output1 and output2 to the
1179 : : * results of r1 - r2 (in order). Otherwise return false and don't set output1
1180 : : * or output2. Neither input range should be empty.
1181 : : */
1182 : : bool
1773 akorotkov@postgresql 1183 : 66 : range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
1184 : : RangeType **output1, RangeType **output2)
1185 : : {
1186 : : RangeBound lower1,
1187 : : lower2;
1188 : : RangeBound upper1,
1189 : : upper2;
1190 : : bool empty1,
1191 : : empty2;
1192 : :
1193 : 66 : range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
1194 : 66 : range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
1195 : :
1196 [ + + + + ]: 105 : if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
1197 : 39 : range_cmp_bounds(typcache, &upper1, &upper2) > 0)
1198 : : {
1199 : : /*
1200 : : * Need to invert inclusive/exclusive for the lower2 and upper2
1201 : : * points. They can't be infinite though. We're allowed to overwrite
1202 : : * these RangeBounds since they only exist locally.
1203 : : */
1204 : 9 : lower2.inclusive = !lower2.inclusive;
1205 : 9 : lower2.lower = false;
1206 : 9 : upper2.inclusive = !upper2.inclusive;
1207 : 9 : upper2.lower = true;
1208 : :
1048 tgl@sss.pgh.pa.us 1209 : 9 : *output1 = make_range(typcache, &lower1, &lower2, false, NULL);
1210 : 9 : *output2 = make_range(typcache, &upper2, &upper1, false, NULL);
1773 akorotkov@postgresql 1211 : 9 : return true;
1212 : : }
1213 : :
1214 : 57 : return false;
1215 : : }
1216 : :
1217 : : /* range -> range aggregate functions */
1218 : :
1219 : : Datum
1220 : 21 : range_intersect_agg_transfn(PG_FUNCTION_ARGS)
1221 : : {
1222 : : MemoryContext aggContext;
1223 : : Oid rngtypoid;
1224 : : TypeCacheEntry *typcache;
1225 : : RangeType *result;
1226 : : RangeType *current;
1227 : :
1228 [ - + ]: 21 : if (!AggCheckCallContext(fcinfo, &aggContext))
1773 akorotkov@postgresql 1229 [ # # ]:UBC 0 : elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
1230 : :
1773 akorotkov@postgresql 1231 :CBC 21 : rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
1232 [ - + ]: 21 : if (!type_is_range(rngtypoid))
1308 peter@eisentraut.org 1233 [ # # ]:UBC 0 : elog(ERROR, "range_intersect_agg must be called with a range");
1234 : :
1773 akorotkov@postgresql 1235 :CBC 21 : typcache = range_get_typcache(fcinfo, rngtypoid);
1236 : :
1237 : : /* strictness ensures these are non-null */
1238 : 21 : result = PG_GETARG_RANGE_P(0);
1239 : 21 : current = PG_GETARG_RANGE_P(1);
1240 : :
1241 : 21 : result = range_intersect_internal(typcache, result, current);
1242 : 21 : PG_RETURN_RANGE_P(result);
1243 : : }
1244 : :
1245 : :
1246 : : /* Btree support */
1247 : :
1248 : : /* btree comparator */
1249 : : Datum
5108 heikki.linnakangas@i 1250 : 9354 : range_cmp(PG_FUNCTION_ARGS)
1251 : : {
2962 tgl@sss.pgh.pa.us 1252 : 9354 : RangeType *r1 = PG_GETARG_RANGE_P(0);
1253 : 9354 : RangeType *r2 = PG_GETARG_RANGE_P(1);
1254 : : TypeCacheEntry *typcache;
1255 : : RangeBound lower1,
1256 : : lower2;
1257 : : RangeBound upper1,
1258 : : upper2;
1259 : : bool empty1,
1260 : : empty2;
1261 : : int cmp;
1262 : :
3676 noah@leadboat.com 1263 : 9354 : check_stack_depth(); /* recurses when subtype is a range type */
1264 : :
1265 : : /* Different types should be prevented by ANYRANGE matching rules */
5096 tgl@sss.pgh.pa.us 1266 [ - + ]: 9354 : if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
5108 heikki.linnakangas@i 1267 [ # # ]:UBC 0 : elog(ERROR, "range types do not match");
1268 : :
5096 tgl@sss.pgh.pa.us 1269 :CBC 9354 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
1270 : :
1271 : 9354 : range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
1272 : 9354 : range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
1273 : :
1274 : : /* For b-tree use, empty ranges sort before all else */
5108 heikki.linnakangas@i 1275 [ + + + + ]: 9354 : if (empty1 && empty2)
4416 1276 : 1317 : cmp = 0;
5108 1277 [ + + ]: 8037 : else if (empty1)
4416 1278 : 1737 : cmp = -1;
5108 1279 [ + + ]: 6300 : else if (empty2)
4416 1280 : 1020 : cmp = 1;
1281 : : else
1282 : : {
1283 : 5280 : cmp = range_cmp_bounds(typcache, &lower1, &lower2);
1284 [ + + ]: 5280 : if (cmp == 0)
1285 : 270 : cmp = range_cmp_bounds(typcache, &upper1, &upper2);
1286 : : }
1287 : :
1288 [ + + ]: 9354 : PG_FREE_IF_COPY(r1, 0);
1289 [ + + ]: 9354 : PG_FREE_IF_COPY(r2, 1);
1290 : :
1291 : 9354 : PG_RETURN_INT32(cmp);
1292 : : }
1293 : :
1294 : : /* Sort support strategy routine */
1295 : : Datum
209 1296 : 878 : range_sortsupport(PG_FUNCTION_ARGS)
1297 : : {
1298 : 878 : SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
1299 : :
1300 : 878 : ssup->comparator = range_fast_cmp;
1301 : 878 : ssup->ssup_extra = NULL;
1302 : :
1303 : 878 : PG_RETURN_VOID();
1304 : : }
1305 : :
1306 : : /* like range_cmp, but uses the new sortsupport interface */
1307 : : static int
1308 : 270356 : range_fast_cmp(Datum a, Datum b, SortSupport ssup)
1309 : : {
1310 : 270356 : RangeType *range_a = DatumGetRangeTypeP(a);
1311 : 270356 : RangeType *range_b = DatumGetRangeTypeP(b);
1312 : : TypeCacheEntry *typcache;
1313 : : RangeBound lower1,
1314 : : lower2;
1315 : : RangeBound upper1,
1316 : : upper2;
1317 : : bool empty1,
1318 : : empty2;
1319 : : int cmp;
1320 : :
1321 : : /* cache the range info between calls */
1322 [ + + ]: 270356 : if (ssup->ssup_extra == NULL)
1323 : : {
1324 [ - + ]: 191 : Assert(RangeTypeGetOid(range_a) == RangeTypeGetOid(range_b));
1325 : 191 : ssup->ssup_extra =
1326 : 191 : lookup_type_cache(RangeTypeGetOid(range_a), TYPECACHE_RANGE_INFO);
1327 : : }
1328 : 270356 : typcache = ssup->ssup_extra;
1329 : :
1330 : 270356 : range_deserialize(typcache, range_a, &lower1, &upper1, &empty1);
1331 : 270356 : range_deserialize(typcache, range_b, &lower2, &upper2, &empty2);
1332 : :
1333 : : /* For b-tree use, empty ranges sort before all else */
1334 [ + + + + ]: 270356 : if (empty1 && empty2)
1335 : 38040 : cmp = 0;
1336 [ + + ]: 232316 : else if (empty1)
1337 : 9162 : cmp = -1;
1338 [ + + ]: 223154 : else if (empty2)
1339 : 732 : cmp = 1;
1340 : : else
1341 : : {
1342 : 222422 : cmp = range_cmp_bounds(typcache, &lower1, &lower2);
1343 [ + + ]: 222422 : if (cmp == 0)
1344 : 16155 : cmp = range_cmp_bounds(typcache, &upper1, &upper2);
1345 : : }
1346 : :
81 tgl@sss.pgh.pa.us 1347 [ + - ]:GNC 270356 : if ((Pointer) range_a != DatumGetPointer(a))
209 heikki.linnakangas@i 1348 :CBC 270356 : pfree(range_a);
81 tgl@sss.pgh.pa.us 1349 [ + - ]:GNC 270356 : if ((Pointer) range_b != DatumGetPointer(b))
209 heikki.linnakangas@i 1350 :CBC 270356 : pfree(range_b);
1351 : :
1352 : 270356 : return cmp;
1353 : : }
1354 : :
1355 : :
1356 : : /* inequality operators using the range_cmp function */
1357 : : Datum
5108 1358 : 669 : range_lt(PG_FUNCTION_ARGS)
1359 : : {
81 peter@eisentraut.org 1360 :GNC 669 : int cmp = DatumGetInt32(range_cmp(fcinfo));
1361 : :
5108 heikki.linnakangas@i 1362 :CBC 669 : PG_RETURN_BOOL(cmp < 0);
1363 : : }
1364 : :
1365 : : Datum
1366 : 1506 : range_le(PG_FUNCTION_ARGS)
1367 : : {
81 peter@eisentraut.org 1368 :GNC 1506 : int cmp = DatumGetInt32(range_cmp(fcinfo));
1369 : :
5108 heikki.linnakangas@i 1370 :CBC 1506 : PG_RETURN_BOOL(cmp <= 0);
1371 : : }
1372 : :
1373 : : Datum
1374 : 1518 : range_ge(PG_FUNCTION_ARGS)
1375 : : {
81 peter@eisentraut.org 1376 :GNC 1518 : int cmp = DatumGetInt32(range_cmp(fcinfo));
1377 : :
5108 heikki.linnakangas@i 1378 :CBC 1518 : PG_RETURN_BOOL(cmp >= 0);
1379 : : }
1380 : :
1381 : : Datum
1382 : 1536 : range_gt(PG_FUNCTION_ARGS)
1383 : : {
81 peter@eisentraut.org 1384 :GNC 1536 : int cmp = DatumGetInt32(range_cmp(fcinfo));
1385 : :
5108 heikki.linnakangas@i 1386 :CBC 1536 : PG_RETURN_BOOL(cmp > 0);
1387 : : }
1388 : :
1389 : : /* Hash support */
1390 : :
1391 : : /* hash a range value */
1392 : : Datum
1393 : 105 : hash_range(PG_FUNCTION_ARGS)
1394 : : {
2962 tgl@sss.pgh.pa.us 1395 : 105 : RangeType *r = PG_GETARG_RANGE_P(0);
1396 : : uint32 result;
1397 : : TypeCacheEntry *typcache;
1398 : : TypeCacheEntry *scache;
1399 : : RangeBound lower;
1400 : : RangeBound upper;
1401 : : bool empty;
1402 : : char flags;
1403 : : uint32 lower_hash;
1404 : : uint32 upper_hash;
1405 : :
3676 noah@leadboat.com 1406 : 105 : check_stack_depth(); /* recurses when subtype is a range type */
1407 : :
5096 tgl@sss.pgh.pa.us 1408 : 105 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
1409 : :
1410 : : /* deserialize */
1411 : 105 : range_deserialize(typcache, r, &lower, &upper, &empty);
1412 : 105 : flags = range_get_flags(r);
1413 : :
1414 : : /*
1415 : : * Look up the element type's hash function, if not done already.
1416 : : */
1417 : 105 : scache = typcache->rngelemtype;
1418 [ + + ]: 105 : if (!OidIsValid(scache->hash_proc_finfo.fn_oid))
1419 : : {
1420 : 3 : scache = lookup_type_cache(scache->type_id, TYPECACHE_HASH_PROC_FINFO);
1421 [ - + ]: 3 : if (!OidIsValid(scache->hash_proc_finfo.fn_oid))
5108 heikki.linnakangas@i 1422 [ # # ]:UBC 0 : ereport(ERROR,
1423 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
1424 : : errmsg("could not identify a hash function for type %s",
1425 : : format_type_be(scache->type_id))));
1426 : : }
1427 : :
1428 : : /*
1429 : : * Apply the hash function to each bound.
1430 : : */
5108 heikki.linnakangas@i 1431 [ + + ]:CBC 105 : if (RANGE_HAS_LBOUND(flags))
5096 tgl@sss.pgh.pa.us 1432 : 72 : lower_hash = DatumGetUInt32(FunctionCall1Coll(&scache->hash_proc_finfo,
1433 : : typcache->rng_collation,
1434 : : lower.val));
1435 : : else
1436 : 33 : lower_hash = 0;
1437 : :
5108 heikki.linnakangas@i 1438 [ + + ]: 105 : if (RANGE_HAS_UBOUND(flags))
5096 tgl@sss.pgh.pa.us 1439 : 78 : upper_hash = DatumGetUInt32(FunctionCall1Coll(&scache->hash_proc_finfo,
1440 : : typcache->rng_collation,
1441 : : upper.val));
1442 : : else
1443 : 27 : upper_hash = 0;
1444 : :
1445 : : /* Merge hashes of flags and bounds */
84 peter@eisentraut.org 1446 :GNC 105 : result = hash_bytes_uint32((uint32) flags);
5108 heikki.linnakangas@i 1447 :CBC 105 : result ^= lower_hash;
1346 john.naylor@postgres 1448 : 105 : result = pg_rotate_left32(result, 1);
5108 heikki.linnakangas@i 1449 : 105 : result ^= upper_hash;
1450 : :
1451 : 105 : PG_RETURN_INT32(result);
1452 : : }
1453 : :
1454 : : /*
1455 : : * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
1456 : : * Otherwise, similar to hash_range.
1457 : : */
1458 : : Datum
2980 rhaas@postgresql.org 1459 : 30 : hash_range_extended(PG_FUNCTION_ARGS)
1460 : : {
2962 tgl@sss.pgh.pa.us 1461 : 30 : RangeType *r = PG_GETARG_RANGE_P(0);
2979 rhaas@postgresql.org 1462 : 30 : Datum seed = PG_GETARG_DATUM(1);
1463 : : uint64 result;
1464 : : TypeCacheEntry *typcache;
1465 : : TypeCacheEntry *scache;
1466 : : RangeBound lower;
1467 : : RangeBound upper;
1468 : : bool empty;
1469 : : char flags;
1470 : : uint64 lower_hash;
1471 : : uint64 upper_hash;
1472 : :
2980 1473 : 30 : check_stack_depth();
1474 : :
1475 : 30 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
1476 : :
1477 : 30 : range_deserialize(typcache, r, &lower, &upper, &empty);
1478 : 30 : flags = range_get_flags(r);
1479 : :
1480 : 30 : scache = typcache->rngelemtype;
1481 [ - + ]: 30 : if (!OidIsValid(scache->hash_extended_proc_finfo.fn_oid))
1482 : : {
2980 rhaas@postgresql.org 1483 :UBC 0 : scache = lookup_type_cache(scache->type_id,
1484 : : TYPECACHE_HASH_EXTENDED_PROC_FINFO);
1485 [ # # ]: 0 : if (!OidIsValid(scache->hash_extended_proc_finfo.fn_oid))
1486 [ # # ]: 0 : ereport(ERROR,
1487 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
1488 : : errmsg("could not identify a hash function for type %s",
1489 : : format_type_be(scache->type_id))));
1490 : : }
1491 : :
2980 rhaas@postgresql.org 1492 [ + - ]:CBC 30 : if (RANGE_HAS_LBOUND(flags))
1493 : 30 : lower_hash = DatumGetUInt64(FunctionCall2Coll(&scache->hash_extended_proc_finfo,
1494 : : typcache->rng_collation,
1495 : : lower.val,
1496 : : seed));
1497 : : else
2980 rhaas@postgresql.org 1498 :UBC 0 : lower_hash = 0;
1499 : :
2980 rhaas@postgresql.org 1500 [ + - ]:CBC 30 : if (RANGE_HAS_UBOUND(flags))
1501 : 30 : upper_hash = DatumGetUInt64(FunctionCall2Coll(&scache->hash_extended_proc_finfo,
1502 : : typcache->rng_collation,
1503 : : upper.val,
1504 : : seed));
1505 : : else
2980 rhaas@postgresql.org 1506 :UBC 0 : upper_hash = 0;
1507 : :
1508 : : /* Merge hashes of flags and bounds */
2979 rhaas@postgresql.org 1509 :CBC 30 : result = DatumGetUInt64(hash_uint32_extended((uint32) flags,
1510 : 30 : DatumGetInt64(seed)));
2980 1511 : 30 : result ^= lower_hash;
1512 : 30 : result = ROTATE_HIGH_AND_LOW_32BITS(result);
1513 : 30 : result ^= upper_hash;
1514 : :
1515 : 30 : PG_RETURN_UINT64(result);
1516 : : }
1517 : :
1518 : : /*
1519 : : *----------------------------------------------------------
1520 : : * CANONICAL FUNCTIONS
1521 : : *
1522 : : * Functions for specific built-in range types.
1523 : : *----------------------------------------------------------
1524 : : */
1525 : :
1526 : : Datum
5108 heikki.linnakangas@i 1527 : 222490 : int4range_canonical(PG_FUNCTION_ARGS)
1528 : : {
2962 tgl@sss.pgh.pa.us 1529 : 222490 : RangeType *r = PG_GETARG_RANGE_P(0);
1048 1530 : 222490 : Node *escontext = fcinfo->context;
1531 : : TypeCacheEntry *typcache;
1532 : : RangeBound lower;
1533 : : RangeBound upper;
1534 : : bool empty;
1535 : :
5096 1536 : 222490 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
1537 : :
1538 : 222490 : range_deserialize(typcache, r, &lower, &upper, &empty);
1539 : :
5108 heikki.linnakangas@i 1540 [ - + ]: 222490 : if (empty)
2962 tgl@sss.pgh.pa.us 1541 :UBC 0 : PG_RETURN_RANGE_P(r);
1542 : :
5108 heikki.linnakangas@i 1543 [ + + + + ]:CBC 222490 : if (!lower.infinite && !lower.inclusive)
1544 : : {
1048 tgl@sss.pgh.pa.us 1545 : 1624 : int32 bnd = DatumGetInt32(lower.val);
1546 : :
1547 : : /* Handle possible overflow manually */
1548 [ - + ]: 1624 : if (unlikely(bnd == PG_INT32_MAX))
1048 tgl@sss.pgh.pa.us 1549 [ # # ]:UBC 0 : ereturn(escontext, (Datum) 0,
1550 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1551 : : errmsg("integer out of range")));
1048 tgl@sss.pgh.pa.us 1552 :CBC 1624 : lower.val = Int32GetDatum(bnd + 1);
5108 heikki.linnakangas@i 1553 : 1624 : lower.inclusive = true;
1554 : : }
1555 : :
1556 [ + + + + ]: 222490 : if (!upper.infinite && upper.inclusive)
1557 : : {
1048 tgl@sss.pgh.pa.us 1558 : 1621 : int32 bnd = DatumGetInt32(upper.val);
1559 : :
1560 : : /* Handle possible overflow manually */
1561 [ + + ]: 1621 : if (unlikely(bnd == PG_INT32_MAX))
1562 [ + + ]: 6 : ereturn(escontext, (Datum) 0,
1563 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1564 : : errmsg("integer out of range")));
1565 : 1615 : upper.val = Int32GetDatum(bnd + 1);
5108 heikki.linnakangas@i 1566 : 1615 : upper.inclusive = false;
1567 : : }
1568 : :
1048 tgl@sss.pgh.pa.us 1569 : 222484 : PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper,
1570 : : false, escontext));
1571 : : }
1572 : :
1573 : : Datum
5108 heikki.linnakangas@i 1574 : 49 : int8range_canonical(PG_FUNCTION_ARGS)
1575 : : {
2962 tgl@sss.pgh.pa.us 1576 : 49 : RangeType *r = PG_GETARG_RANGE_P(0);
1048 1577 : 49 : Node *escontext = fcinfo->context;
1578 : : TypeCacheEntry *typcache;
1579 : : RangeBound lower;
1580 : : RangeBound upper;
1581 : : bool empty;
1582 : :
5096 1583 : 49 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
1584 : :
1585 : 49 : range_deserialize(typcache, r, &lower, &upper, &empty);
1586 : :
5108 heikki.linnakangas@i 1587 [ - + ]: 49 : if (empty)
2962 tgl@sss.pgh.pa.us 1588 :UBC 0 : PG_RETURN_RANGE_P(r);
1589 : :
5108 heikki.linnakangas@i 1590 [ + - + + ]:CBC 49 : if (!lower.infinite && !lower.inclusive)
1591 : : {
1048 tgl@sss.pgh.pa.us 1592 : 9 : int64 bnd = DatumGetInt64(lower.val);
1593 : :
1594 : : /* Handle possible overflow manually */
1595 [ - + ]: 9 : if (unlikely(bnd == PG_INT64_MAX))
1048 tgl@sss.pgh.pa.us 1596 [ # # ]:UBC 0 : ereturn(escontext, (Datum) 0,
1597 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1598 : : errmsg("bigint out of range")));
1048 tgl@sss.pgh.pa.us 1599 :CBC 9 : lower.val = Int64GetDatum(bnd + 1);
5108 heikki.linnakangas@i 1600 : 9 : lower.inclusive = true;
1601 : : }
1602 : :
1603 [ + - + + ]: 49 : if (!upper.infinite && upper.inclusive)
1604 : : {
1048 tgl@sss.pgh.pa.us 1605 : 12 : int64 bnd = DatumGetInt64(upper.val);
1606 : :
1607 : : /* Handle possible overflow manually */
1608 [ - + ]: 12 : if (unlikely(bnd == PG_INT64_MAX))
1048 tgl@sss.pgh.pa.us 1609 [ # # ]:UBC 0 : ereturn(escontext, (Datum) 0,
1610 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
1611 : : errmsg("bigint out of range")));
1048 tgl@sss.pgh.pa.us 1612 :CBC 12 : upper.val = Int64GetDatum(bnd + 1);
5108 heikki.linnakangas@i 1613 : 12 : upper.inclusive = false;
1614 : : }
1615 : :
1048 tgl@sss.pgh.pa.us 1616 : 49 : PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper,
1617 : : false, escontext));
1618 : : }
1619 : :
1620 : : Datum
5108 heikki.linnakangas@i 1621 : 1821 : daterange_canonical(PG_FUNCTION_ARGS)
1622 : : {
2962 tgl@sss.pgh.pa.us 1623 : 1821 : RangeType *r = PG_GETARG_RANGE_P(0);
1048 1624 : 1821 : Node *escontext = fcinfo->context;
1625 : : TypeCacheEntry *typcache;
1626 : : RangeBound lower;
1627 : : RangeBound upper;
1628 : : bool empty;
1629 : :
5096 1630 : 1821 : typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
1631 : :
1632 : 1821 : range_deserialize(typcache, r, &lower, &upper, &empty);
1633 : :
5108 heikki.linnakangas@i 1634 [ - + ]: 1821 : if (empty)
2962 tgl@sss.pgh.pa.us 1635 :UBC 0 : PG_RETURN_RANGE_P(r);
1636 : :
2294 jdavis@postgresql.or 1637 [ + + + + :CBC 1821 : if (!lower.infinite && !DATE_NOT_FINITE(DatumGetDateADT(lower.val)) &&
+ - ]
1638 [ + + ]: 1797 : !lower.inclusive)
1639 : : {
1048 tgl@sss.pgh.pa.us 1640 : 18 : DateADT bnd = DatumGetDateADT(lower.val);
1641 : :
1642 : : /* Check for overflow -- note we already eliminated PG_INT32_MAX */
1643 : 18 : bnd++;
1644 [ + - - + : 18 : if (unlikely(!IS_VALID_DATE(bnd)))
- + ]
1048 tgl@sss.pgh.pa.us 1645 [ # # ]:UBC 0 : ereturn(escontext, (Datum) 0,
1646 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1647 : : errmsg("date out of range")));
1048 tgl@sss.pgh.pa.us 1648 :CBC 18 : lower.val = DateADTGetDatum(bnd);
5108 heikki.linnakangas@i 1649 : 18 : lower.inclusive = true;
1650 : : }
1651 : :
2294 jdavis@postgresql.or 1652 [ + + + - : 1821 : if (!upper.infinite && !DATE_NOT_FINITE(DatumGetDateADT(upper.val)) &&
+ + ]
1653 [ + + ]: 1734 : upper.inclusive)
1654 : : {
1048 tgl@sss.pgh.pa.us 1655 : 18 : DateADT bnd = DatumGetDateADT(upper.val);
1656 : :
1657 : : /* Check for overflow -- note we already eliminated PG_INT32_MAX */
1658 : 18 : bnd++;
1659 [ + - + + : 18 : if (unlikely(!IS_VALID_DATE(bnd)))
+ + ]
1660 [ + + ]: 6 : ereturn(escontext, (Datum) 0,
1661 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1662 : : errmsg("date out of range")));
1663 : 12 : upper.val = DateADTGetDatum(bnd);
5108 heikki.linnakangas@i 1664 : 12 : upper.inclusive = false;
1665 : : }
1666 : :
1048 tgl@sss.pgh.pa.us 1667 : 1815 : PG_RETURN_RANGE_P(range_serialize(typcache, &lower, &upper,
1668 : : false, escontext));
1669 : : }
1670 : :
1671 : : /*
1672 : : *----------------------------------------------------------
1673 : : * SUBTYPE_DIFF FUNCTIONS
1674 : : *
1675 : : * Functions for specific built-in range types.
1676 : : *
1677 : : * Note that subtype_diff does return the difference, not the absolute value
1678 : : * of the difference, and it must take care to avoid overflow.
1679 : : * (numrange_subdiff is at some risk there ...)
1680 : : *----------------------------------------------------------
1681 : : */
1682 : :
1683 : : Datum
5108 heikki.linnakangas@i 1684 : 423645 : int4range_subdiff(PG_FUNCTION_ARGS)
1685 : : {
5097 bruce@momjian.us 1686 : 423645 : int32 v1 = PG_GETARG_INT32(0);
1687 : 423645 : int32 v2 = PG_GETARG_INT32(1);
1688 : :
tgl@sss.pgh.pa.us 1689 : 423645 : PG_RETURN_FLOAT8((float8) v1 - (float8) v2);
1690 : : }
1691 : :
1692 : : Datum
5108 heikki.linnakangas@i 1693 :UBC 0 : int8range_subdiff(PG_FUNCTION_ARGS)
1694 : : {
5097 bruce@momjian.us 1695 : 0 : int64 v1 = PG_GETARG_INT64(0);
1696 : 0 : int64 v2 = PG_GETARG_INT64(1);
1697 : :
tgl@sss.pgh.pa.us 1698 : 0 : PG_RETURN_FLOAT8((float8) v1 - (float8) v2);
1699 : : }
1700 : :
1701 : : Datum
5108 heikki.linnakangas@i 1702 :CBC 123 : numrange_subdiff(PG_FUNCTION_ARGS)
1703 : : {
1704 : 123 : Datum v1 = PG_GETARG_DATUM(0);
1705 : 123 : Datum v2 = PG_GETARG_DATUM(1);
1706 : : Datum numresult;
1707 : : float8 floatresult;
1708 : :
1709 : 123 : numresult = DirectFunctionCall2(numeric_sub, v1, v2);
1710 : :
5097 tgl@sss.pgh.pa.us 1711 : 123 : floatresult = DatumGetFloat8(DirectFunctionCall1(numeric_float8,
1712 : : numresult));
1713 : :
5108 heikki.linnakangas@i 1714 : 123 : PG_RETURN_FLOAT8(floatresult);
1715 : : }
1716 : :
1717 : : Datum
5097 tgl@sss.pgh.pa.us 1718 :UBC 0 : daterange_subdiff(PG_FUNCTION_ARGS)
1719 : : {
1720 : 0 : int32 v1 = PG_GETARG_INT32(0);
1721 : 0 : int32 v2 = PG_GETARG_INT32(1);
1722 : :
1723 : 0 : PG_RETURN_FLOAT8((float8) v1 - (float8) v2);
1724 : : }
1725 : :
1726 : : Datum
5108 heikki.linnakangas@i 1727 : 0 : tsrange_subdiff(PG_FUNCTION_ARGS)
1728 : : {
1729 : 0 : Timestamp v1 = PG_GETARG_TIMESTAMP(0);
1730 : 0 : Timestamp v2 = PG_GETARG_TIMESTAMP(1);
1731 : : float8 result;
1732 : :
5097 tgl@sss.pgh.pa.us 1733 : 0 : result = ((float8) v1 - (float8) v2) / USECS_PER_SEC;
5108 heikki.linnakangas@i 1734 : 0 : PG_RETURN_FLOAT8(result);
1735 : : }
1736 : :
1737 : : Datum
1738 : 0 : tstzrange_subdiff(PG_FUNCTION_ARGS)
1739 : : {
1740 : 0 : Timestamp v1 = PG_GETARG_TIMESTAMP(0);
1741 : 0 : Timestamp v2 = PG_GETARG_TIMESTAMP(1);
1742 : : float8 result;
1743 : :
5097 tgl@sss.pgh.pa.us 1744 : 0 : result = ((float8) v1 - (float8) v2) / USECS_PER_SEC;
5108 heikki.linnakangas@i 1745 : 0 : PG_RETURN_FLOAT8(result);
1746 : : }
1747 : :
1748 : : /*
1749 : : *----------------------------------------------------------
1750 : : * SUPPORT FUNCTIONS
1751 : : *
1752 : : * These functions aren't in pg_proc, but are useful for
1753 : : * defining new generic range functions in C.
1754 : : *----------------------------------------------------------
1755 : : */
1756 : :
1757 : : /*
1758 : : * range_get_typcache: get cached information about a range type
1759 : : *
1760 : : * This is for use by range-related functions that follow the convention
1761 : : * of using the fn_extra field as a pointer to the type cache entry for
1762 : : * the range type. Functions that need to cache more information than
1763 : : * that must fend for themselves.
1764 : : */
1765 : : TypeCacheEntry *
5096 tgl@sss.pgh.pa.us 1766 :CBC 2066194 : range_get_typcache(FunctionCallInfo fcinfo, Oid rngtypid)
1767 : : {
1768 : 2066194 : TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
1769 : :
1770 [ + + ]: 2066194 : if (typcache == NULL ||
1771 [ - + ]: 2056948 : typcache->type_id != rngtypid)
1772 : : {
1773 : 9246 : typcache = lookup_type_cache(rngtypid, TYPECACHE_RANGE_INFO);
1774 [ - + ]: 9246 : if (typcache->rngelemtype == NULL)
5096 tgl@sss.pgh.pa.us 1775 [ # # ]:UBC 0 : elog(ERROR, "type %u is not a range type", rngtypid);
334 peter@eisentraut.org 1776 :CBC 9246 : fcinfo->flinfo->fn_extra = typcache;
1777 : : }
1778 : :
5096 tgl@sss.pgh.pa.us 1779 : 2066194 : return typcache;
1780 : : }
1781 : :
1782 : : /*
1783 : : * range_serialize: construct a range value from bounds and empty-flag
1784 : : *
1785 : : * This does not force canonicalization of the range value. In most cases,
1786 : : * external callers should only be canonicalization functions. Note that
1787 : : * we perform some datatype-independent canonicalization checks anyway.
1788 : : */
1789 : : RangeType *
1790 : 454905 : range_serialize(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
1791 : : bool empty, struct Node *escontext)
1792 : : {
1793 : : RangeType *range;
1794 : : int cmp;
1795 : : Size msize;
1796 : : Pointer ptr;
1797 : : int16 typlen;
1798 : : bool typbyval;
1799 : : char typalign;
1800 : : char typstorage;
5097 bruce@momjian.us 1801 : 454905 : char flags = 0;
1802 : :
1803 : : /*
1804 : : * Verify range is not invalid on its face, and construct flags value,
1805 : : * preventing any non-canonical combinations such as infinite+inclusive.
1806 : : */
5089 tgl@sss.pgh.pa.us 1807 [ - + ]: 454905 : Assert(lower->lower);
1808 [ - + ]: 454905 : Assert(!upper->lower);
1809 : :
5108 heikki.linnakangas@i 1810 [ + + ]: 454905 : if (empty)
1811 : 1875 : flags |= RANGE_EMPTY;
1812 : : else
1813 : : {
5089 tgl@sss.pgh.pa.us 1814 : 453030 : cmp = range_cmp_bound_values(typcache, lower, upper);
1815 : :
1816 : : /* error check: if lower bound value is above upper, it's wrong */
1817 [ + + ]: 453030 : if (cmp > 0)
1048 1818 [ + + ]: 33 : ereturn(escontext, NULL,
1819 : : (errcode(ERRCODE_DATA_EXCEPTION),
1820 : : errmsg("range lower bound must be less than or equal to range upper bound")));
1821 : :
1822 : : /* if bounds are equal, and not both inclusive, range is empty */
5089 1823 [ + + + + : 452997 : if (cmp == 0 && !(lower->inclusive && upper->inclusive))
+ + ]
1824 : 192 : flags |= RANGE_EMPTY;
1825 : : else
1826 : : {
1827 : : /* infinite boundaries are never inclusive */
1828 [ + + ]: 452805 : if (lower->infinite)
1829 : 5406 : flags |= RANGE_LB_INF;
1830 [ + + ]: 447399 : else if (lower->inclusive)
1831 : 445583 : flags |= RANGE_LB_INC;
1832 [ + + ]: 452805 : if (upper->infinite)
1833 : 3533 : flags |= RANGE_UB_INF;
1834 [ + + ]: 449272 : else if (upper->inclusive)
1835 : 2063 : flags |= RANGE_UB_INC;
1836 : : }
1837 : : }
1838 : :
1839 : : /* Fetch information about range's element type */
1840 : 454872 : typlen = typcache->rngelemtype->typlen;
1841 : 454872 : typbyval = typcache->rngelemtype->typbyval;
1842 : 454872 : typalign = typcache->rngelemtype->typalign;
1843 : 454872 : typstorage = typcache->rngelemtype->typstorage;
1844 : :
1845 : : /* Count space for varlena header and range type's OID */
5096 1846 : 454872 : msize = sizeof(RangeType);
1847 [ - + ]: 454872 : Assert(msize == MAXALIGN(msize));
1848 : :
1849 : : /* Count space for bounds */
5108 heikki.linnakangas@i 1850 [ + + ]: 454872 : if (RANGE_HAS_LBOUND(flags))
1851 : : {
1852 : : /*
1853 : : * Make sure item to be inserted is not toasted. It is essential that
1854 : : * we not insert an out-of-line toast value pointer into a range
1855 : : * object, for the same reasons that arrays and records can't contain
1856 : : * them. It would work to store a compressed-in-line value, but we
1857 : : * prefer to decompress and then let compression be applied to the
1858 : : * whole range object if necessary. But, unlike arrays, we do allow
1859 : : * short-header varlena objects to stay as-is.
1860 : : */
5097 tgl@sss.pgh.pa.us 1861 [ + + ]: 447399 : if (typlen == -1)
1862 : 2380 : lower->val = PointerGetDatum(PG_DETOAST_DATUM_PACKED(lower->val));
1863 : :
5108 heikki.linnakangas@i 1864 : 447399 : msize = datum_compute_size(msize, lower->val, typbyval, typalign,
1865 : : typlen, typstorage);
1866 : : }
1867 : :
1868 [ + + ]: 454872 : if (RANGE_HAS_UBOUND(flags))
1869 : : {
1870 : : /* Make sure item to be inserted is not toasted */
5097 tgl@sss.pgh.pa.us 1871 [ + + ]: 449272 : if (typlen == -1)
1872 : 2362 : upper->val = PointerGetDatum(PG_DETOAST_DATUM_PACKED(upper->val));
1873 : :
5108 heikki.linnakangas@i 1874 : 449272 : msize = datum_compute_size(msize, upper->val, typbyval, typalign,
1875 : : typlen, typstorage);
1876 : : }
1877 : :
1878 : : /* Add space for flag byte */
1879 : 454872 : msize += sizeof(char);
1880 : :
1881 : : /* Note: zero-fill is required here, just as in heap tuples */
5096 tgl@sss.pgh.pa.us 1882 : 454872 : range = (RangeType *) palloc0(msize);
1883 : 454872 : SET_VARSIZE(range, msize);
1884 : :
1885 : : /* Now fill in the datum */
1886 : 454872 : range->rangetypid = typcache->type_id;
1887 : :
1888 : 454872 : ptr = (char *) (range + 1);
1889 : :
5108 heikki.linnakangas@i 1890 [ + + ]: 454872 : if (RANGE_HAS_LBOUND(flags))
1891 : : {
1892 [ - + ]: 447399 : Assert(lower->lower);
1893 : 447399 : ptr = datum_write(ptr, lower->val, typbyval, typalign, typlen,
1894 : : typstorage);
1895 : : }
1896 : :
1897 [ + + ]: 454872 : if (RANGE_HAS_UBOUND(flags))
1898 : : {
1899 [ - + ]: 449272 : Assert(!upper->lower);
1900 : 449272 : ptr = datum_write(ptr, upper->val, typbyval, typalign, typlen,
1901 : : typstorage);
1902 : : }
1903 : :
5096 tgl@sss.pgh.pa.us 1904 : 454872 : *((char *) ptr) = flags;
1905 : :
1906 : 454872 : return range;
1907 : : }
1908 : :
1909 : : /*
1910 : : * range_deserialize: deconstruct a range value
1911 : : *
1912 : : * NB: the given range object must be fully detoasted; it cannot have a
1913 : : * short varlena header.
1914 : : *
1915 : : * Note that if the element type is pass-by-reference, the datums in the
1916 : : * RangeBound structs will be pointers into the given range object.
1917 : : */
1918 : : void
2189 peter@eisentraut.org 1919 : 4889401 : range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
1920 : : RangeBound *lower, RangeBound *upper, bool *empty)
1921 : : {
1922 : : char flags;
1923 : : int16 typlen;
1924 : : bool typbyval;
1925 : : char typalign;
1926 : : Pointer ptr;
1927 : : Datum lbound;
1928 : : Datum ubound;
1929 : :
1930 : : /* assert caller passed the right typcache entry */
5096 tgl@sss.pgh.pa.us 1931 [ - + ]: 4889401 : Assert(RangeTypeGetOid(range) == typcache->type_id);
1932 : :
1933 : : /* fetch the flag byte from datum's last byte */
2189 peter@eisentraut.org 1934 : 4889401 : flags = *((const char *) range + VARSIZE(range) - 1);
1935 : :
1936 : : /* fetch information about range's element type */
5096 tgl@sss.pgh.pa.us 1937 : 4889401 : typlen = typcache->rngelemtype->typlen;
1938 : 4889401 : typbyval = typcache->rngelemtype->typbyval;
1939 : 4889401 : typalign = typcache->rngelemtype->typalign;
1940 : :
1941 : : /* initialize data pointer just after the range OID */
1942 : 4889401 : ptr = (Pointer) (range + 1);
1943 : :
1944 : : /* fetch lower bound, if any */
5108 heikki.linnakangas@i 1945 [ + + ]: 4889401 : if (RANGE_HAS_LBOUND(flags))
1946 : : {
1947 : : /* att_align_pointer cannot be necessary here */
1948 : 4310163 : lbound = fetch_att(ptr, typbyval, typlen);
5097 tgl@sss.pgh.pa.us 1949 [ + + + - : 4310163 : ptr = (Pointer) att_addlength_pointer(ptr, typlen, ptr);
- - - - -
- - - + -
- - ]
1950 : : }
1951 : : else
5108 heikki.linnakangas@i 1952 : 579238 : lbound = (Datum) 0;
1953 : :
1954 : : /* fetch upper bound, if any */
1955 [ + + ]: 4889401 : if (RANGE_HAS_UBOUND(flags))
1956 : : {
1957 [ + + + + : 4320928 : ptr = (Pointer) att_align_pointer(ptr, typalign, typlen, ptr);
+ + + - +
- - - ]
1958 : 4320928 : ubound = fetch_att(ptr, typbyval, typlen);
1959 : : /* no need for att_addlength_pointer */
1960 : : }
1961 : : else
1962 : 568473 : ubound = (Datum) 0;
1963 : :
1964 : : /* emit results */
1965 : :
5096 tgl@sss.pgh.pa.us 1966 : 4889401 : *empty = (flags & RANGE_EMPTY) != 0;
1967 : :
5097 bruce@momjian.us 1968 : 4889401 : lower->val = lbound;
tgl@sss.pgh.pa.us 1969 : 4889401 : lower->infinite = (flags & RANGE_LB_INF) != 0;
5096 1970 : 4889401 : lower->inclusive = (flags & RANGE_LB_INC) != 0;
5097 bruce@momjian.us 1971 : 4889401 : lower->lower = true;
1972 : :
1973 : 4889401 : upper->val = ubound;
tgl@sss.pgh.pa.us 1974 : 4889401 : upper->infinite = (flags & RANGE_UB_INF) != 0;
5096 1975 : 4889401 : upper->inclusive = (flags & RANGE_UB_INC) != 0;
5097 bruce@momjian.us 1976 : 4889401 : upper->lower = false;
5108 heikki.linnakangas@i 1977 : 4889401 : }
1978 : :
1979 : : /*
1980 : : * range_get_flags: just get the flags from a RangeType value.
1981 : : *
1982 : : * This is frequently useful in places that only need the flags and not
1983 : : * the full results of range_deserialize.
1984 : : */
1985 : : char
2189 peter@eisentraut.org 1986 : 1473172 : range_get_flags(const RangeType *range)
1987 : : {
1988 : : /* fetch the flag byte from datum's last byte */
5096 tgl@sss.pgh.pa.us 1989 : 1473172 : return *((char *) range + VARSIZE(range) - 1);
1990 : : }
1991 : :
1992 : : /*
1993 : : * range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
1994 : : *
1995 : : * This is only needed in GiST operations, so we don't include a provision
1996 : : * for setting it in range_serialize; rather, this function must be applied
1997 : : * afterwards.
1998 : : */
1999 : : void
5084 2000 : 309 : range_set_contain_empty(RangeType *range)
2001 : : {
2002 : : char *flagsp;
2003 : :
2004 : : /* flag byte is datum's last byte */
2005 : 309 : flagsp = (char *) range + VARSIZE(range) - 1;
2006 : :
2007 : 309 : *flagsp |= RANGE_CONTAIN_EMPTY;
2008 : 309 : }
2009 : :
2010 : : /*
2011 : : * This both serializes and canonicalizes (if applicable) the range.
2012 : : * This should be used by most callers.
2013 : : */
2014 : : RangeType *
5096 2015 : 229071 : make_range(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
2016 : : bool empty, struct Node *escontext)
2017 : : {
2018 : : RangeType *range;
2019 : :
1048 2020 : 229071 : range = range_serialize(typcache, lower, upper, empty, escontext);
2021 : :
2022 [ + + + - : 229044 : if (SOFT_ERROR_OCCURRED(escontext))
+ + ]
2023 : 6 : return NULL;
2024 : :
2025 : : /* no need to call canonical on empty ranges ... */
5089 2026 [ + + ]: 229038 : if (OidIsValid(typcache->rng_canonical_finfo.fn_oid) &&
2027 [ + + ]: 226220 : !RangeIsEmpty(range))
2028 : : {
2029 : : /* Do this the hard way so that we can pass escontext */
1048 2030 : 224360 : LOCAL_FCINFO(fcinfo, 1);
2031 : : Datum result;
2032 : :
2033 : 224360 : InitFunctionCallInfoData(*fcinfo, &typcache->rng_canonical_finfo, 1,
2034 : : InvalidOid, escontext, NULL);
2035 : :
2036 : 224360 : fcinfo->args[0].value = RangeTypePGetDatum(range);
2037 : 224360 : fcinfo->args[0].isnull = false;
2038 : :
2039 : 224360 : result = FunctionCallInvoke(fcinfo);
2040 : :
2041 [ + + + - : 224360 : if (SOFT_ERROR_OCCURRED(escontext))
+ + ]
2042 : 12 : return NULL;
2043 : :
2044 : : /* Should not get a null result if there was no error */
2045 [ - + ]: 224348 : if (fcinfo->isnull)
1048 tgl@sss.pgh.pa.us 2046 [ # # ]:UBC 0 : elog(ERROR, "function %u returned NULL",
2047 : : typcache->rng_canonical_finfo.fn_oid);
2048 : :
1048 tgl@sss.pgh.pa.us 2049 :CBC 224348 : range = DatumGetRangeTypeP(result);
2050 : : }
2051 : :
5096 2052 : 229026 : return range;
2053 : : }
2054 : :
2055 : : /*
2056 : : * Compare two range boundary points, returning <0, 0, or >0 according to
2057 : : * whether b1 is less than, equal to, or greater than b2.
2058 : : *
2059 : : * The boundaries can be any combination of upper and lower; so it's useful
2060 : : * for a variety of operators.
2061 : : *
2062 : : * The simple case is when b1 and b2 are both finite and inclusive, in which
2063 : : * case the result is just a comparison of the values held in b1 and b2.
2064 : : *
2065 : : * If a bound is exclusive, then we need to know whether it's a lower bound,
2066 : : * in which case we treat the boundary point as "just greater than" the held
2067 : : * value; or an upper bound, in which case we treat the boundary point as
2068 : : * "just less than" the held value.
2069 : : *
2070 : : * If a bound is infinite, it represents minus infinity (less than every other
2071 : : * point) if it's a lower bound; or plus infinity (greater than every other
2072 : : * point) if it's an upper bound.
2073 : : *
2074 : : * There is only one case where two boundaries compare equal but are not
2075 : : * identical: when both bounds are inclusive and hold the same finite value,
2076 : : * but one is an upper bound and the other a lower bound.
2077 : : */
2078 : : int
2189 peter@eisentraut.org 2079 : 6153875 : range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1, const RangeBound *b2)
2080 : : {
2081 : : int32 result;
2082 : :
2083 : : /*
2084 : : * First, handle cases involving infinity, which don't require invoking
2085 : : * the comparison proc.
2086 : : */
5108 heikki.linnakangas@i 2087 [ + + + + ]: 6153875 : if (b1->infinite && b2->infinite)
2088 : : {
2089 : : /*
2090 : : * Both are infinity, so they are equal unless one is lower and the
2091 : : * other not.
2092 : : */
2093 [ + + ]: 10298 : if (b1->lower == b2->lower)
2094 : 10253 : return 0;
2095 : : else
5094 tgl@sss.pgh.pa.us 2096 [ + + ]: 45 : return b1->lower ? -1 : 1;
2097 : : }
2098 [ + + ]: 6143577 : else if (b1->infinite)
2099 [ + + ]: 48389 : return b1->lower ? -1 : 1;
2100 [ + + ]: 6095188 : else if (b2->infinite)
2101 [ + + ]: 15797 : return b2->lower ? 1 : -1;
2102 : :
2103 : : /*
2104 : : * Both boundaries are finite, so compare the held values.
2105 : : */
5096 2106 : 6079391 : result = DatumGetInt32(FunctionCall2Coll(&typcache->rng_cmp_proc_finfo,
2107 : : typcache->rng_collation,
5108 heikki.linnakangas@i 2108 : 6079391 : b1->val, b2->val));
2109 : :
2110 : : /*
2111 : : * If the comparison is anything other than equal, we're done. If they
2112 : : * compare equal though, we still have to consider whether the boundaries
2113 : : * are inclusive or exclusive.
2114 : : */
2115 [ + + ]: 6079391 : if (result == 0)
2116 : : {
5094 tgl@sss.pgh.pa.us 2117 [ + + + + ]: 426396 : if (!b1->inclusive && !b2->inclusive)
2118 : : {
2119 : : /* both are exclusive */
2120 [ + + ]: 189380 : if (b1->lower == b2->lower)
2121 : 189377 : return 0;
2122 : : else
2123 [ - + ]: 3 : return b1->lower ? 1 : -1;
2124 : : }
2125 [ + + ]: 237016 : else if (!b1->inclusive)
2126 [ - + ]: 384 : return b1->lower ? 1 : -1;
2127 [ + + ]: 236632 : else if (!b2->inclusive)
2128 [ + + ]: 614 : return b2->lower ? -1 : 1;
2129 : : else
2130 : : {
2131 : : /*
2132 : : * Both are inclusive and the values held are equal, so they are
2133 : : * equal regardless of whether they are upper or lower boundaries,
2134 : : * or a mix.
2135 : : */
2136 : 236018 : return 0;
2137 : : }
2138 : : }
2139 : :
5108 heikki.linnakangas@i 2140 : 5652995 : return result;
2141 : : }
2142 : :
2143 : : /*
2144 : : * Compare two range boundary point values, returning <0, 0, or >0 according
2145 : : * to whether b1 is less than, equal to, or greater than b2.
2146 : : *
2147 : : * This is similar to but simpler than range_cmp_bounds(). We just compare
2148 : : * the values held in b1 and b2, ignoring inclusive/exclusive flags. The
2149 : : * lower/upper flags only matter for infinities, where they tell us if the
2150 : : * infinity is plus or minus.
2151 : : */
2152 : : int
2189 peter@eisentraut.org 2153 : 689107 : range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
2154 : : const RangeBound *b2)
2155 : : {
2156 : : /*
2157 : : * First, handle cases involving infinity, which don't require invoking
2158 : : * the comparison proc.
2159 : : */
5089 tgl@sss.pgh.pa.us 2160 [ + + + + ]: 689107 : if (b1->infinite && b2->infinite)
2161 : : {
2162 : : /*
2163 : : * Both are infinity, so they are equal unless one is lower and the
2164 : : * other not.
2165 : : */
2166 [ - + ]: 157 : if (b1->lower == b2->lower)
5089 tgl@sss.pgh.pa.us 2167 :UBC 0 : return 0;
2168 : : else
5089 tgl@sss.pgh.pa.us 2169 [ + + ]:CBC 157 : return b1->lower ? -1 : 1;
2170 : : }
2171 [ + + ]: 688950 : else if (b1->infinite)
2172 [ + + ]: 8575 : return b1->lower ? -1 : 1;
2173 [ + + ]: 680375 : else if (b2->infinite)
2174 [ + + ]: 7060 : return b2->lower ? 1 : -1;
2175 : :
2176 : : /*
2177 : : * Both boundaries are finite, so compare the held values.
2178 : : */
2179 : 673315 : return DatumGetInt32(FunctionCall2Coll(&typcache->rng_cmp_proc_finfo,
2180 : : typcache->rng_collation,
2181 : 673315 : b1->val, b2->val));
2182 : : }
2183 : :
2184 : : /*
2185 : : * qsort callback for sorting ranges.
2186 : : *
2187 : : * Two empty ranges compare equal; an empty range sorts to the left of any
2188 : : * non-empty range. Two non-empty ranges are sorted by lower bound first
2189 : : * and by upper bound next.
2190 : : */
2191 : : int
1773 akorotkov@postgresql 2192 : 13456 : range_compare(const void *key1, const void *key2, void *arg)
2193 : : {
2194 : 13456 : RangeType *r1 = *(RangeType **) key1;
2195 : 13456 : RangeType *r2 = *(RangeType **) key2;
2196 : 13456 : TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
2197 : : RangeBound lower1;
2198 : : RangeBound upper1;
2199 : : RangeBound lower2;
2200 : : RangeBound upper2;
2201 : : bool empty1;
2202 : : bool empty2;
2203 : : int cmp;
2204 : :
2205 : 13456 : range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
2206 : 13456 : range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
2207 : :
2208 [ + + + + ]: 13456 : if (empty1 && empty2)
2209 : 24 : cmp = 0;
2210 [ + + ]: 13432 : else if (empty1)
2211 : 21 : cmp = -1;
2212 [ + + ]: 13411 : else if (empty2)
2213 : 6 : cmp = 1;
2214 : : else
2215 : : {
2216 : 13405 : cmp = range_cmp_bounds(typcache, &lower1, &lower2);
2217 [ + + ]: 13405 : if (cmp == 0)
2218 : 24 : cmp = range_cmp_bounds(typcache, &upper1, &upper2);
2219 : : }
2220 : :
2221 : 13456 : return cmp;
2222 : : }
2223 : :
2224 : : /*
2225 : : * Build an empty range value of the type indicated by the typcache entry.
2226 : : */
2227 : : RangeType *
5096 tgl@sss.pgh.pa.us 2228 : 1581 : make_empty_range(TypeCacheEntry *typcache)
2229 : : {
2230 : : RangeBound lower;
2231 : : RangeBound upper;
2232 : :
2233 : 1581 : lower.val = (Datum) 0;
2234 : 1581 : lower.infinite = false;
2235 : 1581 : lower.inclusive = false;
5108 heikki.linnakangas@i 2236 : 1581 : lower.lower = true;
2237 : :
5096 tgl@sss.pgh.pa.us 2238 : 1581 : upper.val = (Datum) 0;
2239 : 1581 : upper.infinite = false;
2240 : 1581 : upper.inclusive = false;
5108 heikki.linnakangas@i 2241 : 1581 : upper.lower = false;
2242 : :
1048 tgl@sss.pgh.pa.us 2243 : 1581 : return make_range(typcache, &lower, &upper, true, NULL);
2244 : : }
2245 : :
2246 : : /*
2247 : : * Planner support function for elem_contained_by_range (<@ operator).
2248 : : */
2249 : : Datum
647 2250 : 63 : elem_contained_by_range_support(PG_FUNCTION_ARGS)
2251 : : {
2252 : 63 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
2253 : 63 : Node *ret = NULL;
2254 : :
2255 [ + + ]: 63 : if (IsA(rawreq, SupportRequestSimplify))
2256 : : {
2257 : 48 : SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
2258 : 48 : FuncExpr *fexpr = req->fcall;
2259 : : Expr *leftop,
2260 : : *rightop;
2261 : :
2262 [ - + ]: 48 : Assert(list_length(fexpr->args) == 2);
2263 : 48 : leftop = linitial(fexpr->args);
2264 : 48 : rightop = lsecond(fexpr->args);
2265 : :
2266 : 48 : ret = find_simplified_clause(req->root, rightop, leftop);
2267 : : }
2268 : :
2269 : 63 : PG_RETURN_POINTER(ret);
2270 : : }
2271 : :
2272 : : /*
2273 : : * Planner support function for range_contains_elem (@> operator).
2274 : : */
2275 : : Datum
2276 : 153 : range_contains_elem_support(PG_FUNCTION_ARGS)
2277 : : {
2278 : 153 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
2279 : 153 : Node *ret = NULL;
2280 : :
2281 [ + + ]: 153 : if (IsA(rawreq, SupportRequestSimplify))
2282 : : {
2283 : 78 : SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
2284 : 78 : FuncExpr *fexpr = req->fcall;
2285 : : Expr *leftop,
2286 : : *rightop;
2287 : :
2288 [ - + ]: 78 : Assert(list_length(fexpr->args) == 2);
2289 : 78 : leftop = linitial(fexpr->args);
2290 : 78 : rightop = lsecond(fexpr->args);
2291 : :
2292 : 78 : ret = find_simplified_clause(req->root, leftop, rightop);
2293 : : }
2294 : :
2295 : 153 : PG_RETURN_POINTER(ret);
2296 : : }
2297 : :
2298 : :
2299 : : /*
2300 : : *----------------------------------------------------------
2301 : : * STATIC FUNCTIONS
2302 : : *----------------------------------------------------------
2303 : : */
2304 : :
2305 : : /*
2306 : : * Given a string representing the flags for the range type, return the flags
2307 : : * represented as a char.
2308 : : */
2309 : : static char
5097 2310 : 2583 : range_parse_flags(const char *flags_str)
2311 : : {
bruce@momjian.us 2312 : 2583 : char flags = 0;
2313 : :
5108 heikki.linnakangas@i 2314 [ + - ]: 2583 : if (flags_str[0] == '\0' ||
2315 [ + - ]: 2583 : flags_str[1] == '\0' ||
2316 [ - + ]: 2583 : flags_str[2] != '\0')
5108 heikki.linnakangas@i 2317 [ # # ]:UBC 0 : ereport(ERROR,
2318 : : (errcode(ERRCODE_SYNTAX_ERROR),
2319 : : errmsg("invalid range bound flags"),
2320 : : errhint("Valid values are \"[]\", \"[)\", \"(]\", and \"()\".")));
2321 : :
5108 heikki.linnakangas@i 2322 [ + + - ]:CBC 2583 : switch (flags_str[0])
2323 : : {
2324 : 120 : case '[':
2325 : 120 : flags |= RANGE_LB_INC;
2326 : 120 : break;
2327 : 2463 : case '(':
2328 : 2463 : break;
5108 heikki.linnakangas@i 2329 :UBC 0 : default:
2330 [ # # ]: 0 : ereport(ERROR,
2331 : : (errcode(ERRCODE_SYNTAX_ERROR),
2332 : : errmsg("invalid range bound flags"),
2333 : : errhint("Valid values are \"[]\", \"[)\", \"(]\", and \"()\".")));
2334 : : }
2335 : :
5108 heikki.linnakangas@i 2336 [ + + - ]:CBC 2583 : switch (flags_str[1])
2337 : : {
2338 : 2526 : case ']':
2339 : 2526 : flags |= RANGE_UB_INC;
2340 : 2526 : break;
2341 : 57 : case ')':
2342 : 57 : break;
5108 heikki.linnakangas@i 2343 :UBC 0 : default:
2344 [ # # ]: 0 : ereport(ERROR,
2345 : : (errcode(ERRCODE_SYNTAX_ERROR),
2346 : : errmsg("invalid range bound flags"),
2347 : : errhint("Valid values are \"[]\", \"[)\", \"(]\", and \"()\".")));
2348 : : }
2349 : :
5108 heikki.linnakangas@i 2350 :CBC 2583 : return flags;
2351 : : }
2352 : :
2353 : : /*
2354 : : * Parse range input.
2355 : : *
2356 : : * Input parameters:
2357 : : * string: input string to be parsed
2358 : : * Output parameters:
2359 : : * *flags: receives flags bitmask
2360 : : * *lbound_str: receives palloc'd lower bound string, or NULL if none
2361 : : * *ubound_str: receives palloc'd upper bound string, or NULL if none
2362 : : *
2363 : : * This is modeled somewhat after record_in in rowtypes.c.
2364 : : * The input syntax is:
2365 : : * <range> := EMPTY
2366 : : * | <lb-inc> <string>, <string> <ub-inc>
2367 : : * <lb-inc> := '[' | '('
2368 : : * <ub-inc> := ']' | ')'
2369 : : *
2370 : : * Whitespace before or after <range> is ignored. Whitespace within a <string>
2371 : : * is taken literally and becomes part of the input string for that bound.
2372 : : *
2373 : : * A <string> of length zero is taken as "infinite" (i.e. no bound), unless it
2374 : : * is surrounded by double-quotes, in which case it is the literal empty
2375 : : * string.
2376 : : *
2377 : : * Within a <string>, special characters (such as comma, parenthesis, or
2378 : : * brackets) can be enclosed in double-quotes or escaped with backslash. Within
2379 : : * double-quotes, a double-quote can be escaped with double-quote or backslash.
2380 : : *
2381 : : * Returns true on success, false on failure (but failures will return only if
2382 : : * escontext is an ErrorSaveContext).
2383 : : */
2384 : : static bool
5089 tgl@sss.pgh.pa.us 2385 : 3658 : range_parse(const char *string, char *flags, char **lbound_str,
2386 : : char **ubound_str, Node *escontext)
2387 : : {
2388 : 3658 : const char *ptr = string;
2389 : : bool infinite;
2390 : :
5108 heikki.linnakangas@i 2391 : 3658 : *flags = 0;
2392 : :
2393 : : /* consume whitespace */
5096 tgl@sss.pgh.pa.us 2394 [ + + + + ]: 3670 : while (*ptr != '\0' && isspace((unsigned char) *ptr))
5108 heikki.linnakangas@i 2395 : 12 : ptr++;
2396 : :
2397 : : /* check for empty range */
2398 [ + + ]: 3658 : if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
2399 : : strlen(RANGE_EMPTY_LITERAL)) == 0)
2400 : : {
2401 : 294 : *flags = RANGE_EMPTY;
5089 tgl@sss.pgh.pa.us 2402 : 294 : *lbound_str = NULL;
2403 : 294 : *ubound_str = NULL;
2404 : :
5108 heikki.linnakangas@i 2405 : 294 : ptr += strlen(RANGE_EMPTY_LITERAL);
2406 : :
2407 : : /* the rest should be whitespace */
5096 tgl@sss.pgh.pa.us 2408 [ + + + - ]: 300 : while (*ptr != '\0' && isspace((unsigned char) *ptr))
5108 heikki.linnakangas@i 2409 : 6 : ptr++;
2410 : :
2411 : : /* should have consumed everything */
2412 [ - + ]: 294 : if (*ptr != '\0')
1048 tgl@sss.pgh.pa.us 2413 [ # # ]:UBC 0 : ereturn(escontext, false,
2414 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2415 : : errmsg("malformed range literal: \"%s\"",
2416 : : string),
2417 : : errdetail("Junk after \"empty\" key word.")));
2418 : :
1048 tgl@sss.pgh.pa.us 2419 :CBC 294 : return true;
2420 : : }
2421 : :
5089 2422 [ + + ]: 3364 : if (*ptr == '[')
2423 : : {
2424 : 3012 : *flags |= RANGE_LB_INC;
5108 heikki.linnakangas@i 2425 : 3012 : ptr++;
2426 : : }
5089 tgl@sss.pgh.pa.us 2427 [ + + ]: 352 : else if (*ptr == '(')
2428 : 340 : ptr++;
2429 : : else
1048 2430 [ + + ]: 12 : ereturn(escontext, false,
2431 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2432 : : errmsg("malformed range literal: \"%s\"",
2433 : : string),
2434 : : errdetail("Missing left parenthesis or bracket.")));
2435 : :
2436 : 3352 : ptr = range_parse_bound(string, ptr, lbound_str, &infinite, escontext);
2437 [ - + ]: 3349 : if (ptr == NULL)
1048 tgl@sss.pgh.pa.us 2438 :UBC 0 : return false;
5108 heikki.linnakangas@i 2439 [ + + ]:CBC 3349 : if (infinite)
2440 : 90 : *flags |= RANGE_LB_INF;
2441 : :
5089 tgl@sss.pgh.pa.us 2442 [ + + ]: 3349 : if (*ptr == ',')
2443 : 3337 : ptr++;
2444 : : else
1048 2445 [ + - ]: 12 : ereturn(escontext, false,
2446 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2447 : : errmsg("malformed range literal: \"%s\"",
2448 : : string),
2449 : : errdetail("Missing comma after lower bound.")));
2450 : :
2451 : 3337 : ptr = range_parse_bound(string, ptr, ubound_str, &infinite, escontext);
2452 [ + + ]: 3337 : if (ptr == NULL)
2453 : 6 : return false;
5089 2454 [ + + ]: 3331 : if (infinite)
2455 : 132 : *flags |= RANGE_UB_INF;
2456 : :
2457 [ + + ]: 3331 : if (*ptr == ']')
2458 : : {
2459 : 326 : *flags |= RANGE_UB_INC;
2460 : 326 : ptr++;
2461 : : }
2462 [ + + ]: 3005 : else if (*ptr == ')')
2463 : 2999 : ptr++;
2464 : : else /* must be a comma */
1048 2465 [ + - ]: 6 : ereturn(escontext, false,
2466 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2467 : : errmsg("malformed range literal: \"%s\"",
2468 : : string),
2469 : : errdetail("Too many commas.")));
2470 : :
2471 : : /* consume whitespace */
5096 2472 [ + + + + ]: 3340 : while (*ptr != '\0' && isspace((unsigned char) *ptr))
5108 heikki.linnakangas@i 2473 : 15 : ptr++;
2474 : :
2475 [ + + ]: 3325 : if (*ptr != '\0')
1048 tgl@sss.pgh.pa.us 2476 [ + - ]: 9 : ereturn(escontext, false,
2477 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2478 : : errmsg("malformed range literal: \"%s\"",
2479 : : string),
2480 : : errdetail("Junk after right parenthesis or bracket.")));
2481 : :
2482 : 3316 : return true;
2483 : : }
2484 : :
2485 : : /*
2486 : : * Helper for range_parse: parse and de-quote one bound string.
2487 : : *
2488 : : * We scan until finding comma, right parenthesis, or right bracket.
2489 : : *
2490 : : * Input parameters:
2491 : : * string: entire input string (used only for error reports)
2492 : : * ptr: where to start parsing bound
2493 : : * Output parameters:
2494 : : * *bound_str: receives palloc'd bound string, or NULL if none
2495 : : * *infinite: set true if no bound, else false
2496 : : *
2497 : : * The return value is the scan ptr, advanced past the bound string.
2498 : : * However, if escontext is an ErrorSaveContext, we return NULL on failure.
2499 : : */
2500 : : static const char *
5089 2501 : 6689 : range_parse_bound(const char *string, const char *ptr,
2502 : : char **bound_str, bool *infinite, Node *escontext)
2503 : : {
2504 : : StringInfoData buf;
2505 : :
2506 : : /* Check for null: completely empty input means null */
5108 heikki.linnakangas@i 2507 [ + + + + : 6689 : if (*ptr == ',' || *ptr == ')' || *ptr == ']')
+ + ]
2508 : : {
2509 : 222 : *bound_str = NULL;
5097 bruce@momjian.us 2510 : 222 : *infinite = true;
2511 : : }
2512 : : else
2513 : : {
2514 : : /* Extract string for this bound */
5108 heikki.linnakangas@i 2515 : 6467 : bool inquote = false;
2516 : :
2517 : 6467 : initStringInfo(&buf);
2518 [ + + + + : 21468 : while (inquote || !(*ptr == ',' || *ptr == ')' || *ptr == ']'))
+ + + + ]
2519 : : {
2520 : 15010 : char ch = *ptr++;
2521 : :
2522 [ + + ]: 15010 : if (ch == '\0')
1048 tgl@sss.pgh.pa.us 2523 [ + + ]: 9 : ereturn(escontext, NULL,
2524 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2525 : : errmsg("malformed range literal: \"%s\"",
2526 : : string),
2527 : : errdetail("Unexpected end of input.")));
5108 heikki.linnakangas@i 2528 [ + + ]: 15001 : if (ch == '\\')
2529 : : {
2530 [ - + ]: 21 : if (*ptr == '\0')
1048 tgl@sss.pgh.pa.us 2531 [ # # ]:UBC 0 : ereturn(escontext, NULL,
2532 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2533 : : errmsg("malformed range literal: \"%s\"",
2534 : : string),
2535 : : errdetail("Unexpected end of input.")));
5108 heikki.linnakangas@i 2536 :CBC 21 : appendStringInfoChar(&buf, *ptr++);
2537 : : }
3598 peter_e@gmx.net 2538 [ + + ]: 14980 : else if (ch == '"')
2539 : : {
5108 heikki.linnakangas@i 2540 [ + + ]: 200 : if (!inquote)
2541 : 100 : inquote = true;
3598 peter_e@gmx.net 2542 [ + + ]: 100 : else if (*ptr == '"')
2543 : : {
2544 : : /* doubled quote within quote sequence */
5108 heikki.linnakangas@i 2545 : 3 : appendStringInfoChar(&buf, *ptr++);
2546 : : }
2547 : : else
2548 : 97 : inquote = false;
2549 : : }
2550 : : else
2551 : 14780 : appendStringInfoChar(&buf, ch);
2552 : : }
2553 : :
2554 : 6458 : *bound_str = buf.data;
5097 bruce@momjian.us 2555 : 6458 : *infinite = false;
2556 : : }
2557 : :
5108 heikki.linnakangas@i 2558 : 6680 : return ptr;
2559 : : }
2560 : :
2561 : : /*
2562 : : * Convert a deserialized range value to text form
2563 : : *
2564 : : * Inputs are the flags byte, and the two bound values already converted to
2565 : : * text (but not yet quoted). If no bound value, pass NULL.
2566 : : *
2567 : : * Result is a palloc'd string
2568 : : */
2569 : : static char *
5089 tgl@sss.pgh.pa.us 2570 : 54128 : range_deparse(char flags, const char *lbound_str, const char *ubound_str)
2571 : : {
2572 : : StringInfoData buf;
2573 : :
5108 heikki.linnakangas@i 2574 [ + + ]: 54128 : if (flags & RANGE_EMPTY)
2575 : 8452 : return pstrdup(RANGE_EMPTY_LITERAL);
2576 : :
5096 tgl@sss.pgh.pa.us 2577 : 45676 : initStringInfo(&buf);
2578 : :
5089 2579 [ + + ]: 45676 : appendStringInfoChar(&buf, (flags & RANGE_LB_INC) ? '[' : '(');
2580 : :
5108 heikki.linnakangas@i 2581 [ + + ]: 45676 : if (RANGE_HAS_LBOUND(flags))
2582 : 44418 : appendStringInfoString(&buf, range_bound_escape(lbound_str));
2583 : :
5089 tgl@sss.pgh.pa.us 2584 : 45676 : appendStringInfoChar(&buf, ',');
2585 : :
5108 heikki.linnakangas@i 2586 [ + + ]: 45676 : if (RANGE_HAS_UBOUND(flags))
2587 : 44349 : appendStringInfoString(&buf, range_bound_escape(ubound_str));
2588 : :
5089 tgl@sss.pgh.pa.us 2589 [ + + ]: 45676 : appendStringInfoChar(&buf, (flags & RANGE_UB_INC) ? ']' : ')');
2590 : :
5108 heikki.linnakangas@i 2591 : 45676 : return buf.data;
2592 : : }
2593 : :
2594 : : /*
2595 : : * Helper for range_deparse: quote a bound value as needed
2596 : : *
2597 : : * Result is a palloc'd string
2598 : : */
2599 : : static char *
5089 tgl@sss.pgh.pa.us 2600 : 88767 : range_bound_escape(const char *value)
2601 : : {
2602 : : bool nq;
2603 : : const char *ptr;
2604 : : StringInfoData buf;
2605 : :
5108 heikki.linnakangas@i 2606 : 88767 : initStringInfo(&buf);
2607 : :
2608 : : /* Detect whether we need double quotes for this value */
2609 : 88767 : nq = (value[0] == '\0'); /* force quotes for empty string */
5089 tgl@sss.pgh.pa.us 2610 [ + + ]: 406857 : for (ptr = value; *ptr; ptr++)
2611 : : {
2612 : 318345 : char ch = *ptr;
2613 : :
5108 heikki.linnakangas@i 2614 [ + + + + : 318345 : if (ch == '"' || ch == '\\' ||
+ + ]
2615 [ + - + + ]: 318276 : ch == '(' || ch == ')' ||
2616 [ + - + + ]: 318264 : ch == '[' || ch == ']' ||
2617 : 318240 : ch == ',' ||
2618 [ + + ]: 318240 : isspace((unsigned char) ch))
2619 : : {
2620 : 255 : nq = true;
2621 : 255 : break;
2622 : : }
2623 : : }
2624 : :
2625 : : /* And emit the string */
2626 [ + + ]: 88767 : if (nq)
2627 : 267 : appendStringInfoChar(&buf, '"');
5089 tgl@sss.pgh.pa.us 2628 [ + + ]: 408954 : for (ptr = value; *ptr; ptr++)
2629 : : {
2630 : 320187 : char ch = *ptr;
2631 : :
5108 heikki.linnakangas@i 2632 [ + + + + ]: 320187 : if (ch == '"' || ch == '\\')
2633 : 60 : appendStringInfoChar(&buf, ch);
2634 : 320187 : appendStringInfoChar(&buf, ch);
2635 : : }
2636 [ + + ]: 88767 : if (nq)
2637 : 267 : appendStringInfoChar(&buf, '"');
2638 : :
2639 : 88767 : return buf.data;
2640 : : }
2641 : :
2642 : : /*
2643 : : * Test whether range r1 contains range r2.
2644 : : *
2645 : : * Caller has already checked that they are the same range type, and looked up
2646 : : * the necessary typcache entry.
2647 : : */
2648 : : bool
2189 peter@eisentraut.org 2649 : 241607 : range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
2650 : : {
2651 : : RangeBound lower1;
2652 : : RangeBound upper1;
2653 : : bool empty1;
2654 : : RangeBound lower2;
2655 : : RangeBound upper2;
2656 : : bool empty2;
2657 : :
2658 : : /* Different types should be prevented by ANYRANGE matching rules */
4850 heikki.linnakangas@i 2659 [ - + ]: 241607 : if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
4850 heikki.linnakangas@i 2660 [ # # ]:UBC 0 : elog(ERROR, "range types do not match");
2661 : :
5096 tgl@sss.pgh.pa.us 2662 :CBC 241607 : range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
2663 : 241607 : range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
2664 : :
2665 : : /* If either range is empty, the answer is easy */
5108 heikki.linnakangas@i 2666 [ + + ]: 241607 : if (empty2)
2667 : 157356 : return true;
2668 [ + + ]: 84251 : else if (empty1)
2669 : 6783 : return false;
2670 : :
2671 : : /* Else we must have lower1 <= lower2 and upper1 >= upper2 */
5096 tgl@sss.pgh.pa.us 2672 [ + + ]: 77468 : if (range_cmp_bounds(typcache, &lower1, &lower2) > 0)
5108 heikki.linnakangas@i 2673 : 37055 : return false;
5096 tgl@sss.pgh.pa.us 2674 [ + + ]: 40413 : if (range_cmp_bounds(typcache, &upper1, &upper2) < 0)
5108 heikki.linnakangas@i 2675 : 36284 : return false;
2676 : :
5089 tgl@sss.pgh.pa.us 2677 : 4129 : return true;
2678 : : }
2679 : :
2680 : : bool
2189 peter@eisentraut.org 2681 : 60986 : range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
2682 : : {
4850 heikki.linnakangas@i 2683 : 60986 : return range_contains_internal(typcache, r2, r1);
2684 : : }
2685 : :
2686 : : /*
2687 : : * Test whether range r contains a specific element value.
2688 : : */
2689 : : bool
2189 peter@eisentraut.org 2690 : 44486 : range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val)
2691 : : {
2692 : : RangeBound lower;
2693 : : RangeBound upper;
2694 : : bool empty;
2695 : : int32 cmp;
2696 : :
5089 tgl@sss.pgh.pa.us 2697 : 44486 : range_deserialize(typcache, r, &lower, &upper, &empty);
2698 : :
2699 [ + + ]: 44486 : if (empty)
2700 : 6432 : return false;
2701 : :
2702 [ + + ]: 38054 : if (!lower.infinite)
2703 : : {
2704 : 35927 : cmp = DatumGetInt32(FunctionCall2Coll(&typcache->rng_cmp_proc_finfo,
2705 : : typcache->rng_collation,
2706 : : lower.val, val));
2707 [ + + ]: 35927 : if (cmp > 0)
2708 : 34752 : return false;
2709 [ + + - + ]: 1175 : if (cmp == 0 && !lower.inclusive)
5089 tgl@sss.pgh.pa.us 2710 :UBC 0 : return false;
2711 : : }
2712 : :
5089 tgl@sss.pgh.pa.us 2713 [ + + ]:CBC 3302 : if (!upper.infinite)
2714 : : {
2715 : 3264 : cmp = DatumGetInt32(FunctionCall2Coll(&typcache->rng_cmp_proc_finfo,
2716 : : typcache->rng_collation,
2717 : : upper.val, val));
2718 [ + + ]: 3264 : if (cmp < 0)
2719 : 240 : return false;
2720 [ - + - - ]: 3024 : if (cmp == 0 && !upper.inclusive)
5089 tgl@sss.pgh.pa.us 2721 :UBC 0 : return false;
2722 : : }
2723 : :
5108 heikki.linnakangas@i 2724 :CBC 3062 : return true;
2725 : : }
2726 : :
2727 : :
2728 : : /*
2729 : : * datum_compute_size() and datum_write() are used to insert the bound
2730 : : * values into a range object. They are modeled after heaptuple.c's
2731 : : * heap_compute_data_size() and heap_fill_tuple(), but we need not handle
2732 : : * null values here. TYPE_IS_PACKABLE must test the same conditions as
2733 : : * heaptuple.c's ATT_IS_PACKABLE macro. See the comments there for more
2734 : : * details.
2735 : : */
2736 : :
2737 : : /* Does datatype allow packing into the 1-byte-header varlena format? */
2738 : : #define TYPE_IS_PACKABLE(typlen, typstorage) \
2739 : : ((typlen) == -1 && (typstorage) != TYPSTORAGE_PLAIN)
2740 : :
2741 : : /*
2742 : : * Increment data_length by the space needed by the datum, including any
2743 : : * preceding alignment padding.
2744 : : */
2745 : : static Size
2746 : 896671 : datum_compute_size(Size data_length, Datum val, bool typbyval, char typalign,
2747 : : int16 typlen, char typstorage)
2748 : : {
2749 [ + + + - : 901413 : if (TYPE_IS_PACKABLE(typlen, typstorage) &&
+ + ]
2750 [ + + + + ]: 4742 : VARATT_CAN_MAKE_SHORT(DatumGetPointer(val)))
2751 : : {
2752 : : /*
2753 : : * we're anticipating converting to a short varlena header, so adjust
2754 : : * length and don't count any alignment
2755 : : */
2756 : 4283 : data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val));
2757 : : }
2758 : : else
2759 : : {
2760 [ + + + + : 892388 : data_length = att_align_datum(data_length, typalign, typlen, val);
+ + + - +
- - - ]
2761 [ + + + - : 892388 : data_length = att_addlength_datum(data_length, typlen, val);
- - - - -
- - - + +
- - ]
2762 : : }
2763 : :
2764 : 896671 : return data_length;
2765 : : }
2766 : :
2767 : : /*
2768 : : * Write the given datum beginning at ptr (after advancing to correct
2769 : : * alignment, if needed). Return the pointer incremented by space used.
2770 : : */
2771 : : static Pointer
2772 : 896671 : datum_write(Pointer ptr, Datum datum, bool typbyval, char typalign,
2773 : : int16 typlen, char typstorage)
2774 : : {
2775 : : Size data_length;
2776 : :
2777 [ + + ]: 896671 : if (typbyval)
2778 : : {
2779 : : /* pass-by-value */
2780 [ + + + - : 891929 : ptr = (char *) att_align_nominal(ptr, typalign);
+ - - - ]
2781 : 891929 : store_att_byval(ptr, datum, typlen);
2782 : 891929 : data_length = typlen;
2783 : : }
2784 [ + - ]: 4742 : else if (typlen == -1)
2785 : : {
2786 : : /* varlena */
2787 : 4742 : Pointer val = DatumGetPointer(datum);
2788 : :
2789 [ - + ]: 4742 : if (VARATT_IS_EXTERNAL(val))
2790 : : {
2791 : : /*
2792 : : * Throw error, because we must never put a toast pointer inside a
2793 : : * range object. Caller should have detoasted it.
2794 : : */
5097 tgl@sss.pgh.pa.us 2795 [ # # ]:UBC 0 : elog(ERROR, "cannot store a toast pointer inside a range");
2796 : : data_length = 0; /* keep compiler quiet */
2797 : : }
5108 heikki.linnakangas@i 2798 [ + + ]:CBC 4742 : else if (VARATT_IS_SHORT(val))
2799 : : {
2800 : : /* no alignment for short varlenas */
2801 : 441 : data_length = VARSIZE_SHORT(val);
2802 : 441 : memcpy(ptr, val, data_length);
2803 : : }
2804 [ + - + - : 8602 : else if (TYPE_IS_PACKABLE(typlen, typstorage) &&
+ + ]
2805 [ + - + + ]: 4301 : VARATT_CAN_MAKE_SHORT(val))
2806 : : {
2807 : : /* convert to short varlena -- no alignment */
2808 : 4283 : data_length = VARATT_CONVERTED_SHORT_SIZE(val);
2809 : 4283 : SET_VARSIZE_SHORT(ptr, data_length);
2810 : 4283 : memcpy(ptr + 1, VARDATA(val), data_length - 1);
2811 : : }
2812 : : else
2813 : : {
2814 : : /* full 4-byte header varlena */
2815 [ + - - - : 18 : ptr = (char *) att_align_nominal(ptr, typalign);
- - - - ]
2816 : 18 : data_length = VARSIZE(val);
2817 : 18 : memcpy(ptr, val, data_length);
2818 : : }
2819 : : }
5108 heikki.linnakangas@i 2820 [ # # ]:UBC 0 : else if (typlen == -2)
2821 : : {
2822 : : /* cstring ... never needs alignment */
2064 tgl@sss.pgh.pa.us 2823 [ # # ]: 0 : Assert(typalign == TYPALIGN_CHAR);
5108 heikki.linnakangas@i 2824 : 0 : data_length = strlen(DatumGetCString(datum)) + 1;
2825 : 0 : memcpy(ptr, DatumGetPointer(datum), data_length);
2826 : : }
2827 : : else
2828 : : {
2829 : : /* fixed-length pass-by-reference */
2830 [ # # # # : 0 : ptr = (char *) att_align_nominal(ptr, typalign);
# # # # ]
2831 [ # # ]: 0 : Assert(typlen > 0);
2832 : 0 : data_length = typlen;
2833 : 0 : memcpy(ptr, DatumGetPointer(datum), data_length);
2834 : : }
2835 : :
5108 heikki.linnakangas@i 2836 :CBC 896671 : ptr += data_length;
2837 : :
2838 : 896671 : return ptr;
2839 : : }
2840 : :
2841 : : /*
2842 : : * Common code for the elem_contained_by_range and range_contains_elem
2843 : : * support functions. The caller has extracted the function argument
2844 : : * expressions, and swapped them if necessary to pass the range first.
2845 : : *
2846 : : * Returns a simplified replacement expression, or NULL if we can't simplify.
2847 : : */
2848 : : static Node *
647 tgl@sss.pgh.pa.us 2849 : 126 : find_simplified_clause(PlannerInfo *root, Expr *rangeExpr, Expr *elemExpr)
2850 : : {
2851 : : RangeType *range;
2852 : : TypeCacheEntry *rangetypcache;
2853 : : RangeBound lower;
2854 : : RangeBound upper;
2855 : : bool empty;
2856 : :
2857 : : /* can't do anything unless the range is a non-null constant */
2858 [ + + - + ]: 126 : if (!IsA(rangeExpr, Const) || ((Const *) rangeExpr)->constisnull)
2859 : 78 : return NULL;
2860 : 48 : range = DatumGetRangeTypeP(((Const *) rangeExpr)->constvalue);
2861 : :
2862 : 48 : rangetypcache = lookup_type_cache(RangeTypeGetOid(range),
2863 : : TYPECACHE_RANGE_INFO);
2864 [ - + ]: 48 : if (rangetypcache->rngelemtype == NULL)
647 tgl@sss.pgh.pa.us 2865 [ # # ]:UBC 0 : elog(ERROR, "type %u is not a range type", RangeTypeGetOid(range));
2866 : :
647 tgl@sss.pgh.pa.us 2867 :CBC 48 : range_deserialize(rangetypcache, range, &lower, &upper, &empty);
2868 : :
2869 [ + + ]: 48 : if (empty)
2870 : : {
2871 : : /* if the range is empty, then there can be no matches */
2872 : 3 : return makeBoolConst(false, false);
2873 : : }
2874 [ + + + + ]: 45 : else if (lower.infinite && upper.infinite)
2875 : : {
2876 : : /* the range has infinite bounds, so it matches everything */
2877 : 3 : return makeBoolConst(true, false);
2878 : : }
2879 : : else
2880 : : {
2881 : : /* at least one bound is available, we have something to work with */
2882 : 42 : TypeCacheEntry *elemTypcache = rangetypcache->rngelemtype;
2883 : 42 : Oid opfamily = rangetypcache->rng_opfamily;
2884 : 42 : Oid rng_collation = rangetypcache->rng_collation;
2885 : 42 : Expr *lowerExpr = NULL;
2886 : 42 : Expr *upperExpr = NULL;
2887 : :
2888 [ + + + + ]: 42 : if (!lower.infinite && !upper.infinite)
2889 : : {
2890 : : /*
2891 : : * When both bounds are present, we have a problem: the
2892 : : * "simplified" clause would need to evaluate the elemExpr twice.
2893 : : * That's definitely not okay if the elemExpr is volatile, and
2894 : : * it's also unattractive if the elemExpr is expensive.
2895 : : */
2896 : : QualCost eval_cost;
2897 : :
2898 [ + + ]: 33 : if (contain_volatile_functions((Node *) elemExpr))
2899 : 3 : return NULL;
2900 : :
2901 : : /*
2902 : : * We define "expensive" as "contains any subplan or more than 10
2903 : : * operators". Note that the subplan search has to be done
2904 : : * explicitly, since cost_qual_eval() will barf on unplanned
2905 : : * subselects.
2906 : : */
2907 [ - + ]: 30 : if (contain_subplans((Node *) elemExpr))
647 tgl@sss.pgh.pa.us 2908 :UBC 0 : return NULL;
647 tgl@sss.pgh.pa.us 2909 :CBC 30 : cost_qual_eval_node(&eval_cost, (Node *) elemExpr, root);
2910 : 30 : if (eval_cost.startup + eval_cost.per_tuple >
2911 [ - + ]: 30 : 10 * cpu_operator_cost)
647 tgl@sss.pgh.pa.us 2912 :UBC 0 : return NULL;
2913 : : }
2914 : :
2915 : : /* Okay, try to build boundary comparison expressions */
647 tgl@sss.pgh.pa.us 2916 [ + + ]:CBC 39 : if (!lower.infinite)
2917 : : {
2918 : 36 : lowerExpr = build_bound_expr(elemExpr,
2919 : : lower.val,
2920 : : true,
2921 : 36 : lower.inclusive,
2922 : : elemTypcache,
2923 : : opfamily,
2924 : : rng_collation);
2925 [ - + ]: 36 : if (lowerExpr == NULL)
647 tgl@sss.pgh.pa.us 2926 :UBC 0 : return NULL;
2927 : : }
2928 : :
647 tgl@sss.pgh.pa.us 2929 [ + + ]:CBC 39 : if (!upper.infinite)
2930 : : {
2931 : : /* Copy the elemExpr if we need two copies */
2932 [ + + ]: 33 : if (!lower.infinite)
2933 : 30 : elemExpr = copyObject(elemExpr);
2934 : 33 : upperExpr = build_bound_expr(elemExpr,
2935 : : upper.val,
2936 : : false,
2937 : 33 : upper.inclusive,
2938 : : elemTypcache,
2939 : : opfamily,
2940 : : rng_collation);
2941 [ - + ]: 33 : if (upperExpr == NULL)
647 tgl@sss.pgh.pa.us 2942 :UBC 0 : return NULL;
2943 : : }
2944 : :
647 tgl@sss.pgh.pa.us 2945 [ + + + + ]:CBC 39 : if (lowerExpr != NULL && upperExpr != NULL)
2946 : 30 : return (Node *) make_andclause(list_make2(lowerExpr, upperExpr));
2947 [ + + ]: 9 : else if (lowerExpr != NULL)
2948 : 6 : return (Node *) lowerExpr;
2949 [ + - ]: 3 : else if (upperExpr != NULL)
2950 : 3 : return (Node *) upperExpr;
2951 : : else
2952 : : {
647 tgl@sss.pgh.pa.us 2953 :UBC 0 : Assert(false);
2954 : : return NULL;
2955 : : }
2956 : : }
2957 : : }
2958 : :
2959 : : /*
2960 : : * Helper function for find_simplified_clause().
2961 : : *
2962 : : * Build the expression (elemExpr Operator val), where the operator is
2963 : : * the appropriate member of the given opfamily depending on
2964 : : * isLowerBound and isInclusive. typeCache is the typcache entry for
2965 : : * the "val" value (presently, this will be the same type as elemExpr).
2966 : : * rng_collation is the collation to use in the comparison.
2967 : : *
2968 : : * Return NULL on failure (if, for some reason, we can't find the operator).
2969 : : */
2970 : : static Expr *
647 tgl@sss.pgh.pa.us 2971 :CBC 69 : build_bound_expr(Expr *elemExpr, Datum val,
2972 : : bool isLowerBound, bool isInclusive,
2973 : : TypeCacheEntry *typeCache,
2974 : : Oid opfamily, Oid rng_collation)
2975 : : {
2976 : 69 : Oid elemType = typeCache->type_id;
2977 : 69 : int16 elemTypeLen = typeCache->typlen;
2978 : 69 : bool elemByValue = typeCache->typbyval;
2979 : 69 : Oid elemCollation = typeCache->typcollation;
2980 : : int16 strategy;
2981 : : Oid oproid;
2982 : : Expr *constExpr;
2983 : :
2984 : : /* Identify the comparison operator to use */
2985 [ + + ]: 69 : if (isLowerBound)
2986 [ + + ]: 36 : strategy = isInclusive ? BTGreaterEqualStrategyNumber : BTGreaterStrategyNumber;
2987 : : else
2988 [ + + ]: 33 : strategy = isInclusive ? BTLessEqualStrategyNumber : BTLessStrategyNumber;
2989 : :
2990 : : /*
2991 : : * We could use exprType(elemExpr) here, if it ever becomes possible that
2992 : : * elemExpr is not the exact same type as the range elements.
2993 : : */
2994 : 69 : oproid = get_opfamily_member(opfamily, elemType, elemType, strategy);
2995 : :
2996 : : /* We don't really expect failure here, but just in case ... */
2997 [ - + ]: 69 : if (!OidIsValid(oproid))
647 tgl@sss.pgh.pa.us 2998 :UBC 0 : return NULL;
2999 : :
3000 : : /* OK, convert "val" to a full-fledged Const node, and make the OpExpr */
647 tgl@sss.pgh.pa.us 3001 :CBC 69 : constExpr = (Expr *) makeConst(elemType,
3002 : : -1,
3003 : : elemCollation,
3004 : : elemTypeLen,
3005 : : val,
3006 : : false,
3007 : : elemByValue);
3008 : :
3009 : 69 : return make_opclause(oproid,
3010 : : BOOLOID,
3011 : : false,
3012 : : elemExpr,
3013 : : constExpr,
3014 : : InvalidOid,
3015 : : rng_collation);
3016 : : }
|