Coverage Report

Created: 2026-10-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/access/nbtree/nbtcompare.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * nbtcompare.c
4
 *    Comparison functions for btree access method.
5
 *
6
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 *
10
 * IDENTIFICATION
11
 *    src/backend/access/nbtree/nbtcompare.c
12
 *
13
 * NOTES
14
 *
15
 *  These functions are stored in pg_amproc.  For each operator class
16
 *  defined on btrees, they compute
17
 *
18
 *        compare(a, b):
19
 *            < 0 if a < b,
20
 *            = 0 if a == b,
21
 *            > 0 if a > b.
22
 *
23
 *  The result is always an int32 regardless of the input datatype.
24
 *
25
 *  Although any negative int32 is acceptable for reporting "<",
26
 *  and any positive int32 is acceptable for reporting ">", routines
27
 *  that work on 32-bit or wider datatypes can't just return "a - b".
28
 *  That could overflow and give the wrong answer.
29
 *
30
 *  NOTE: it is critical that the comparison function impose a total order
31
 *  on all non-NULL values of the data type, and that the datatype's
32
 *  boolean comparison operators (= < >= etc) yield results consistent
33
 *  with the comparison routine.  Otherwise bad behavior may ensue.
34
 *  (For example, the comparison operators must NOT punt when faced with
35
 *  NAN or other funny values; you must devise some collation sequence for
36
 *  all such values.)  If the datatype is not trivial, this is most
37
 *  reliably done by having the boolean operators invoke the same
38
 *  three-way comparison code that the btree function does.  Therefore,
39
 *  this file contains only btree support for "trivial" datatypes ---
40
 *  all others are in the /utils/adt/ files that implement their datatypes.
41
 *
42
 *  NOTE: these routines must not leak memory, since memory allocated
43
 *  during an index access won't be recovered till end of query.  This
44
 *  primarily affects comparison routines for toastable datatypes;
45
 *  they have to be careful to free any detoasted copy of an input datum.
46
 *
47
 *  NOTE: we used to forbid comparison functions from returning INT_MIN,
48
 *  but that proves to be too error-prone because some platforms' versions
49
 *  of memcmp() etc can return INT_MIN.  As a means of stress-testing
50
 *  callers, this file can be compiled with STRESS_SORT_INT_MIN defined
51
 *  to cause many of these functions to return INT_MIN or INT_MAX instead of
52
 *  their customary -1/+1.  For production, though, that's not a good idea
53
 *  since users or third-party code might expect the traditional results.
54
 *-------------------------------------------------------------------------
55
 */
56
#include "postgres.h"
57
58
#include <limits.h>
59
60
#include "utils/builtins.h"
61
#include "utils/fmgrprotos.h"
62
#include "utils/skipsupport.h"
63
#include "utils/sortsupport.h"
64
65
#ifdef STRESS_SORT_INT_MIN
66
#define A_LESS_THAN_B   INT_MIN
67
#define A_GREATER_THAN_B  INT_MAX
68
#else
69
#define A_LESS_THAN_B   (-1)
70
#define A_GREATER_THAN_B  1
71
#endif
72
73
74
Datum
75
btboolcmp(PG_FUNCTION_ARGS)
76
0
{
77
0
  bool    a = PG_GETARG_BOOL(0);
78
0
  bool    b = PG_GETARG_BOOL(1);
79
80
0
  PG_RETURN_INT32((int32) a - (int32) b);
81
0
}
82
83
static Datum
84
bool_decrement(Relation rel, Datum existing, bool *underflow)
85
0
{
86
0
  bool    bexisting = DatumGetBool(existing);
87
88
0
  if (bexisting == false)
89
0
  {
90
    /* return value is undefined */
91
0
    *underflow = true;
92
0
    return (Datum) 0;
93
0
  }
94
95
0
  *underflow = false;
96
0
  return BoolGetDatum(bexisting - 1);
97
0
}
98
99
static Datum
100
bool_increment(Relation rel, Datum existing, bool *overflow)
101
0
{
102
0
  bool    bexisting = DatumGetBool(existing);
103
104
0
  if (bexisting == true)
105
0
  {
106
    /* return value is undefined */
107
0
    *overflow = true;
108
0
    return (Datum) 0;
109
0
  }
110
111
0
  *overflow = false;
112
0
  return BoolGetDatum(bexisting + 1);
113
0
}
114
115
Datum
116
btboolskipsupport(PG_FUNCTION_ARGS)
117
0
{
118
0
  SkipSupport sksup = (SkipSupport) PG_GETARG_POINTER(0);
119
120
0
  sksup->decrement = bool_decrement;
121
0
  sksup->increment = bool_increment;
122
0
  sksup->low_elem = BoolGetDatum(false);
123
0
  sksup->high_elem = BoolGetDatum(true);
124
125
0
  PG_RETURN_VOID();
126
0
}
127
128
Datum
129
btint2cmp(PG_FUNCTION_ARGS)
130
0
{
131
0
  int16   a = PG_GETARG_INT16(0);
132
0
  int16   b = PG_GETARG_INT16(1);
133
134
0
  PG_RETURN_INT32((int32) a - (int32) b);
135
0
}
136
137
Datum
138
btint2sortsupport(PG_FUNCTION_ARGS)
139
0
{
140
0
  SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
141
142
0
  ssup->comparator = ssup_datum_int32_cmp;
143
0
  PG_RETURN_VOID();
144
0
}
145
146
static Datum
147
int2_decrement(Relation rel, Datum existing, bool *underflow)
148
0
{
149
0
  int16   iexisting = DatumGetInt16(existing);
150
151
0
  if (iexisting == PG_INT16_MIN)
152
0
  {
153
    /* return value is undefined */
154
0
    *underflow = true;
155
0
    return (Datum) 0;
156
0
  }
157
158
0
  *underflow = false;
159
0
  return Int16GetDatum(iexisting - 1);
160
0
}
161
162
static Datum
163
int2_increment(Relation rel, Datum existing, bool *overflow)
164
0
{
165
0
  int16   iexisting = DatumGetInt16(existing);
166
167
0
  if (iexisting == PG_INT16_MAX)
168
0
  {
169
    /* return value is undefined */
170
0
    *overflow = true;
171
0
    return (Datum) 0;
172
0
  }
173
174
0
  *overflow = false;
175
0
  return Int16GetDatum(iexisting + 1);
176
0
}
177
178
Datum
179
btint2skipsupport(PG_FUNCTION_ARGS)
180
0
{
181
0
  SkipSupport sksup = (SkipSupport) PG_GETARG_POINTER(0);
182
183
0
  sksup->decrement = int2_decrement;
184
0
  sksup->increment = int2_increment;
185
0
  sksup->low_elem = Int16GetDatum(PG_INT16_MIN);
186
0
  sksup->high_elem = Int16GetDatum(PG_INT16_MAX);
187
188
0
  PG_RETURN_VOID();
189
0
}
190
191
Datum
192
btint4cmp(PG_FUNCTION_ARGS)
193
0
{
194
0
  int32   a = PG_GETARG_INT32(0);
195
0
  int32   b = PG_GETARG_INT32(1);
196
197
0
  if (a > b)
198
0
    PG_RETURN_INT32(A_GREATER_THAN_B);
199
0
  else if (a == b)
200
0
    PG_RETURN_INT32(0);
201
0
  else
202
0
    PG_RETURN_INT32(A_LESS_THAN_B);
203
0
}
204
205
Datum
206
btint4sortsupport(PG_FUNCTION_ARGS)
207
0
{
208
0
  SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
209
210
0
  ssup->comparator = ssup_datum_int32_cmp;
211
0
  PG_RETURN_VOID();
212
0
}
213
214
static Datum
215
int4_decrement(Relation rel, Datum existing, bool *underflow)
216
0
{
217
0
  int32   iexisting = DatumGetInt32(existing);
218
219
0
  if (iexisting == PG_INT32_MIN)
220
0
  {
221
    /* return value is undefined */
222
0
    *underflow = true;
223
0
    return (Datum) 0;
224
0
  }
225
226
0
  *underflow = false;
227
0
  return Int32GetDatum(iexisting - 1);
228
0
}
229
230
static Datum
231
int4_increment(Relation rel, Datum existing, bool *overflow)
232
0
{
233
0
  int32   iexisting = DatumGetInt32(existing);
234
235
0
  if (iexisting == PG_INT32_MAX)
236
0
  {
237
    /* return value is undefined */
238
0
    *overflow = true;
239
0
    return (Datum) 0;
240
0
  }
241
242
0
  *overflow = false;
243
0
  return Int32GetDatum(iexisting + 1);
244
0
}
245
246
Datum
247
btint4skipsupport(PG_FUNCTION_ARGS)
248
0
{
249
0
  SkipSupport sksup = (SkipSupport) PG_GETARG_POINTER(0);
250
251
0
  sksup->decrement = int4_decrement;
252
0
  sksup->increment = int4_increment;
253
0
  sksup->low_elem = Int32GetDatum(PG_INT32_MIN);
254
0
  sksup->high_elem = Int32GetDatum(PG_INT32_MAX);
255
256
0
  PG_RETURN_VOID();
257
0
}
258
259
Datum
260
btint8cmp(PG_FUNCTION_ARGS)
261
0
{
262
0
  int64   a = PG_GETARG_INT64(0);
263
0
  int64   b = PG_GETARG_INT64(1);
264
265
0
  if (a > b)
266
0
    PG_RETURN_INT32(A_GREATER_THAN_B);
267
0
  else if (a == b)
268
0
    PG_RETURN_INT32(0);
269
0
  else
270
0
    PG_RETURN_INT32(A_LESS_THAN_B);
271
0
}
272
273
Datum
274
btint8sortsupport(PG_FUNCTION_ARGS)
275
0
{
276
0
  SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
277
278
0
  ssup->comparator = ssup_datum_int64_cmp;
279
0
  PG_RETURN_VOID();
280
0
}
281
282
static Datum
283
int8_decrement(Relation rel, Datum existing, bool *underflow)
284
0
{
285
0
  int64   iexisting = DatumGetInt64(existing);
286
287
0
  if (iexisting == PG_INT64_MIN)
288
0
  {
289
    /* return value is undefined */
290
0
    *underflow = true;
291
0
    return (Datum) 0;
292
0
  }
293
294
0
  *underflow = false;
295
0
  return Int64GetDatum(iexisting - 1);
296
0
}
297
298
static Datum
299
int8_increment(Relation rel, Datum existing, bool *overflow)
300
0
{
301
0
  int64   iexisting = DatumGetInt64(existing);
302
303
0
  if (iexisting == PG_INT64_MAX)
304
0
  {
305
    /* return value is undefined */
306
0
    *overflow = true;
307
0
    return (Datum) 0;
308
0
  }
309
310
0
  *overflow = false;
311
0
  return Int64GetDatum(iexisting + 1);
312
0
}
313
314
Datum
315
btint8skipsupport(PG_FUNCTION_ARGS)
316
0
{
317
0
  SkipSupport sksup = (SkipSupport) PG_GETARG_POINTER(0);
318
319
0
  sksup->decrement = int8_decrement;
320
0
  sksup->increment = int8_increment;
321
0
  sksup->low_elem = Int64GetDatum(PG_INT64_MIN);
322
0
  sksup->high_elem = Int64GetDatum(PG_INT64_MAX);
323
324
0
  PG_RETURN_VOID();
325
0
}
326
327
Datum
328
btint48cmp(PG_FUNCTION_ARGS)
329
0
{
330
0
  int32   a = PG_GETARG_INT32(0);
331
0
  int64   b = PG_GETARG_INT64(1);
332
333
0
  if (a > b)
334
0
    PG_RETURN_INT32(A_GREATER_THAN_B);
335
0
  else if (a == b)
336
0
    PG_RETURN_INT32(0);
337
0
  else
338
0
    PG_RETURN_INT32(A_LESS_THAN_B);
339
0
}
340
341
Datum
342
btint84cmp(PG_FUNCTION_ARGS)
343
0
{
344
0
  int64   a = PG_GETARG_INT64(0);
345
0
  int32   b = PG_GETARG_INT32(1);
346
347
0
  if (a > b)
348
0
    PG_RETURN_INT32(A_GREATER_THAN_B);
349
0
  else if (a == b)
350
0
    PG_RETURN_INT32(0);
351
0
  else
352
0
    PG_RETURN_INT32(A_LESS_THAN_B);
353
0
}
354
355
Datum
356
btint24cmp(PG_FUNCTION_ARGS)
357
0
{
358
0
  int16   a = PG_GETARG_INT16(0);
359
0
  int32   b = PG_GETARG_INT32(1);
360
361
0
  if (a > b)
362
0
    PG_RETURN_INT32(A_GREATER_THAN_B);
363
0
  else if (a == b)
364
0
    PG_RETURN_INT32(0);
365
0
  else
366
0
    PG_RETURN_INT32(A_LESS_THAN_B);
367
0
}
368
369
Datum
370
btint42cmp(PG_FUNCTION_ARGS)
371
0
{
372
0
  int32   a = PG_GETARG_INT32(0);
373
0
  int16   b = PG_GETARG_INT16(1);
374
375
0
  if (a > b)
376
0
    PG_RETURN_INT32(A_GREATER_THAN_B);
377
0
  else if (a == b)
378
0
    PG_RETURN_INT32(0);
379
0
  else
380
0
    PG_RETURN_INT32(A_LESS_THAN_B);
381
0
}
382
383
Datum
384
btint28cmp(PG_FUNCTION_ARGS)
385
0
{
386
0
  int16   a = PG_GETARG_INT16(0);
387
0
  int64   b = PG_GETARG_INT64(1);
388
389
0
  if (a > b)
390
0
    PG_RETURN_INT32(A_GREATER_THAN_B);
391
0
  else if (a == b)
392
0
    PG_RETURN_INT32(0);
393
0
  else
394
0
    PG_RETURN_INT32(A_LESS_THAN_B);
395
0
}
396
397
Datum
398
btint82cmp(PG_FUNCTION_ARGS)
399
0
{
400
0
  int64   a = PG_GETARG_INT64(0);
401
0
  int16   b = PG_GETARG_INT16(1);
402
403
0
  if (a > b)
404
0
    PG_RETURN_INT32(A_GREATER_THAN_B);
405
0
  else if (a == b)
406
0
    PG_RETURN_INT32(0);
407
0
  else
408
0
    PG_RETURN_INT32(A_LESS_THAN_B);
409
0
}
410
411
Datum
412
btoidcmp(PG_FUNCTION_ARGS)
413
0
{
414
0
  Oid     a = PG_GETARG_OID(0);
415
0
  Oid     b = PG_GETARG_OID(1);
416
417
0
  if (a > b)
418
0
    PG_RETURN_INT32(A_GREATER_THAN_B);
419
0
  else if (a == b)
420
0
    PG_RETURN_INT32(0);
421
0
  else
422
0
    PG_RETURN_INT32(A_LESS_THAN_B);
423
0
}
424
425
Datum
426
btoidsortsupport(PG_FUNCTION_ARGS)
427
0
{
428
0
  SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
429
430
0
  ssup->comparator = ssup_datum_uint32_cmp;
431
0
  PG_RETURN_VOID();
432
0
}
433
434
static Datum
435
oid_decrement(Relation rel, Datum existing, bool *underflow)
436
0
{
437
0
  Oid     oexisting = DatumGetObjectId(existing);
438
439
0
  if (oexisting == InvalidOid)
440
0
  {
441
    /* return value is undefined */
442
0
    *underflow = true;
443
0
    return (Datum) 0;
444
0
  }
445
446
0
  *underflow = false;
447
0
  return ObjectIdGetDatum(oexisting - 1);
448
0
}
449
450
static Datum
451
oid_increment(Relation rel, Datum existing, bool *overflow)
452
0
{
453
0
  Oid     oexisting = DatumGetObjectId(existing);
454
455
0
  if (oexisting == OID_MAX)
456
0
  {
457
    /* return value is undefined */
458
0
    *overflow = true;
459
0
    return (Datum) 0;
460
0
  }
461
462
0
  *overflow = false;
463
0
  return ObjectIdGetDatum(oexisting + 1);
464
0
}
465
466
Datum
467
btoidskipsupport(PG_FUNCTION_ARGS)
468
0
{
469
0
  SkipSupport sksup = (SkipSupport) PG_GETARG_POINTER(0);
470
471
0
  sksup->decrement = oid_decrement;
472
0
  sksup->increment = oid_increment;
473
0
  sksup->low_elem = ObjectIdGetDatum(InvalidOid);
474
0
  sksup->high_elem = ObjectIdGetDatum(OID_MAX);
475
476
0
  PG_RETURN_VOID();
477
0
}
478
479
Datum
480
btoid8cmp(PG_FUNCTION_ARGS)
481
0
{
482
0
  Oid8    a = PG_GETARG_OID8(0);
483
0
  Oid8    b = PG_GETARG_OID8(1);
484
485
0
  if (a > b)
486
0
    PG_RETURN_INT32(A_GREATER_THAN_B);
487
0
  else if (a == b)
488
0
    PG_RETURN_INT32(0);
489
0
  else
490
0
    PG_RETURN_INT32(A_LESS_THAN_B);
491
0
}
492
493
Datum
494
btoid8sortsupport(PG_FUNCTION_ARGS)
495
0
{
496
0
  SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
497
498
0
  ssup->comparator = ssup_datum_uint64_cmp;
499
0
  PG_RETURN_VOID();
500
0
}
501
502
static Datum
503
oid8_decrement(Relation rel, Datum existing, bool *underflow)
504
0
{
505
0
  Oid8    oexisting = DatumGetObjectId8(existing);
506
507
0
  if (oexisting == InvalidOid8)
508
0
  {
509
    /* return value is undefined */
510
0
    *underflow = true;
511
0
    return (Datum) 0;
512
0
  }
513
514
0
  *underflow = false;
515
0
  return ObjectId8GetDatum(oexisting - 1);
516
0
}
517
518
static Datum
519
oid8_increment(Relation rel, Datum existing, bool *overflow)
520
0
{
521
0
  Oid8    oexisting = DatumGetObjectId8(existing);
522
523
0
  if (oexisting == OID8_MAX)
524
0
  {
525
    /* return value is undefined */
526
0
    *overflow = true;
527
0
    return (Datum) 0;
528
0
  }
529
530
0
  *overflow = false;
531
0
  return ObjectId8GetDatum(oexisting + 1);
532
0
}
533
534
Datum
535
btoid8skipsupport(PG_FUNCTION_ARGS)
536
0
{
537
0
  SkipSupport sksup = (SkipSupport) PG_GETARG_POINTER(0);
538
539
0
  sksup->decrement = oid8_decrement;
540
0
  sksup->increment = oid8_increment;
541
0
  sksup->low_elem = ObjectId8GetDatum(InvalidOid8);
542
0
  sksup->high_elem = ObjectId8GetDatum(OID8_MAX);
543
544
0
  PG_RETURN_VOID();
545
0
}
546
547
Datum
548
btoidvectorcmp(PG_FUNCTION_ARGS)
549
0
{
550
0
  oidvector  *a = (oidvector *) PG_GETARG_POINTER(0);
551
0
  oidvector  *b = (oidvector *) PG_GETARG_POINTER(1);
552
0
  int     i;
553
554
0
  check_valid_oidvector(a);
555
0
  check_valid_oidvector(b);
556
557
  /* We arbitrarily choose to sort first by vector length */
558
0
  if (a->dim1 != b->dim1)
559
0
    PG_RETURN_INT32(a->dim1 - b->dim1);
560
561
0
  for (i = 0; i < a->dim1; i++)
562
0
  {
563
0
    if (a->values[i] != b->values[i])
564
0
    {
565
0
      if (a->values[i] > b->values[i])
566
0
        PG_RETURN_INT32(A_GREATER_THAN_B);
567
0
      else
568
0
        PG_RETURN_INT32(A_LESS_THAN_B);
569
0
    }
570
0
  }
571
0
  PG_RETURN_INT32(0);
572
0
}
573
574
Datum
575
btcharcmp(PG_FUNCTION_ARGS)
576
0
{
577
0
  char    a = PG_GETARG_CHAR(0);
578
0
  char    b = PG_GETARG_CHAR(1);
579
580
  /* Be careful to compare chars as unsigned */
581
0
  PG_RETURN_INT32((int32) ((uint8) a) - (int32) ((uint8) b));
582
0
}
583
584
static Datum
585
char_decrement(Relation rel, Datum existing, bool *underflow)
586
0
{
587
0
  uint8   cexisting = DatumGetUInt8(existing);
588
589
0
  if (cexisting == 0)
590
0
  {
591
    /* return value is undefined */
592
0
    *underflow = true;
593
0
    return (Datum) 0;
594
0
  }
595
596
0
  *underflow = false;
597
0
  return CharGetDatum((uint8) cexisting - 1);
598
0
}
599
600
static Datum
601
char_increment(Relation rel, Datum existing, bool *overflow)
602
0
{
603
0
  uint8   cexisting = DatumGetUInt8(existing);
604
605
0
  if (cexisting == UCHAR_MAX)
606
0
  {
607
    /* return value is undefined */
608
0
    *overflow = true;
609
0
    return (Datum) 0;
610
0
  }
611
612
0
  *overflow = false;
613
0
  return CharGetDatum((uint8) cexisting + 1);
614
0
}
615
616
Datum
617
btcharskipsupport(PG_FUNCTION_ARGS)
618
0
{
619
0
  SkipSupport sksup = (SkipSupport) PG_GETARG_POINTER(0);
620
621
0
  sksup->decrement = char_decrement;
622
0
  sksup->increment = char_increment;
623
624
  /* btcharcmp compares chars as unsigned */
625
0
  sksup->low_elem = UInt8GetDatum(0);
626
0
  sksup->high_elem = UInt8GetDatum(UCHAR_MAX);
627
628
0
  PG_RETURN_VOID();
629
0
}