00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "postgres.h"
00017
00018 #include <time.h>
00019
00020 #include "access/nbtree.h"
00021 #include "access/reloptions.h"
00022 #include "access/relscan.h"
00023 #include "miscadmin.h"
00024 #include "utils/array.h"
00025 #include "utils/lsyscache.h"
00026 #include "utils/memutils.h"
00027 #include "utils/rel.h"
00028
00029
00030 typedef struct BTSortArrayContext
00031 {
00032 FmgrInfo flinfo;
00033 Oid collation;
00034 bool reverse;
00035 } BTSortArrayContext;
00036
00037 static Datum _bt_find_extreme_element(IndexScanDesc scan, ScanKey skey,
00038 StrategyNumber strat,
00039 Datum *elems, int nelems);
00040 static int _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
00041 bool reverse,
00042 Datum *elems, int nelems);
00043 static int _bt_compare_array_elements(const void *a, const void *b, void *arg);
00044 static bool _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op,
00045 ScanKey leftarg, ScanKey rightarg,
00046 bool *result);
00047 static bool _bt_fix_scankey_strategy(ScanKey skey, int16 *indoption);
00048 static void _bt_mark_scankey_required(ScanKey skey);
00049 static bool _bt_check_rowcompare(ScanKey skey,
00050 IndexTuple tuple, TupleDesc tupdesc,
00051 ScanDirection dir, bool *continuescan);
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061 ScanKey
00062 _bt_mkscankey(Relation rel, IndexTuple itup)
00063 {
00064 ScanKey skey;
00065 TupleDesc itupdesc;
00066 int natts;
00067 int16 *indoption;
00068 int i;
00069
00070 itupdesc = RelationGetDescr(rel);
00071 natts = RelationGetNumberOfAttributes(rel);
00072 indoption = rel->rd_indoption;
00073
00074 skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
00075
00076 for (i = 0; i < natts; i++)
00077 {
00078 FmgrInfo *procinfo;
00079 Datum arg;
00080 bool null;
00081 int flags;
00082
00083
00084
00085
00086
00087 procinfo = index_getprocinfo(rel, i + 1, BTORDER_PROC);
00088 arg = index_getattr(itup, i + 1, itupdesc, &null);
00089 flags = (null ? SK_ISNULL : 0) | (indoption[i] << SK_BT_INDOPTION_SHIFT);
00090 ScanKeyEntryInitializeWithInfo(&skey[i],
00091 flags,
00092 (AttrNumber) (i + 1),
00093 InvalidStrategy,
00094 InvalidOid,
00095 rel->rd_indcollation[i],
00096 procinfo,
00097 arg);
00098 }
00099
00100 return skey;
00101 }
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114 ScanKey
00115 _bt_mkscankey_nodata(Relation rel)
00116 {
00117 ScanKey skey;
00118 int natts;
00119 int16 *indoption;
00120 int i;
00121
00122 natts = RelationGetNumberOfAttributes(rel);
00123 indoption = rel->rd_indoption;
00124
00125 skey = (ScanKey) palloc(natts * sizeof(ScanKeyData));
00126
00127 for (i = 0; i < natts; i++)
00128 {
00129 FmgrInfo *procinfo;
00130 int flags;
00131
00132
00133
00134
00135
00136 procinfo = index_getprocinfo(rel, i + 1, BTORDER_PROC);
00137 flags = SK_ISNULL | (indoption[i] << SK_BT_INDOPTION_SHIFT);
00138 ScanKeyEntryInitializeWithInfo(&skey[i],
00139 flags,
00140 (AttrNumber) (i + 1),
00141 InvalidStrategy,
00142 InvalidOid,
00143 rel->rd_indcollation[i],
00144 procinfo,
00145 (Datum) 0);
00146 }
00147
00148 return skey;
00149 }
00150
00151
00152
00153
00154 void
00155 _bt_freeskey(ScanKey skey)
00156 {
00157 pfree(skey);
00158 }
00159
00160
00161
00162
00163 void
00164 _bt_freestack(BTStack stack)
00165 {
00166 BTStack ostack;
00167
00168 while (stack != NULL)
00169 {
00170 ostack = stack;
00171 stack = stack->bts_parent;
00172 pfree(ostack);
00173 }
00174 }
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191 void
00192 _bt_preprocess_array_keys(IndexScanDesc scan)
00193 {
00194 BTScanOpaque so = (BTScanOpaque) scan->opaque;
00195 int numberOfKeys = scan->numberOfKeys;
00196 int16 *indoption = scan->indexRelation->rd_indoption;
00197 int numArrayKeys;
00198 ScanKey cur;
00199 int i;
00200 MemoryContext oldContext;
00201
00202
00203 numArrayKeys = 0;
00204 for (i = 0; i < numberOfKeys; i++)
00205 {
00206 cur = &scan->keyData[i];
00207 if (cur->sk_flags & SK_SEARCHARRAY)
00208 {
00209 numArrayKeys++;
00210 Assert(!(cur->sk_flags & (SK_ROW_HEADER | SK_SEARCHNULL | SK_SEARCHNOTNULL)));
00211
00212 if (cur->sk_flags & SK_ISNULL)
00213 {
00214 so->numArrayKeys = -1;
00215 so->arrayKeyData = NULL;
00216 return;
00217 }
00218 }
00219 }
00220
00221
00222 if (numArrayKeys == 0)
00223 {
00224 so->numArrayKeys = 0;
00225 so->arrayKeyData = NULL;
00226 return;
00227 }
00228
00229
00230
00231
00232
00233 if (so->arrayContext == NULL)
00234 so->arrayContext = AllocSetContextCreate(CurrentMemoryContext,
00235 "BTree Array Context",
00236 ALLOCSET_SMALL_MINSIZE,
00237 ALLOCSET_SMALL_INITSIZE,
00238 ALLOCSET_SMALL_MAXSIZE);
00239 else
00240 MemoryContextReset(so->arrayContext);
00241
00242 oldContext = MemoryContextSwitchTo(so->arrayContext);
00243
00244
00245 so->arrayKeyData = (ScanKey) palloc(scan->numberOfKeys * sizeof(ScanKeyData));
00246 memcpy(so->arrayKeyData,
00247 scan->keyData,
00248 scan->numberOfKeys * sizeof(ScanKeyData));
00249
00250
00251 so->arrayKeys = (BTArrayKeyInfo *) palloc0(numArrayKeys * sizeof(BTArrayKeyInfo));
00252
00253
00254 numArrayKeys = 0;
00255 for (i = 0; i < numberOfKeys; i++)
00256 {
00257 ArrayType *arrayval;
00258 int16 elmlen;
00259 bool elmbyval;
00260 char elmalign;
00261 int num_elems;
00262 Datum *elem_values;
00263 bool *elem_nulls;
00264 int num_nonnulls;
00265 int j;
00266
00267 cur = &so->arrayKeyData[i];
00268 if (!(cur->sk_flags & SK_SEARCHARRAY))
00269 continue;
00270
00271
00272
00273
00274
00275
00276 arrayval = DatumGetArrayTypeP(cur->sk_argument);
00277
00278 get_typlenbyvalalign(ARR_ELEMTYPE(arrayval),
00279 &elmlen, &elmbyval, &elmalign);
00280 deconstruct_array(arrayval,
00281 ARR_ELEMTYPE(arrayval),
00282 elmlen, elmbyval, elmalign,
00283 &elem_values, &elem_nulls, &num_elems);
00284
00285
00286
00287
00288
00289 num_nonnulls = 0;
00290 for (j = 0; j < num_elems; j++)
00291 {
00292 if (!elem_nulls[j])
00293 elem_values[num_nonnulls++] = elem_values[j];
00294 }
00295
00296
00297
00298
00299 if (num_nonnulls == 0)
00300 {
00301 numArrayKeys = -1;
00302 break;
00303 }
00304
00305
00306
00307
00308
00309
00310 switch (cur->sk_strategy)
00311 {
00312 case BTLessStrategyNumber:
00313 case BTLessEqualStrategyNumber:
00314 cur->sk_argument =
00315 _bt_find_extreme_element(scan, cur,
00316 BTGreaterStrategyNumber,
00317 elem_values, num_nonnulls);
00318 continue;
00319 case BTEqualStrategyNumber:
00320
00321 break;
00322 case BTGreaterEqualStrategyNumber:
00323 case BTGreaterStrategyNumber:
00324 cur->sk_argument =
00325 _bt_find_extreme_element(scan, cur,
00326 BTLessStrategyNumber,
00327 elem_values, num_nonnulls);
00328 continue;
00329 default:
00330 elog(ERROR, "unrecognized StrategyNumber: %d",
00331 (int) cur->sk_strategy);
00332 break;
00333 }
00334
00335
00336
00337
00338
00339
00340 num_elems = _bt_sort_array_elements(scan, cur,
00341 (indoption[cur->sk_attno - 1] & INDOPTION_DESC) != 0,
00342 elem_values, num_nonnulls);
00343
00344
00345
00346
00347 so->arrayKeys[numArrayKeys].scan_key = i;
00348 so->arrayKeys[numArrayKeys].num_elems = num_elems;
00349 so->arrayKeys[numArrayKeys].elem_values = elem_values;
00350 numArrayKeys++;
00351 }
00352
00353 so->numArrayKeys = numArrayKeys;
00354
00355 MemoryContextSwitchTo(oldContext);
00356 }
00357
00358
00359
00360
00361
00362
00363
00364
00365 static Datum
00366 _bt_find_extreme_element(IndexScanDesc scan, ScanKey skey,
00367 StrategyNumber strat,
00368 Datum *elems, int nelems)
00369 {
00370 Relation rel = scan->indexRelation;
00371 Oid elemtype,
00372 cmp_op;
00373 RegProcedure cmp_proc;
00374 FmgrInfo flinfo;
00375 Datum result;
00376 int i;
00377
00378
00379
00380
00381
00382
00383 elemtype = skey->sk_subtype;
00384 if (elemtype == InvalidOid)
00385 elemtype = rel->rd_opcintype[skey->sk_attno - 1];
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395 cmp_op = get_opfamily_member(rel->rd_opfamily[skey->sk_attno - 1],
00396 elemtype,
00397 elemtype,
00398 strat);
00399 if (!OidIsValid(cmp_op))
00400 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
00401 strat, elemtype, elemtype,
00402 rel->rd_opfamily[skey->sk_attno - 1]);
00403 cmp_proc = get_opcode(cmp_op);
00404 if (!RegProcedureIsValid(cmp_proc))
00405 elog(ERROR, "missing oprcode for operator %u", cmp_op);
00406
00407 fmgr_info(cmp_proc, &flinfo);
00408
00409 Assert(nelems > 0);
00410 result = elems[0];
00411 for (i = 1; i < nelems; i++)
00412 {
00413 if (DatumGetBool(FunctionCall2Coll(&flinfo,
00414 skey->sk_collation,
00415 elems[i],
00416 result)))
00417 result = elems[i];
00418 }
00419
00420 return result;
00421 }
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432 static int
00433 _bt_sort_array_elements(IndexScanDesc scan, ScanKey skey,
00434 bool reverse,
00435 Datum *elems, int nelems)
00436 {
00437 Relation rel = scan->indexRelation;
00438 Oid elemtype;
00439 RegProcedure cmp_proc;
00440 BTSortArrayContext cxt;
00441 int last_non_dup;
00442 int i;
00443
00444 if (nelems <= 1)
00445 return nelems;
00446
00447
00448
00449
00450
00451
00452 elemtype = skey->sk_subtype;
00453 if (elemtype == InvalidOid)
00454 elemtype = rel->rd_opcintype[skey->sk_attno - 1];
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464 cmp_proc = get_opfamily_proc(rel->rd_opfamily[skey->sk_attno - 1],
00465 elemtype,
00466 elemtype,
00467 BTORDER_PROC);
00468 if (!RegProcedureIsValid(cmp_proc))
00469 elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
00470 BTORDER_PROC, elemtype, elemtype,
00471 rel->rd_opfamily[skey->sk_attno - 1]);
00472
00473
00474 fmgr_info(cmp_proc, &cxt.flinfo);
00475 cxt.collation = skey->sk_collation;
00476 cxt.reverse = reverse;
00477 qsort_arg((void *) elems, nelems, sizeof(Datum),
00478 _bt_compare_array_elements, (void *) &cxt);
00479
00480
00481 last_non_dup = 0;
00482 for (i = 1; i < nelems; i++)
00483 {
00484 int32 compare;
00485
00486 compare = DatumGetInt32(FunctionCall2Coll(&cxt.flinfo,
00487 cxt.collation,
00488 elems[last_non_dup],
00489 elems[i]));
00490 if (compare != 0)
00491 elems[++last_non_dup] = elems[i];
00492 }
00493
00494 return last_non_dup + 1;
00495 }
00496
00497
00498
00499
00500 static int
00501 _bt_compare_array_elements(const void *a, const void *b, void *arg)
00502 {
00503 Datum da = *((const Datum *) a);
00504 Datum db = *((const Datum *) b);
00505 BTSortArrayContext *cxt = (BTSortArrayContext *) arg;
00506 int32 compare;
00507
00508 compare = DatumGetInt32(FunctionCall2Coll(&cxt->flinfo,
00509 cxt->collation,
00510 da, db));
00511 if (cxt->reverse)
00512 compare = -compare;
00513 return compare;
00514 }
00515
00516
00517
00518
00519
00520
00521
00522 void
00523 _bt_start_array_keys(IndexScanDesc scan, ScanDirection dir)
00524 {
00525 BTScanOpaque so = (BTScanOpaque) scan->opaque;
00526 int i;
00527
00528 for (i = 0; i < so->numArrayKeys; i++)
00529 {
00530 BTArrayKeyInfo *curArrayKey = &so->arrayKeys[i];
00531 ScanKey skey = &so->arrayKeyData[curArrayKey->scan_key];
00532
00533 Assert(curArrayKey->num_elems > 0);
00534 if (ScanDirectionIsBackward(dir))
00535 curArrayKey->cur_elem = curArrayKey->num_elems - 1;
00536 else
00537 curArrayKey->cur_elem = 0;
00538 skey->sk_argument = curArrayKey->elem_values[curArrayKey->cur_elem];
00539 }
00540 }
00541
00542
00543
00544
00545
00546
00547
00548 bool
00549 _bt_advance_array_keys(IndexScanDesc scan, ScanDirection dir)
00550 {
00551 BTScanOpaque so = (BTScanOpaque) scan->opaque;
00552 bool found = false;
00553 int i;
00554
00555
00556
00557
00558
00559
00560
00561 for (i = so->numArrayKeys - 1; i >= 0; i--)
00562 {
00563 BTArrayKeyInfo *curArrayKey = &so->arrayKeys[i];
00564 ScanKey skey = &so->arrayKeyData[curArrayKey->scan_key];
00565 int cur_elem = curArrayKey->cur_elem;
00566 int num_elems = curArrayKey->num_elems;
00567
00568 if (ScanDirectionIsBackward(dir))
00569 {
00570 if (--cur_elem < 0)
00571 {
00572 cur_elem = num_elems - 1;
00573 found = false;
00574 }
00575 else
00576 found = true;
00577 }
00578 else
00579 {
00580 if (++cur_elem >= num_elems)
00581 {
00582 cur_elem = 0;
00583 found = false;
00584 }
00585 else
00586 found = true;
00587 }
00588
00589 curArrayKey->cur_elem = cur_elem;
00590 skey->sk_argument = curArrayKey->elem_values[cur_elem];
00591 if (found)
00592 break;
00593 }
00594
00595 return found;
00596 }
00597
00598
00599
00600
00601
00602
00603 void
00604 _bt_mark_array_keys(IndexScanDesc scan)
00605 {
00606 BTScanOpaque so = (BTScanOpaque) scan->opaque;
00607 int i;
00608
00609 for (i = 0; i < so->numArrayKeys; i++)
00610 {
00611 BTArrayKeyInfo *curArrayKey = &so->arrayKeys[i];
00612
00613 curArrayKey->mark_elem = curArrayKey->cur_elem;
00614 }
00615 }
00616
00617
00618
00619
00620
00621
00622 void
00623 _bt_restore_array_keys(IndexScanDesc scan)
00624 {
00625 BTScanOpaque so = (BTScanOpaque) scan->opaque;
00626 bool changed = false;
00627 int i;
00628
00629
00630 for (i = 0; i < so->numArrayKeys; i++)
00631 {
00632 BTArrayKeyInfo *curArrayKey = &so->arrayKeys[i];
00633 ScanKey skey = &so->arrayKeyData[curArrayKey->scan_key];
00634 int mark_elem = curArrayKey->mark_elem;
00635
00636 if (curArrayKey->cur_elem != mark_elem)
00637 {
00638 curArrayKey->cur_elem = mark_elem;
00639 skey->sk_argument = curArrayKey->elem_values[mark_elem];
00640 changed = true;
00641 }
00642 }
00643
00644
00645
00646
00647
00648
00649 if (changed)
00650 {
00651 _bt_preprocess_keys(scan);
00652
00653 Assert(so->qual_ok);
00654 }
00655 }
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742 void
00743 _bt_preprocess_keys(IndexScanDesc scan)
00744 {
00745 BTScanOpaque so = (BTScanOpaque) scan->opaque;
00746 int numberOfKeys = scan->numberOfKeys;
00747 int16 *indoption = scan->indexRelation->rd_indoption;
00748 int new_numberOfKeys;
00749 int numberOfEqualCols;
00750 ScanKey inkeys;
00751 ScanKey outkeys;
00752 ScanKey cur;
00753 ScanKey xform[BTMaxStrategyNumber];
00754 bool test_result;
00755 int i,
00756 j;
00757 AttrNumber attno;
00758
00759
00760 so->qual_ok = true;
00761 so->numberOfKeys = 0;
00762
00763 if (numberOfKeys < 1)
00764 return;
00765
00766
00767
00768
00769 if (so->arrayKeyData != NULL)
00770 inkeys = so->arrayKeyData;
00771 else
00772 inkeys = scan->keyData;
00773
00774 outkeys = so->keyData;
00775 cur = &inkeys[0];
00776
00777 if (cur->sk_attno < 1)
00778 elog(ERROR, "btree index keys must be ordered by attribute");
00779
00780
00781 if (numberOfKeys == 1)
00782 {
00783
00784 if (!_bt_fix_scankey_strategy(cur, indoption))
00785 so->qual_ok = false;
00786 memcpy(outkeys, cur, sizeof(ScanKeyData));
00787 so->numberOfKeys = 1;
00788
00789 if (cur->sk_attno == 1)
00790 _bt_mark_scankey_required(outkeys);
00791 return;
00792 }
00793
00794
00795
00796
00797 new_numberOfKeys = 0;
00798 numberOfEqualCols = 0;
00799
00800
00801
00802
00803
00804
00805
00806 attno = 1;
00807 memset(xform, 0, sizeof(xform));
00808
00809
00810
00811
00812
00813
00814 for (i = 0;; cur++, i++)
00815 {
00816 if (i < numberOfKeys)
00817 {
00818
00819 if (!_bt_fix_scankey_strategy(cur, indoption))
00820 {
00821
00822 so->qual_ok = false;
00823 return;
00824 }
00825 }
00826
00827
00828
00829
00830
00831 if (i == numberOfKeys || cur->sk_attno != attno)
00832 {
00833 int priorNumberOfEqualCols = numberOfEqualCols;
00834
00835
00836 if (i < numberOfKeys && cur->sk_attno < attno)
00837 elog(ERROR, "btree index keys must be ordered by attribute");
00838
00839
00840
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850 if (xform[BTEqualStrategyNumber - 1])
00851 {
00852 ScanKey eq = xform[BTEqualStrategyNumber - 1];
00853
00854 for (j = BTMaxStrategyNumber; --j >= 0;)
00855 {
00856 ScanKey chk = xform[j];
00857
00858 if (!chk || j == (BTEqualStrategyNumber - 1))
00859 continue;
00860
00861 if (eq->sk_flags & SK_SEARCHNULL)
00862 {
00863
00864 so->qual_ok = false;
00865 return;
00866 }
00867
00868 if (_bt_compare_scankey_args(scan, chk, eq, chk,
00869 &test_result))
00870 {
00871 if (!test_result)
00872 {
00873
00874 so->qual_ok = false;
00875 return;
00876 }
00877
00878 xform[j] = NULL;
00879 }
00880
00881 }
00882
00883 numberOfEqualCols++;
00884 }
00885
00886
00887 if (xform[BTLessStrategyNumber - 1]
00888 && xform[BTLessEqualStrategyNumber - 1])
00889 {
00890 ScanKey lt = xform[BTLessStrategyNumber - 1];
00891 ScanKey le = xform[BTLessEqualStrategyNumber - 1];
00892
00893 if (_bt_compare_scankey_args(scan, le, lt, le,
00894 &test_result))
00895 {
00896 if (test_result)
00897 xform[BTLessEqualStrategyNumber - 1] = NULL;
00898 else
00899 xform[BTLessStrategyNumber - 1] = NULL;
00900 }
00901 }
00902
00903
00904 if (xform[BTGreaterStrategyNumber - 1]
00905 && xform[BTGreaterEqualStrategyNumber - 1])
00906 {
00907 ScanKey gt = xform[BTGreaterStrategyNumber - 1];
00908 ScanKey ge = xform[BTGreaterEqualStrategyNumber - 1];
00909
00910 if (_bt_compare_scankey_args(scan, ge, gt, ge,
00911 &test_result))
00912 {
00913 if (test_result)
00914 xform[BTGreaterEqualStrategyNumber - 1] = NULL;
00915 else
00916 xform[BTGreaterStrategyNumber - 1] = NULL;
00917 }
00918 }
00919
00920
00921
00922
00923
00924
00925 for (j = BTMaxStrategyNumber; --j >= 0;)
00926 {
00927 if (xform[j])
00928 {
00929 ScanKey outkey = &outkeys[new_numberOfKeys++];
00930
00931 memcpy(outkey, xform[j], sizeof(ScanKeyData));
00932 if (priorNumberOfEqualCols == attno - 1)
00933 _bt_mark_scankey_required(outkey);
00934 }
00935 }
00936
00937
00938
00939
00940 if (i == numberOfKeys)
00941 break;
00942
00943
00944 attno = cur->sk_attno;
00945 memset(xform, 0, sizeof(xform));
00946 }
00947
00948
00949 j = cur->sk_strategy - 1;
00950
00951
00952 if (cur->sk_flags & SK_ROW_HEADER)
00953 {
00954 ScanKey outkey = &outkeys[new_numberOfKeys++];
00955
00956 memcpy(outkey, cur, sizeof(ScanKeyData));
00957 if (numberOfEqualCols == attno - 1)
00958 _bt_mark_scankey_required(outkey);
00959
00960
00961
00962
00963
00964 Assert(j != (BTEqualStrategyNumber - 1));
00965 continue;
00966 }
00967
00968
00969 if (xform[j] == NULL)
00970 {
00971
00972 xform[j] = cur;
00973 }
00974 else
00975 {
00976
00977 if (_bt_compare_scankey_args(scan, cur, cur, xform[j],
00978 &test_result))
00979 {
00980 if (test_result)
00981 xform[j] = cur;
00982 else if (j == (BTEqualStrategyNumber - 1))
00983 {
00984
00985 so->qual_ok = false;
00986 return;
00987 }
00988
00989 }
00990 else
00991 {
00992
00993
00994
00995
00996
00997 ScanKey outkey = &outkeys[new_numberOfKeys++];
00998
00999 memcpy(outkey, cur, sizeof(ScanKeyData));
01000 if (numberOfEqualCols == attno - 1)
01001 _bt_mark_scankey_required(outkey);
01002 }
01003 }
01004 }
01005
01006 so->numberOfKeys = new_numberOfKeys;
01007 }
01008
01009
01010
01011
01012
01013
01014
01015
01016
01017
01018
01019
01020
01021
01022
01023
01024
01025
01026
01027
01028
01029
01030
01031
01032 static bool
01033 _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op,
01034 ScanKey leftarg, ScanKey rightarg,
01035 bool *result)
01036 {
01037 Relation rel = scan->indexRelation;
01038 Oid lefttype,
01039 righttype,
01040 optype,
01041 opcintype,
01042 cmp_op;
01043 StrategyNumber strat;
01044
01045
01046
01047
01048
01049 if ((leftarg->sk_flags | rightarg->sk_flags) & SK_ISNULL)
01050 {
01051 bool leftnull,
01052 rightnull;
01053
01054 if (leftarg->sk_flags & SK_ISNULL)
01055 {
01056 Assert(leftarg->sk_flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL));
01057 leftnull = true;
01058 }
01059 else
01060 leftnull = false;
01061 if (rightarg->sk_flags & SK_ISNULL)
01062 {
01063 Assert(rightarg->sk_flags & (SK_SEARCHNULL | SK_SEARCHNOTNULL));
01064 rightnull = true;
01065 }
01066 else
01067 rightnull = false;
01068
01069
01070
01071
01072
01073
01074 strat = op->sk_strategy;
01075 if (op->sk_flags & SK_BT_NULLS_FIRST)
01076 strat = BTCommuteStrategyNumber(strat);
01077
01078 switch (strat)
01079 {
01080 case BTLessStrategyNumber:
01081 *result = (leftnull < rightnull);
01082 break;
01083 case BTLessEqualStrategyNumber:
01084 *result = (leftnull <= rightnull);
01085 break;
01086 case BTEqualStrategyNumber:
01087 *result = (leftnull == rightnull);
01088 break;
01089 case BTGreaterEqualStrategyNumber:
01090 *result = (leftnull >= rightnull);
01091 break;
01092 case BTGreaterStrategyNumber:
01093 *result = (leftnull > rightnull);
01094 break;
01095 default:
01096 elog(ERROR, "unrecognized StrategyNumber: %d", (int) strat);
01097 *result = false;
01098 break;
01099 }
01100 return true;
01101 }
01102
01103
01104
01105
01106 Assert(leftarg->sk_attno == rightarg->sk_attno);
01107
01108 opcintype = rel->rd_opcintype[leftarg->sk_attno - 1];
01109
01110
01111
01112
01113
01114
01115 lefttype = leftarg->sk_subtype;
01116 if (lefttype == InvalidOid)
01117 lefttype = opcintype;
01118 righttype = rightarg->sk_subtype;
01119 if (righttype == InvalidOid)
01120 righttype = opcintype;
01121 optype = op->sk_subtype;
01122 if (optype == InvalidOid)
01123 optype = opcintype;
01124
01125
01126
01127
01128
01129 if (lefttype == opcintype && righttype == optype)
01130 {
01131 *result = DatumGetBool(FunctionCall2Coll(&op->sk_func,
01132 op->sk_collation,
01133 leftarg->sk_argument,
01134 rightarg->sk_argument));
01135 return true;
01136 }
01137
01138
01139
01140
01141
01142
01143
01144
01145
01146
01147 strat = op->sk_strategy;
01148 if (op->sk_flags & SK_BT_DESC)
01149 strat = BTCommuteStrategyNumber(strat);
01150
01151 cmp_op = get_opfamily_member(rel->rd_opfamily[leftarg->sk_attno - 1],
01152 lefttype,
01153 righttype,
01154 strat);
01155 if (OidIsValid(cmp_op))
01156 {
01157 RegProcedure cmp_proc = get_opcode(cmp_op);
01158
01159 if (RegProcedureIsValid(cmp_proc))
01160 {
01161 *result = DatumGetBool(OidFunctionCall2Coll(cmp_proc,
01162 op->sk_collation,
01163 leftarg->sk_argument,
01164 rightarg->sk_argument));
01165 return true;
01166 }
01167 }
01168
01169
01170 *result = false;
01171 return false;
01172 }
01173
01174
01175
01176
01177
01178
01179
01180
01181
01182
01183
01184
01185
01186
01187
01188
01189
01190
01191
01192
01193
01194
01195
01196 static bool
01197 _bt_fix_scankey_strategy(ScanKey skey, int16 *indoption)
01198 {
01199 int addflags;
01200
01201 addflags = indoption[skey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT;
01202
01203
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218
01219
01220
01221
01222
01223 if (skey->sk_flags & SK_ISNULL)
01224 {
01225
01226 Assert(!(skey->sk_flags & SK_ROW_HEADER));
01227
01228
01229 skey->sk_flags |= addflags;
01230
01231
01232 if (skey->sk_flags & SK_SEARCHNULL)
01233 {
01234 skey->sk_strategy = BTEqualStrategyNumber;
01235 skey->sk_subtype = InvalidOid;
01236 skey->sk_collation = InvalidOid;
01237 }
01238 else if (skey->sk_flags & SK_SEARCHNOTNULL)
01239 {
01240 if (skey->sk_flags & SK_BT_NULLS_FIRST)
01241 skey->sk_strategy = BTGreaterStrategyNumber;
01242 else
01243 skey->sk_strategy = BTLessStrategyNumber;
01244 skey->sk_subtype = InvalidOid;
01245 skey->sk_collation = InvalidOid;
01246 }
01247 else
01248 {
01249
01250 return false;
01251 }
01252
01253
01254 return true;
01255 }
01256
01257
01258 if ((addflags & SK_BT_DESC) && !(skey->sk_flags & SK_BT_DESC))
01259 skey->sk_strategy = BTCommuteStrategyNumber(skey->sk_strategy);
01260 skey->sk_flags |= addflags;
01261
01262
01263 if (skey->sk_flags & SK_ROW_HEADER)
01264 {
01265 ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument);
01266
01267 for (;;)
01268 {
01269 Assert(subkey->sk_flags & SK_ROW_MEMBER);
01270 addflags = indoption[subkey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT;
01271 if ((addflags & SK_BT_DESC) && !(subkey->sk_flags & SK_BT_DESC))
01272 subkey->sk_strategy = BTCommuteStrategyNumber(subkey->sk_strategy);
01273 subkey->sk_flags |= addflags;
01274 if (subkey->sk_flags & SK_ROW_END)
01275 break;
01276 subkey++;
01277 }
01278 }
01279
01280 return true;
01281 }
01282
01283
01284
01285
01286
01287
01288
01289
01290
01291
01292
01293
01294
01295
01296
01297
01298
01299
01300
01301 static void
01302 _bt_mark_scankey_required(ScanKey skey)
01303 {
01304 int addflags;
01305
01306 switch (skey->sk_strategy)
01307 {
01308 case BTLessStrategyNumber:
01309 case BTLessEqualStrategyNumber:
01310 addflags = SK_BT_REQFWD;
01311 break;
01312 case BTEqualStrategyNumber:
01313 addflags = SK_BT_REQFWD | SK_BT_REQBKWD;
01314 break;
01315 case BTGreaterEqualStrategyNumber:
01316 case BTGreaterStrategyNumber:
01317 addflags = SK_BT_REQBKWD;
01318 break;
01319 default:
01320 elog(ERROR, "unrecognized StrategyNumber: %d",
01321 (int) skey->sk_strategy);
01322 addflags = 0;
01323 break;
01324 }
01325
01326 skey->sk_flags |= addflags;
01327
01328 if (skey->sk_flags & SK_ROW_HEADER)
01329 {
01330 ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument);
01331 AttrNumber attno = skey->sk_attno;
01332
01333
01334 Assert(subkey->sk_attno == attno);
01335
01336 for (;;)
01337 {
01338 Assert(subkey->sk_flags & SK_ROW_MEMBER);
01339 if (subkey->sk_attno != attno)
01340 break;
01341 if (subkey->sk_strategy != skey->sk_strategy)
01342 break;
01343 subkey->sk_flags |= addflags;
01344 if (subkey->sk_flags & SK_ROW_END)
01345 break;
01346 subkey++;
01347 attno++;
01348 }
01349 }
01350 }
01351
01352
01353
01354
01355
01356
01357
01358
01359
01360
01361
01362
01363
01364
01365
01366
01367
01368
01369
01370
01371 IndexTuple
01372 _bt_checkkeys(IndexScanDesc scan,
01373 Page page, OffsetNumber offnum,
01374 ScanDirection dir, bool *continuescan)
01375 {
01376 ItemId iid = PageGetItemId(page, offnum);
01377 bool tuple_alive;
01378 IndexTuple tuple;
01379 TupleDesc tupdesc;
01380 BTScanOpaque so;
01381 int keysz;
01382 int ikey;
01383 ScanKey key;
01384
01385 *continuescan = true;
01386
01387
01388
01389
01390
01391
01392
01393
01394
01395 if (scan->ignore_killed_tuples && ItemIdIsDead(iid))
01396 {
01397
01398 if (ScanDirectionIsForward(dir))
01399 {
01400 if (offnum < PageGetMaxOffsetNumber(page))
01401 return NULL;
01402 }
01403 else
01404 {
01405 BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page);
01406
01407 if (offnum > P_FIRSTDATAKEY(opaque))
01408 return NULL;
01409 }
01410
01411
01412
01413
01414
01415 tuple_alive = false;
01416 }
01417 else
01418 tuple_alive = true;
01419
01420 tuple = (IndexTuple) PageGetItem(page, iid);
01421
01422 tupdesc = RelationGetDescr(scan->indexRelation);
01423 so = (BTScanOpaque) scan->opaque;
01424 keysz = so->numberOfKeys;
01425
01426 for (key = so->keyData, ikey = 0; ikey < keysz; key++, ikey++)
01427 {
01428 Datum datum;
01429 bool isNull;
01430 Datum test;
01431
01432
01433 if (key->sk_flags & SK_ROW_HEADER)
01434 {
01435 if (_bt_check_rowcompare(key, tuple, tupdesc, dir, continuescan))
01436 continue;
01437 return NULL;
01438 }
01439
01440 datum = index_getattr(tuple,
01441 key->sk_attno,
01442 tupdesc,
01443 &isNull);
01444
01445 if (key->sk_flags & SK_ISNULL)
01446 {
01447
01448 if (key->sk_flags & SK_SEARCHNULL)
01449 {
01450 if (isNull)
01451 continue;
01452 }
01453 else
01454 {
01455 Assert(key->sk_flags & SK_SEARCHNOTNULL);
01456 if (!isNull)
01457 continue;
01458 }
01459
01460
01461
01462
01463
01464
01465 if ((key->sk_flags & SK_BT_REQFWD) &&
01466 ScanDirectionIsForward(dir))
01467 *continuescan = false;
01468 else if ((key->sk_flags & SK_BT_REQBKWD) &&
01469 ScanDirectionIsBackward(dir))
01470 *continuescan = false;
01471
01472
01473
01474
01475 return NULL;
01476 }
01477
01478 if (isNull)
01479 {
01480 if (key->sk_flags & SK_BT_NULLS_FIRST)
01481 {
01482
01483
01484
01485
01486
01487
01488
01489
01490
01491
01492 if ((key->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)) &&
01493 ScanDirectionIsBackward(dir))
01494 *continuescan = false;
01495 }
01496 else
01497 {
01498
01499
01500
01501
01502
01503
01504
01505
01506
01507
01508 if ((key->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)) &&
01509 ScanDirectionIsForward(dir))
01510 *continuescan = false;
01511 }
01512
01513
01514
01515
01516 return NULL;
01517 }
01518
01519 test = FunctionCall2Coll(&key->sk_func, key->sk_collation,
01520 datum, key->sk_argument);
01521
01522 if (!DatumGetBool(test))
01523 {
01524
01525
01526
01527
01528
01529
01530
01531
01532
01533
01534 if ((key->sk_flags & SK_BT_REQFWD) &&
01535 ScanDirectionIsForward(dir))
01536 *continuescan = false;
01537 else if ((key->sk_flags & SK_BT_REQBKWD) &&
01538 ScanDirectionIsBackward(dir))
01539 *continuescan = false;
01540
01541
01542
01543
01544 return NULL;
01545 }
01546 }
01547
01548
01549 if (!tuple_alive)
01550 return NULL;
01551
01552
01553 return tuple;
01554 }
01555
01556
01557
01558
01559
01560
01561
01562
01563
01564
01565 static bool
01566 _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, TupleDesc tupdesc,
01567 ScanDirection dir, bool *continuescan)
01568 {
01569 ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument);
01570 int32 cmpresult = 0;
01571 bool result;
01572
01573
01574 Assert(subkey->sk_attno == skey->sk_attno);
01575
01576
01577 for (;;)
01578 {
01579 Datum datum;
01580 bool isNull;
01581
01582 Assert(subkey->sk_flags & SK_ROW_MEMBER);
01583
01584 datum = index_getattr(tuple,
01585 subkey->sk_attno,
01586 tupdesc,
01587 &isNull);
01588
01589 if (isNull)
01590 {
01591 if (subkey->sk_flags & SK_BT_NULLS_FIRST)
01592 {
01593
01594
01595
01596
01597
01598
01599
01600
01601
01602
01603 if ((subkey->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)) &&
01604 ScanDirectionIsBackward(dir))
01605 *continuescan = false;
01606 }
01607 else
01608 {
01609
01610
01611
01612
01613
01614
01615
01616
01617
01618
01619 if ((subkey->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD)) &&
01620 ScanDirectionIsForward(dir))
01621 *continuescan = false;
01622 }
01623
01624
01625
01626
01627 return false;
01628 }
01629
01630 if (subkey->sk_flags & SK_ISNULL)
01631 {
01632
01633
01634
01635
01636
01637
01638 if (subkey != (ScanKey) DatumGetPointer(skey->sk_argument))
01639 subkey--;
01640 if ((subkey->sk_flags & SK_BT_REQFWD) &&
01641 ScanDirectionIsForward(dir))
01642 *continuescan = false;
01643 else if ((subkey->sk_flags & SK_BT_REQBKWD) &&
01644 ScanDirectionIsBackward(dir))
01645 *continuescan = false;
01646 return false;
01647 }
01648
01649
01650 cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func,
01651 subkey->sk_collation,
01652 datum,
01653 subkey->sk_argument));
01654
01655 if (subkey->sk_flags & SK_BT_DESC)
01656 cmpresult = -cmpresult;
01657
01658
01659 if (cmpresult != 0)
01660 break;
01661
01662 if (subkey->sk_flags & SK_ROW_END)
01663 break;
01664 subkey++;
01665 }
01666
01667
01668
01669
01670
01671
01672 switch (subkey->sk_strategy)
01673 {
01674
01675 case BTLessStrategyNumber:
01676 result = (cmpresult < 0);
01677 break;
01678 case BTLessEqualStrategyNumber:
01679 result = (cmpresult <= 0);
01680 break;
01681 case BTGreaterEqualStrategyNumber:
01682 result = (cmpresult >= 0);
01683 break;
01684 case BTGreaterStrategyNumber:
01685 result = (cmpresult > 0);
01686 break;
01687 default:
01688 elog(ERROR, "unrecognized RowCompareType: %d",
01689 (int) subkey->sk_strategy);
01690 result = 0;
01691 break;
01692 }
01693
01694 if (!result)
01695 {
01696
01697
01698
01699
01700
01701
01702 if ((subkey->sk_flags & SK_BT_REQFWD) &&
01703 ScanDirectionIsForward(dir))
01704 *continuescan = false;
01705 else if ((subkey->sk_flags & SK_BT_REQBKWD) &&
01706 ScanDirectionIsBackward(dir))
01707 *continuescan = false;
01708 }
01709
01710 return result;
01711 }
01712
01713
01714
01715
01716
01717
01718
01719
01720
01721
01722
01723
01724
01725
01726
01727
01728
01729
01730
01731
01732
01733
01734
01735
01736 void
01737 _bt_killitems(IndexScanDesc scan, bool haveLock)
01738 {
01739 BTScanOpaque so = (BTScanOpaque) scan->opaque;
01740 Page page;
01741 BTPageOpaque opaque;
01742 OffsetNumber minoff;
01743 OffsetNumber maxoff;
01744 int i;
01745 bool killedsomething = false;
01746
01747 Assert(BufferIsValid(so->currPos.buf));
01748
01749 if (!haveLock)
01750 LockBuffer(so->currPos.buf, BT_READ);
01751
01752 page = BufferGetPage(so->currPos.buf);
01753 opaque = (BTPageOpaque) PageGetSpecialPointer(page);
01754 minoff = P_FIRSTDATAKEY(opaque);
01755 maxoff = PageGetMaxOffsetNumber(page);
01756
01757 for (i = 0; i < so->numKilled; i++)
01758 {
01759 int itemIndex = so->killedItems[i];
01760 BTScanPosItem *kitem = &so->currPos.items[itemIndex];
01761 OffsetNumber offnum = kitem->indexOffset;
01762
01763 Assert(itemIndex >= so->currPos.firstItem &&
01764 itemIndex <= so->currPos.lastItem);
01765 if (offnum < minoff)
01766 continue;
01767 while (offnum <= maxoff)
01768 {
01769 ItemId iid = PageGetItemId(page, offnum);
01770 IndexTuple ituple = (IndexTuple) PageGetItem(page, iid);
01771
01772 if (ItemPointerEquals(&ituple->t_tid, &kitem->heapTid))
01773 {
01774
01775 ItemIdMarkDead(iid);
01776 killedsomething = true;
01777 break;
01778 }
01779 offnum = OffsetNumberNext(offnum);
01780 }
01781 }
01782
01783
01784
01785
01786
01787
01788
01789 if (killedsomething)
01790 {
01791 opaque->btpo_flags |= BTP_HAS_GARBAGE;
01792 MarkBufferDirtyHint(so->currPos.buf);
01793 }
01794
01795 if (!haveLock)
01796 LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
01797
01798
01799
01800
01801
01802 so->numKilled = 0;
01803 }
01804
01805
01806
01807
01808
01809
01810
01811
01812
01813
01814
01815
01816
01817
01818
01819
01820
01821 typedef struct BTOneVacInfo
01822 {
01823 LockRelId relid;
01824 BTCycleId cycleid;
01825 } BTOneVacInfo;
01826
01827 typedef struct BTVacInfo
01828 {
01829 BTCycleId cycle_ctr;
01830 int num_vacuums;
01831 int max_vacuums;
01832 BTOneVacInfo vacuums[1];
01833 } BTVacInfo;
01834
01835 static BTVacInfo *btvacinfo;
01836
01837
01838
01839
01840
01841
01842
01843
01844
01845
01846
01847 BTCycleId
01848 _bt_vacuum_cycleid(Relation rel)
01849 {
01850 BTCycleId result = 0;
01851 int i;
01852
01853
01854 LWLockAcquire(BtreeVacuumLock, LW_SHARED);
01855
01856 for (i = 0; i < btvacinfo->num_vacuums; i++)
01857 {
01858 BTOneVacInfo *vac = &btvacinfo->vacuums[i];
01859
01860 if (vac->relid.relId == rel->rd_lockInfo.lockRelId.relId &&
01861 vac->relid.dbId == rel->rd_lockInfo.lockRelId.dbId)
01862 {
01863 result = vac->cycleid;
01864 break;
01865 }
01866 }
01867
01868 LWLockRelease(BtreeVacuumLock);
01869 return result;
01870 }
01871
01872
01873
01874
01875
01876
01877
01878
01879
01880
01881 BTCycleId
01882 _bt_start_vacuum(Relation rel)
01883 {
01884 BTCycleId result;
01885 int i;
01886 BTOneVacInfo *vac;
01887
01888 LWLockAcquire(BtreeVacuumLock, LW_EXCLUSIVE);
01889
01890
01891
01892
01893
01894 result = ++(btvacinfo->cycle_ctr);
01895 if (result == 0 || result > MAX_BT_CYCLE_ID)
01896 result = btvacinfo->cycle_ctr = 1;
01897
01898
01899 for (i = 0; i < btvacinfo->num_vacuums; i++)
01900 {
01901 vac = &btvacinfo->vacuums[i];
01902 if (vac->relid.relId == rel->rd_lockInfo.lockRelId.relId &&
01903 vac->relid.dbId == rel->rd_lockInfo.lockRelId.dbId)
01904 {
01905
01906
01907
01908
01909
01910
01911 LWLockRelease(BtreeVacuumLock);
01912 elog(ERROR, "multiple active vacuums for index \"%s\"",
01913 RelationGetRelationName(rel));
01914 }
01915 }
01916
01917
01918 if (btvacinfo->num_vacuums >= btvacinfo->max_vacuums)
01919 {
01920 LWLockRelease(BtreeVacuumLock);
01921 elog(ERROR, "out of btvacinfo slots");
01922 }
01923 vac = &btvacinfo->vacuums[btvacinfo->num_vacuums];
01924 vac->relid = rel->rd_lockInfo.lockRelId;
01925 vac->cycleid = result;
01926 btvacinfo->num_vacuums++;
01927
01928 LWLockRelease(BtreeVacuumLock);
01929 return result;
01930 }
01931
01932
01933
01934
01935
01936
01937
01938 void
01939 _bt_end_vacuum(Relation rel)
01940 {
01941 int i;
01942
01943 LWLockAcquire(BtreeVacuumLock, LW_EXCLUSIVE);
01944
01945
01946 for (i = 0; i < btvacinfo->num_vacuums; i++)
01947 {
01948 BTOneVacInfo *vac = &btvacinfo->vacuums[i];
01949
01950 if (vac->relid.relId == rel->rd_lockInfo.lockRelId.relId &&
01951 vac->relid.dbId == rel->rd_lockInfo.lockRelId.dbId)
01952 {
01953
01954 *vac = btvacinfo->vacuums[btvacinfo->num_vacuums - 1];
01955 btvacinfo->num_vacuums--;
01956 break;
01957 }
01958 }
01959
01960 LWLockRelease(BtreeVacuumLock);
01961 }
01962
01963
01964
01965
01966 void
01967 _bt_end_vacuum_callback(int code, Datum arg)
01968 {
01969 _bt_end_vacuum((Relation) DatumGetPointer(arg));
01970 }
01971
01972
01973
01974
01975 Size
01976 BTreeShmemSize(void)
01977 {
01978 Size size;
01979
01980 size = offsetof(BTVacInfo, vacuums[0]);
01981 size = add_size(size, mul_size(MaxBackends, sizeof(BTOneVacInfo)));
01982 return size;
01983 }
01984
01985
01986
01987
01988 void
01989 BTreeShmemInit(void)
01990 {
01991 bool found;
01992
01993 btvacinfo = (BTVacInfo *) ShmemInitStruct("BTree Vacuum State",
01994 BTreeShmemSize(),
01995 &found);
01996
01997 if (!IsUnderPostmaster)
01998 {
01999
02000 Assert(!found);
02001
02002
02003
02004
02005
02006
02007 btvacinfo->cycle_ctr = (BTCycleId) time(NULL);
02008
02009 btvacinfo->num_vacuums = 0;
02010 btvacinfo->max_vacuums = MaxBackends;
02011 }
02012 else
02013 Assert(found);
02014 }
02015
02016 Datum
02017 btoptions(PG_FUNCTION_ARGS)
02018 {
02019 Datum reloptions = PG_GETARG_DATUM(0);
02020 bool validate = PG_GETARG_BOOL(1);
02021 bytea *result;
02022
02023 result = default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);
02024 if (result)
02025 PG_RETURN_BYTEA_P(result);
02026 PG_RETURN_NULL();
02027 }