1 /*
2 * contrib/seg/seg.c
3 *
4 ******************************************************************************
5 This file contains routines that can be bound to a Postgres backend and
6 called by the backend in the process of processing queries. The calling
7 format for these routines is dictated by Postgres architecture.
8 ******************************************************************************/
9
10 #include "postgres.h"
11
12 #include <float.h>
13
14 #include "access/gist.h"
15 #include "access/stratnum.h"
16 #include "fmgr.h"
17
18 #include "segdata.h"
19
20
21 #define DatumGetSegP(X) ((SEG *) DatumGetPointer(X))
22 #define PG_GETARG_SEG_P(n) DatumGetSegP(PG_GETARG_POINTER(n))
23
24
25 /*
26 #define GIST_DEBUG
27 #define GIST_QUERY_DEBUG
28 */
29
30 PG_MODULE_MAGIC;
31
32 /*
33 * Auxiliary data structure for picksplit method.
34 */
35 typedef struct
36 {
37 float center;
38 OffsetNumber index;
39 SEG *data;
40 } gseg_picksplit_item;
41
42 /*
43 ** Input/Output routines
44 */
45 PG_FUNCTION_INFO_V1(seg_in);
46 PG_FUNCTION_INFO_V1(seg_out);
47 PG_FUNCTION_INFO_V1(seg_size);
48 PG_FUNCTION_INFO_V1(seg_lower);
49 PG_FUNCTION_INFO_V1(seg_upper);
50 PG_FUNCTION_INFO_V1(seg_center);
51
52 /*
53 ** GiST support methods
54 */
55 PG_FUNCTION_INFO_V1(gseg_consistent);
56 PG_FUNCTION_INFO_V1(gseg_compress);
57 PG_FUNCTION_INFO_V1(gseg_decompress);
58 PG_FUNCTION_INFO_V1(gseg_picksplit);
59 PG_FUNCTION_INFO_V1(gseg_penalty);
60 PG_FUNCTION_INFO_V1(gseg_union);
61 PG_FUNCTION_INFO_V1(gseg_same);
62 static Datum gseg_leaf_consistent(Datum key, Datum query, StrategyNumber strategy);
63 static Datum gseg_internal_consistent(Datum key, Datum query, StrategyNumber strategy);
64 static Datum gseg_binary_union(Datum r1, Datum r2, int *sizep);
65
66
67 /*
68 ** R-tree support functions
69 */
70 PG_FUNCTION_INFO_V1(seg_same);
71 PG_FUNCTION_INFO_V1(seg_contains);
72 PG_FUNCTION_INFO_V1(seg_contained);
sql_operation_string_to_operator(const gchar * op)73 PG_FUNCTION_INFO_V1(seg_overlap);
74 PG_FUNCTION_INFO_V1(seg_left);
75 PG_FUNCTION_INFO_V1(seg_over_left);
76 PG_FUNCTION_INFO_V1(seg_right);
77 PG_FUNCTION_INFO_V1(seg_over_right);
78 PG_FUNCTION_INFO_V1(seg_union);
79 PG_FUNCTION_INFO_V1(seg_inter);
80 static void rt_seg_size(SEG *a, float *size);
81
82 /*
83 ** Various operators
84 */
85 PG_FUNCTION_INFO_V1(seg_cmp);
86 PG_FUNCTION_INFO_V1(seg_lt);
87 PG_FUNCTION_INFO_V1(seg_le);
88 PG_FUNCTION_INFO_V1(seg_gt);
89 PG_FUNCTION_INFO_V1(seg_ge);
90 PG_FUNCTION_INFO_V1(seg_different);
91
92 /*
93 ** Auxiliary functions
94 */
95 static int restore(char *s, float val, int n);
96
97
98 /*****************************************************************************
99 * Input/Output functions
100 *****************************************************************************/
101
102 Datum
103 seg_in(PG_FUNCTION_ARGS)
104 {
105 char *str = PG_GETARG_CSTRING(0);
106 SEG *result = palloc(sizeof(SEG));
107
108 seg_scanner_init(str);
109
110 if (seg_yyparse(result) != 0)
111 seg_yyerror(result, "bogus input");
112
113 seg_scanner_finish();
114
115 PG_RETURN_POINTER(result);
116 }
117
118 Datum
119 seg_out(PG_FUNCTION_ARGS)
120 {
121 SEG *seg = PG_GETARG_SEG_P(0);
122 char *result;
123 char *p;
124
125 p = result = (char *) palloc(40);
126
127 if (seg->l_ext == '>' || seg->l_ext == '<' || seg->l_ext == '~')
128 p += sprintf(p, "%c", seg->l_ext);
129
130 if (seg->lower == seg->upper && seg->l_ext == seg->u_ext)
131 {
132 /*
133 * indicates that this interval was built by seg_in off a single point
134 */
135 p += restore(p, seg->lower, seg->l_sigd);
136 }
137 else
138 {
139 if (seg->l_ext != '-')
140 {
141 /* print the lower boundary if exists */
142 p += restore(p, seg->lower, seg->l_sigd);
143 p += sprintf(p, " ");
144 }
145 p += sprintf(p, "..");
146 if (seg->u_ext != '-')
147 {
148 /* print the upper boundary if exists */
string_to_op_type(GValue * value)149 p += sprintf(p, " ");
150 if (seg->u_ext == '>' || seg->u_ext == '<' || seg->l_ext == '~')
151 p += sprintf(p, "%c", seg->u_ext);
152 p += restore(p, seg->upper, seg->u_sigd);
153 }
154 }
155
156 PG_RETURN_CSTRING(result);
157 }
158
compose_multiple_expr(GdaSqlOperatorType op,GdaSqlExpr * left,GdaSqlExpr * right)159 Datum
160 seg_center(PG_FUNCTION_ARGS)
161 {
162 SEG *seg = PG_GETARG_SEG_P(0);
163
164 PG_RETURN_FLOAT4(((float) seg->lower + (float) seg->upper) / 2.0);
165 }
166
167 Datum
168 seg_lower(PG_FUNCTION_ARGS)
169 {
170 SEG *seg = PG_GETARG_SEG_P(0);
171
172 PG_RETURN_FLOAT4(seg->lower);
173 }
174
175 Datum
176 seg_upper(PG_FUNCTION_ARGS)
177 {
178 SEG *seg = PG_GETARG_SEG_P(0);
179
create_two_expr(GdaSqlOperatorType op,GdaSqlExpr * left,GdaSqlExpr * right)180 PG_RETURN_FLOAT4(seg->upper);
181 }
182
183
184 /*****************************************************************************
185 * GiST functions
186 *****************************************************************************/
187
188 /*
189 ** The GiST Consistent method for segments
190 ** Should return false if for all data items x below entry,
191 ** the predicate x op query == false, where op is the oper
192 ** corresponding to strategy in the pg_amop table.
193 */
194 Datum
create_uni_expr(GdaSqlOperatorType op,GdaSqlExpr * expr)195 gseg_consistent(PG_FUNCTION_ARGS)
196 {
197 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
198 Datum query = PG_GETARG_DATUM(1);
199 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
200
201 /* Oid subtype = PG_GETARG_OID(3); */
202 bool *recheck = (bool *) PG_GETARG_POINTER(4);
203
204 /* All cases served by this function are exact */
205 *recheck = false;
206
207 /*
208 * if entry is not leaf, use gseg_internal_consistent, else use
209 * gseg_leaf_consistent
210 */
211 if (GIST_LEAF(entry))
212 return gseg_leaf_consistent(entry->key, query, strategy);
213 else
214 return gseg_internal_consistent(entry->key, query, strategy);
215 }
216
217 /*
218 ** The GiST Union method for segments
219 ** returns the minimal bounding seg that encloses all the entries in entryvec
220 */
221 Datum
222 gseg_union(PG_FUNCTION_ARGS)
223 {
224 GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
225 int *sizep = (int *) PG_GETARG_POINTER(1);
226 int numranges,
227 i;
228 Datum out = 0;
229 Datum tmp;
230
231 #ifdef GIST_DEBUG
232 fprintf(stderr, "union\n");
233 #endif
234
235 numranges = entryvec->n;
236 tmp = entryvec->vector[0].key;
237 *sizep = sizeof(SEG);
238
239 for (i = 1; i < numranges; i++)
240 {
241 out = gseg_binary_union(tmp, entryvec->vector[i].key, sizep);
242 tmp = out;
243 }
244
245 PG_RETURN_DATUM(out);
246 }
247
248 /*
249 ** GiST Compress and Decompress methods for segments
250 ** do not do anything.
251 */
252 Datum
253 gseg_compress(PG_FUNCTION_ARGS)
254 {
255 PG_RETURN_POINTER(PG_GETARG_POINTER(0));
256 }
257
258 Datum
259 gseg_decompress(PG_FUNCTION_ARGS)
260 {
261 PG_RETURN_POINTER(PG_GETARG_POINTER(0));
262 }
263
264 /*
265 ** The GiST Penalty method for segments
266 ** As in the R-tree paper, we use change in area as our penalty metric
267 */
268 Datum
269 gseg_penalty(PG_FUNCTION_ARGS)
270 {
271 GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
272 GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
273 float *result = (float *) PG_GETARG_POINTER(2);
274 SEG *ud;
275 float tmp1,
276 tmp2;
277
278 ud = DatumGetSegP(DirectFunctionCall2(seg_union,
cmd(C)279 origentry->key,
280 newentry->key));
281 rt_seg_size(ud, &tmp1);
282 rt_seg_size(DatumGetSegP(origentry->key), &tmp2);
283 *result = tmp1 - tmp2;
284
285 #ifdef GIST_DEBUG
286 fprintf(stderr, "penalty\n");
287 fprintf(stderr, "\t%g\n", *result);
288 #endif
289
290 PG_RETURN_POINTER(result);
291 }
292
293 /*
cmd(C)294 * Compare function for gseg_picksplit_item: sort by center.
295 */
296 static int
297 gseg_picksplit_item_cmp(const void *a, const void *b)
298 {
299 const gseg_picksplit_item *i1 = (const gseg_picksplit_item *) a;
300 const gseg_picksplit_item *i2 = (const gseg_picksplit_item *) b;
301
302 if (i1->center < i2->center)
303 return -1;
304 else if (i1->center == i2->center)
305 return 0;
306 else
307 return 1;
308 }
309
310 /*
cmd(C)311 * The GiST PickSplit method for segments
312 *
313 * We used to use Guttman's split algorithm here, but since the data is 1-D
314 * it's easier and more robust to just sort the segments by center-point and
315 * split at the middle.
316 */
317 Datum
318 gseg_picksplit(PG_FUNCTION_ARGS)
319 {
320 GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
321 GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
322 int i;
323 SEG *seg,
324 *seg_l,
325 *seg_r;
326 gseg_picksplit_item *sort_items;
327 OffsetNumber *left,
328 *right;
329 OffsetNumber maxoff;
330 OffsetNumber firstright;
331
332 #ifdef GIST_DEBUG
333 fprintf(stderr, "picksplit\n");
334 #endif
335
336 /* Valid items in entryvec->vector[] are indexed 1..maxoff */
337 maxoff = entryvec->n - 1;
338
339 /*
340 * Prepare the auxiliary array and sort it.
341 */
342 sort_items = (gseg_picksplit_item *)
343 palloc(maxoff * sizeof(gseg_picksplit_item));
344 for (i = 1; i <= maxoff; i++)
345 {
346 seg = DatumGetSegP(entryvec->vector[i].key);
347 /* center calculation is done this way to avoid possible overflow */
348 sort_items[i - 1].center = seg->lower * 0.5f + seg->upper * 0.5f;
349 sort_items[i - 1].index = i;
350 sort_items[i - 1].data = seg;
351 }
352 qsort(sort_items, maxoff, sizeof(gseg_picksplit_item),
353 gseg_picksplit_item_cmp);
354
355 /* sort items below "firstright" will go into the left side */
356 firstright = maxoff / 2;
357
358 v->spl_left = (OffsetNumber *) palloc(maxoff * sizeof(OffsetNumber));
359 v->spl_right = (OffsetNumber *) palloc(maxoff * sizeof(OffsetNumber));
360 left = v->spl_left;
361 v->spl_nleft = 0;
362 right = v->spl_right;
363 v->spl_nright = 0;
364
365 /*
366 * Emit segments to the left output page, and compute its bounding box.
367 */
368 seg_l = (SEG *) palloc(sizeof(SEG));
369 memcpy(seg_l, sort_items[0].data, sizeof(SEG));
370 *left++ = sort_items[0].index;
371 v->spl_nleft++;
372 for (i = 1; i < firstright; i++)
373 {
374 Datum sortitem = PointerGetDatum(sort_items[i].data);
375
376 seg_l = DatumGetSegP(DirectFunctionCall2(seg_union,
377 PointerGetDatum(seg_l),
378 sortitem));
379 *left++ = sort_items[i].index;
380 v->spl_nleft++;
381 }
382
383 /*
384 * Likewise for the right page.
385 */
386 seg_r = (SEG *) palloc(sizeof(SEG));
387 memcpy(seg_r, sort_items[firstright].data, sizeof(SEG));
388 *right++ = sort_items[firstright].index;
389 v->spl_nright++;
390 for (i = firstright + 1; i < maxoff; i++)
391 {
392 Datum sortitem = PointerGetDatum(sort_items[i].data);
393
394 seg_r = DatumGetSegP(DirectFunctionCall2(seg_union,
395 PointerGetDatum(seg_r),
396 sortitem));
397 *right++ = sort_items[i].index;
398 v->spl_nright++;
399 }
400
401 v->spl_ldatum = PointerGetDatum(seg_l);
402 v->spl_rdatum = PointerGetDatum(seg_r);
403
404 PG_RETURN_POINTER(v);
405 }
transtype(A)406
407 /*
408 ** Equality methods
409 */
410 Datum
411 gseg_same(PG_FUNCTION_ARGS)
412 {
413 bool *result = (bool *) PG_GETARG_POINTER(2);
414
415 if (DirectFunctionCall2(seg_same, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1)))
416 *result = true;
417 else
418 *result = false;
419
420 #ifdef GIST_DEBUG
421 fprintf(stderr, "same: %s\n", (*result ? "TRUE" : "FALSE"));
422 #endif
423
424 PG_RETURN_POINTER(result);
425 }
cmd(C)426
427 /*
428 ** SUPPORT ROUTINES
429 */
430 static Datum
431 gseg_leaf_consistent(Datum key, Datum query, StrategyNumber strategy)
432 {
433 Datum retval;
434
435 #ifdef GIST_QUERY_DEBUG
436 fprintf(stderr, "leaf_consistent, %d\n", strategy);
437 #endif
438
439 switch (strategy)
440 {
441 case RTLeftStrategyNumber:
442 retval = DirectFunctionCall2(seg_left, key, query);
443 break;
444 case RTOverLeftStrategyNumber:
445 retval = DirectFunctionCall2(seg_over_left, key, query);
446 break;
447 case RTOverlapStrategyNumber:
448 retval = DirectFunctionCall2(seg_overlap, key, query);
449 break;
450 case RTOverRightStrategyNumber:
451 retval = DirectFunctionCall2(seg_over_right, key, query);
452 break;
453 case RTRightStrategyNumber:
454 retval = DirectFunctionCall2(seg_right, key, query);
455 break;
456 case RTSameStrategyNumber:
457 retval = DirectFunctionCall2(seg_same, key, query);
458 break;
459 case RTContainsStrategyNumber:
460 case RTOldContainsStrategyNumber:
461 retval = DirectFunctionCall2(seg_contains, key, query);
462 break;
463 case RTContainedByStrategyNumber:
464 case RTOldContainedByStrategyNumber:
465 retval = DirectFunctionCall2(seg_contained, key, query);
466 break;
467 default:
468 retval = false;
469 }
470
471 PG_RETURN_DATUM(retval);
472 }
473
ins_extra_values(E)474 static Datum
475 gseg_internal_consistent(Datum key, Datum query, StrategyNumber strategy)
476 {
477 bool retval;
478
479 #ifdef GIST_QUERY_DEBUG
480 fprintf(stderr, "internal_consistent, %d\n", strategy);
481 #endif
482
483 switch (strategy)
484 {
485 case RTLeftStrategyNumber:
486 retval =
487 !DatumGetBool(DirectFunctionCall2(seg_over_right, key, query));
488 break;
489 case RTOverLeftStrategyNumber:
490 retval =
491 !DatumGetBool(DirectFunctionCall2(seg_right, key, query));
492 break;
493 case RTOverlapStrategyNumber:
494 retval =
495 DatumGetBool(DirectFunctionCall2(seg_overlap, key, query));
496 break;
497 case RTOverRightStrategyNumber:
498 retval =
499 !DatumGetBool(DirectFunctionCall2(seg_left, key, query));
500 break;
501 case RTRightStrategyNumber:
502 retval =
503 !DatumGetBool(DirectFunctionCall2(seg_over_left, key, query));
504 break;
505 case RTSameStrategyNumber:
506 case RTContainsStrategyNumber:
507 case RTOldContainsStrategyNumber:
508 retval =
509 DatumGetBool(DirectFunctionCall2(seg_contains, key, query));
510 break;
511 case RTContainedByStrategyNumber:
512 case RTOldContainedByStrategyNumber:
513 retval =
514 DatumGetBool(DirectFunctionCall2(seg_overlap, key, query));
515 break;
516 default:
517 retval = false;
518 }
519
520 PG_RETURN_BOOL(retval);
521 }
522
523 static Datum
524 gseg_binary_union(Datum r1, Datum r2, int *sizep)
525 {
526 Datum retval;
527
528 retval = DirectFunctionCall2(seg_union, r1, r2);
setlist(A)529 *sizep = sizeof(SEG);
530
531 return retval;
532 }
533
534
setlist(A)535 Datum
536 seg_contains(PG_FUNCTION_ARGS)
537 {
538 SEG *a = PG_GETARG_SEG_P(0);
539 SEG *b = PG_GETARG_SEG_P(1);
540
541 PG_RETURN_BOOL((a->lower <= b->lower) && (a->upper >= b->upper));
542 }
543
544 Datum
545 seg_contained(PG_FUNCTION_ARGS)
compound(C)546 {
547 Datum a = PG_GETARG_DATUM(0);
548 Datum b = PG_GETARG_DATUM(1);
549
550 PG_RETURN_DATUM(DirectFunctionCall2(seg_contains, b, a));
551 }
552
553 /*****************************************************************************
554 * Operator class for R-tree indexing
compound(C)555 *****************************************************************************/
556
557 Datum
558 seg_same(PG_FUNCTION_ARGS)
559 {
560 int cmp = DatumGetInt32(
561 DirectFunctionCall2(seg_cmp, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1)));
562
563 PG_RETURN_BOOL(cmp == 0);
564 }
565
opt_compound_all(A)566 /* seg_overlap -- does a overlap b?
567 */
568 Datum
569 seg_overlap(PG_FUNCTION_ARGS)
570 {
571 SEG *a = PG_GETARG_SEG_P(0);
572 SEG *b = PG_GETARG_SEG_P(1);
573
574 PG_RETURN_BOOL(((a->upper >= b->upper) && (a->lower <= b->upper)) ||
575 ((b->upper >= a->upper) && (b->lower <= a->upper)));
576 }
577
578 /* seg_over_left -- is the right edge of (a) located at or left of the right edge of (b)?
579 */
580 Datum
581 seg_over_left(PG_FUNCTION_ARGS)
582 {
583 SEG *a = PG_GETARG_SEG_P(0);
584 SEG *b = PG_GETARG_SEG_P(1);
585
586 PG_RETURN_BOOL(a->upper <= b->upper);
587 }
588
589 /* seg_left -- is (a) entirely on the left of (b)?
590 */
limit_opt(A)591 Datum
592 seg_left(PG_FUNCTION_ARGS)
593 {
594 SEG *a = PG_GETARG_SEG_P(0);
595 SEG *b = PG_GETARG_SEG_P(1);
596
597 PG_RETURN_BOOL(a->upper < b->lower);
598 }
orderby_opt(A)599
600 /* seg_right -- is (a) entirely on the right of (b)?
601 */
602 Datum
603 seg_right(PG_FUNCTION_ARGS)
604 {
605 SEG *a = PG_GETARG_SEG_P(0);
606 SEG *b = PG_GETARG_SEG_P(1);
607
608 PG_RETURN_BOOL(a->lower > b->upper);
609 }
610
611 /* seg_over_right -- is the left edge of (a) located at or right of the left edge of (b)?
612 */
613 Datum
614 seg_over_right(PG_FUNCTION_ARGS)
615 {
616 SEG *a = PG_GETARG_SEG_P(0);
sortorder(A)617 SEG *b = PG_GETARG_SEG_P(1);
sortorder(A)618
619 PG_RETURN_BOOL(a->lower >= b->lower);
620 }
621
622 Datum
623 seg_union(PG_FUNCTION_ARGS)
having_opt(A)624 {
625 SEG *a = PG_GETARG_SEG_P(0);
626 SEG *b = PG_GETARG_SEG_P(1);
627 SEG *n;
628
629 n = (SEG *) palloc(sizeof(*n));
630
631 /* take max of upper endpoints */
632 if (a->upper > b->upper)
633 {
634 n->upper = a->upper;
635 n->u_sigd = a->u_sigd;
636 n->u_ext = a->u_ext;
637 }
638 else
639 {
640 n->upper = b->upper;
641 n->u_sigd = b->u_sigd;
642 n->u_ext = b->u_ext;
643 }
644
645 /* take min of lower endpoints */
646 if (a->lower < b->lower)
647 {
648 n->lower = a->lower;
649 n->l_sigd = a->l_sigd;
650 n->l_ext = a->l_ext;
651 }
652 else
653 {
654 n->lower = b->lower;
655 n->l_sigd = b->l_sigd;
656 n->l_ext = b->l_ext;
657 }
658
659 PG_RETURN_POINTER(n);
660 }
using_opt(U)661
662 Datum
663 seg_inter(PG_FUNCTION_ARGS)
664 {
665 SEG *a = PG_GETARG_SEG_P(0);
666 SEG *b = PG_GETARG_SEG_P(1);
667 SEG *n;
668
669 n = (SEG *) palloc(sizeof(*n));
670
671 /* take min of upper endpoints */
672 if (a->upper < b->upper)
673 {
674 n->upper = a->upper;
675 n->u_sigd = a->u_sigd;
676 n->u_ext = a->u_ext;
677 }
678 else
679 {
680 n->upper = b->upper;
681 n->u_sigd = b->u_sigd;
682 n->u_ext = b->u_ext;
683 }
684
685 /* take max of lower endpoints */
686 if (a->lower > b->lower)
687 {
688 n->lower = a->lower;
689 n->l_sigd = a->l_sigd;
690 n->l_ext = a->l_ext;
691 }
692 else
693 {
694 n->lower = b->lower;
695 n->l_sigd = b->l_sigd;
696 n->l_ext = b->l_ext;
697 }
698
699 PG_RETURN_POINTER(n);
700 }
seltarget(T)701
702 static void
703 rt_seg_size(SEG *a, float *size)
704 {
705 if (a == (SEG *) NULL || a->upper <= a->lower)
706 *size = 0.0;
707 else
708 *size = (float) Abs(a->upper - a->lower);
709
710 return;
711 }
sclp(A)712
713 Datum
714 seg_size(PG_FUNCTION_ARGS)
715 {
716 SEG *seg = PG_GETARG_SEG_P(0);
717
718 PG_RETURN_FLOAT4((float) Abs(seg->upper - seg->lower));
719 }
720
721
722 /*****************************************************************************
723 * Miscellaneous operators
starname(S)724 *****************************************************************************/
725 Datum
726 seg_cmp(PG_FUNCTION_ARGS)
727 {
728 SEG *a = PG_GETARG_SEG_P(0);
729 SEG *b = PG_GETARG_SEG_P(1);
730
731 /*
732 * First compare on lower boundary position
733 */
734 if (a->lower < b->lower)
735 PG_RETURN_INT32(-1);
736 if (a->lower > b->lower)
737 PG_RETURN_INT32(1);
738
739 /*
740 * a->lower == b->lower, so consider type of boundary.
741 *
742 * A '-' lower bound is < any other kind (this could only be relevant if
743 * -HUGE_VAL is used as a regular data value). A '<' lower bound is < any
744 * other kind except '-'. A '>' lower bound is > any other kind.
745 */
746 if (a->l_ext != b->l_ext)
747 {
748 if (a->l_ext == '-')
749 PG_RETURN_INT32(-1);
750 if (b->l_ext == '-')
751 PG_RETURN_INT32(1);
752 if (a->l_ext == '<')
753 PG_RETURN_INT32(-1);
754 if (b->l_ext == '<')
755 PG_RETURN_INT32(1);
756 if (a->l_ext == '>')
757 PG_RETURN_INT32(1);
758 if (b->l_ext == '>')
759 PG_RETURN_INT32(-1);
760 }
761
762 /*
763 * For other boundary types, consider # of significant digits first.
764 */
765 if (a->l_sigd < b->l_sigd) /* (a) is blurred and is likely to include (b) */
766 PG_RETURN_INT32(-1);
767 if (a->l_sigd > b->l_sigd) /* (a) is less blurred and is likely to be
768 * included in (b) */
769 PG_RETURN_INT32(1);
770
771 /*
772 * For same # of digits, an approximate boundary is more blurred than
773 * exact.
774 */
775 if (a->l_ext != b->l_ext)
776 {
777 if (a->l_ext == '~') /* (a) is approximate, while (b) is exact */
778 PG_RETURN_INT32(-1);
779 if (b->l_ext == '~')
780 PG_RETURN_INT32(1);
781 /* can't get here unless data is corrupt */
782 elog(ERROR, "bogus lower boundary types %d %d",
783 (int) a->l_ext, (int) b->l_ext);
784 }
785
786 /* at this point, the lower boundaries are identical */
787
788 /*
789 * First compare on upper boundary position
790 */
791 if (a->upper < b->upper)
792 PG_RETURN_INT32(-1);
793 if (a->upper > b->upper)
794 PG_RETURN_INT32(1);
795
796 /*
797 * a->upper == b->upper, so consider type of boundary.
798 *
799 * A '-' upper bound is > any other kind (this could only be relevant if
800 * HUGE_VAL is used as a regular data value). A '<' upper bound is < any
801 * other kind. A '>' upper bound is > any other kind except '-'.
802 */
803 if (a->u_ext != b->u_ext)
804 {
805 if (a->u_ext == '-')
806 PG_RETURN_INT32(1);
807 if (b->u_ext == '-')
808 PG_RETURN_INT32(-1);
809 if (a->u_ext == '<')
810 PG_RETURN_INT32(-1);
811 if (b->u_ext == '<')
812 PG_RETURN_INT32(1);
813 if (a->u_ext == '>')
814 PG_RETURN_INT32(1);
815 if (b->u_ext == '>')
816 PG_RETURN_INT32(-1);
817 }
818
819 /*
820 * For other boundary types, consider # of significant digits first. Note
821 * result here is converse of the lower-boundary case.
822 */
823 if (a->u_sigd < b->u_sigd) /* (a) is blurred and is likely to include (b) */
824 PG_RETURN_INT32(1);
825 if (a->u_sigd > b->u_sigd) /* (a) is less blurred and is likely to be
826 * included in (b) */
827 PG_RETURN_INT32(-1);
828
829 /*
830 * For same # of digits, an approximate boundary is more blurred than
831 * exact. Again, result is converse of lower-boundary case.
832 */
833 if (a->u_ext != b->u_ext)
834 {
835 if (a->u_ext == '~') /* (a) is approximate, while (b) is exact */
836 PG_RETURN_INT32(1);
837 if (b->u_ext == '~')
838 PG_RETURN_INT32(-1);
839 /* can't get here unless data is corrupt */
840 elog(ERROR, "bogus upper boundary types %d %d",
841 (int) a->u_ext, (int) b->u_ext);
842 }
843
844 PG_RETURN_INT32(0);
845 }
846
847 Datum
848 seg_lt(PG_FUNCTION_ARGS)
849 {
850 int cmp = DatumGetInt32(
851 DirectFunctionCall2(seg_cmp, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1)));
852
853 PG_RETURN_BOOL(cmp < 0);
854 }
855
856 Datum
857 seg_le(PG_FUNCTION_ARGS)
858 {
859 int cmp = DatumGetInt32(
expr(C)860 DirectFunctionCall2(seg_cmp, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1)));
expr(C)861
862 PG_RETURN_BOOL(cmp <= 0);
863 }
expr(C)864
865 Datum
866 seg_gt(PG_FUNCTION_ARGS)
867 {
868 int cmp = DatumGetInt32(
869 DirectFunctionCall2(seg_cmp, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1)));
870
871 PG_RETURN_BOOL(cmp > 0);
872 }
873
874 Datum
875 seg_ge(PG_FUNCTION_ARGS)
expr(E)876 {
877 int cmp = DatumGetInt32(
878 DirectFunctionCall2(seg_cmp, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1)));
879
880 PG_RETURN_BOOL(cmp >= 0);
881 }
882
883
884 Datum
885 seg_different(PG_FUNCTION_ARGS)
886 {
887 int cmp = DatumGetInt32(
888 DirectFunctionCall2(seg_cmp, PG_GETARG_DATUM(0), PG_GETARG_DATUM(1)));
expr(E)889
890 PG_RETURN_BOOL(cmp != 0);
891 }
892
893
894
895 /*****************************************************************************
896 * Auxiliary functions
897 *****************************************************************************/
898
expr(E)899 /*
900 * The purpose of this routine is to print the given floating point
901 * value with exactly n significant digits. Its behaviour
902 * is similar to %.ng except it prints 8.00 where %.ng would
903 * print 8. Returns the length of the string written at "result".
904 *
905 * Caller must provide a sufficiently large result buffer; 16 bytes
906 * should be enough for all known float implementations.
907 */
908 static int
909 restore(char *result, float val, int n)
910 {
911 char buf[25] = {
912 '0', '0', '0', '0', '0',
913 '0', '0', '0', '0', '0',
914 '0', '0', '0', '0', '0',
915 '0', '0', '0', '0', '0',
916 '0', '0', '0', '0', '\0'
917 };
918 char *p;
919 int exp;
920 int i,
921 dp,
922 sign;
923
924 /*
925 * Put a cap on the number of significant digits to avoid garbage in the
926 * output and ensure we don't overrun the result buffer.
927 */
928 n = Min(n, FLT_DIG);
929
930 /* remember the sign */
931 sign = (val < 0 ? 1 : 0);
932
933 /* print, in %e style to start with */
934 sprintf(result, "%.*e", n - 1, val);
935
936 /* find the exponent */
937 p = strchr(result, 'e');
938
939 /* punt if we have 'inf' or similar */
940 if (p == NULL)
941 return strlen(result);
942
943 exp = atoi(p + 1);
944 if (exp == 0)
945 {
946 /* just truncate off the 'e+00' */
947 *p = '\0';
948 }
949 else
950 {
951 if (Abs(exp) <= 4)
952 {
953 /*
954 * remove the decimal point from the mantissa and write the digits
955 * to the buf array
956 */
957 for (p = result + sign, i = 10, dp = 0; *p != 'e'; p++, i++)
958 {
959 buf[i] = *p;
960 if (*p == '.')
961 {
962 dp = i--; /* skip the decimal point */
963 }
964 }
965 if (dp == 0)
966 dp = i--; /* no decimal point was found in the above
967 * for() loop */
968
969 if (exp > 0)
970 {
971 if (dp - 10 + exp >= n)
972 {
973 /*
974 * the decimal point is behind the last significant digit;
975 * the digits in between must be converted to the exponent
976 * and the decimal point placed after the first digit
977 */
978 exp = dp - 10 + exp - n;
979 buf[10 + n] = '\0';
980
981 /* insert the decimal point */
982 if (n > 1)
983 {
984 dp = 11;
985 for (i = 23; i > dp; i--)
986 buf[i] = buf[i - 1];
987 buf[dp] = '.';
988 }
989
990 /*
991 * adjust the exponent by the number of digits after the
992 * decimal point
993 */
994 if (n > 1)
995 sprintf(&buf[11 + n], "e%d", exp + n - 1);
996 else
997 sprintf(&buf[11], "e%d", exp + n - 1);
998
999 if (sign)
1000 {
1001 buf[9] = '-';
1002 strcpy(result, &buf[9]);
1003 }
1004 else
1005 strcpy(result, &buf[10]);
1006 }
1007 else
1008 { /* insert the decimal point */
1009 dp += exp;
1010 for (i = 23; i > dp; i--)
1011 buf[i] = buf[i - 1];
1012 buf[11 + n] = '\0';
1013 buf[dp] = '.';
1014 if (sign)
1015 {
1016 buf[9] = '-';
1017 strcpy(result, &buf[9]);
1018 }
1019 else
1020 strcpy(result, &buf[10]);
1021 }
1022 }
1023 else
1024 { /* exp <= 0 */
1025 dp += exp - 1;
1026 buf[10 + n] = '\0';
1027 buf[dp] = '.';
1028 if (sign)
1029 {
1030 buf[dp - 2] = '-';
1031 strcpy(result, &buf[dp - 2]);
1032 }
1033 else
1034 strcpy(result, &buf[dp - 1]);
1035 }
1036 }
1037
1038 /* do nothing for Abs(exp) > 4; %e must be OK */
1039 /* just get rid of zeroes after [eE]- and +zeroes after [Ee]. */
1040
1041 /* ... this is not done yet. */
1042 }
1043 return strlen(result);
1044 }
1045
1046
1047 /*
1048 ** Miscellany
1049 */
1050
1051 /* find out the number of significant digits in a string representing
1052 * a floating point number
1053 */
1054 int
1055 significant_digits(const char *s)
1056 {
1057 const char *p = s;
1058 int n,
1059 c,
1060 zeroes;
1061
1062 zeroes = 1;
1063 /* skip leading zeroes and sign */
1064 for (c = *p; (c == '0' || c == '+' || c == '-') && c != 0; c = *(++p));
1065
1066 /* skip decimal point and following zeroes */
1067 for (c = *p; (c == '0' || c == '.') && c != 0; c = *(++p))
1068 {
1069 if (c != '.')
1070 zeroes++;
1071 }
1072
1073 /* count significant digits (n) */
1074 for (c = *p, n = 0; c != 0; c = *(++p))
1075 {
1076 if (!((c >= '0' && c <= '9') || (c == '.')))
1077 break;
1078 if (c != '.')
1079 n++;
1080 }
1081
1082 if (!n)
1083 return zeroes;
1084
1085 return n;
1086 }
1087