00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "postgres.h"
00016
00017 #include "access/hash.h"
00018 #include "access/relscan.h"
00019 #include "miscadmin.h"
00020 #include "pgstat.h"
00021 #include "utils/rel.h"
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 bool
00034 _hash_next(IndexScanDesc scan, ScanDirection dir)
00035 {
00036 Relation rel = scan->indexRelation;
00037 HashScanOpaque so = (HashScanOpaque) scan->opaque;
00038 Buffer buf;
00039 Page page;
00040 OffsetNumber offnum;
00041 ItemPointer current;
00042 IndexTuple itup;
00043
00044
00045 buf = so->hashso_curbuf;
00046 Assert(BufferIsValid(buf));
00047
00048
00049
00050
00051 if (!_hash_step(scan, &buf, dir))
00052 return false;
00053
00054
00055 current = &(so->hashso_curpos);
00056 offnum = ItemPointerGetOffsetNumber(current);
00057 _hash_checkpage(rel, buf, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
00058 page = BufferGetPage(buf);
00059 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
00060 so->hashso_heappos = itup->t_tid;
00061
00062 return true;
00063 }
00064
00065
00066
00067
00068 static void
00069 _hash_readnext(Relation rel,
00070 Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
00071 {
00072 BlockNumber blkno;
00073
00074 blkno = (*opaquep)->hasho_nextblkno;
00075 _hash_relbuf(rel, *bufp);
00076 *bufp = InvalidBuffer;
00077
00078 CHECK_FOR_INTERRUPTS();
00079 if (BlockNumberIsValid(blkno))
00080 {
00081 *bufp = _hash_getbuf(rel, blkno, HASH_READ, LH_OVERFLOW_PAGE);
00082 *pagep = BufferGetPage(*bufp);
00083 *opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
00084 }
00085 }
00086
00087
00088
00089
00090 static void
00091 _hash_readprev(Relation rel,
00092 Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
00093 {
00094 BlockNumber blkno;
00095
00096 blkno = (*opaquep)->hasho_prevblkno;
00097 _hash_relbuf(rel, *bufp);
00098 *bufp = InvalidBuffer;
00099
00100 CHECK_FOR_INTERRUPTS();
00101 if (BlockNumberIsValid(blkno))
00102 {
00103 *bufp = _hash_getbuf(rel, blkno, HASH_READ,
00104 LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
00105 *pagep = BufferGetPage(*bufp);
00106 *opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
00107 }
00108 }
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119 bool
00120 _hash_first(IndexScanDesc scan, ScanDirection dir)
00121 {
00122 Relation rel = scan->indexRelation;
00123 HashScanOpaque so = (HashScanOpaque) scan->opaque;
00124 ScanKey cur;
00125 uint32 hashkey;
00126 Bucket bucket;
00127 BlockNumber blkno;
00128 BlockNumber oldblkno = InvalidBuffer;
00129 bool retry = false;
00130 Buffer buf;
00131 Buffer metabuf;
00132 Page page;
00133 HashPageOpaque opaque;
00134 HashMetaPage metap;
00135 IndexTuple itup;
00136 ItemPointer current;
00137 OffsetNumber offnum;
00138
00139 pgstat_count_index_scan(rel);
00140
00141 current = &(so->hashso_curpos);
00142 ItemPointerSetInvalid(current);
00143
00144
00145
00146
00147
00148
00149
00150 if (scan->numberOfKeys < 1)
00151 ereport(ERROR,
00152 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00153 errmsg("hash indexes do not support whole-index scans")));
00154
00155
00156 cur = &scan->keyData[0];
00157
00158
00159 Assert(cur->sk_attno == 1);
00160
00161 Assert(cur->sk_strategy == HTEqualStrategyNumber);
00162
00163
00164
00165
00166
00167 if (cur->sk_flags & SK_ISNULL)
00168 return false;
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180 if (cur->sk_subtype == rel->rd_opcintype[0] ||
00181 cur->sk_subtype == InvalidOid)
00182 hashkey = _hash_datum2hashkey(rel, cur->sk_argument);
00183 else
00184 hashkey = _hash_datum2hashkey_type(rel, cur->sk_argument,
00185 cur->sk_subtype);
00186
00187 so->hashso_sk_hash = hashkey;
00188
00189
00190 metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
00191 metap = HashPageGetMeta(BufferGetPage(metabuf));
00192
00193
00194
00195
00196 for (;;)
00197 {
00198
00199
00200
00201 bucket = _hash_hashkey2bucket(hashkey,
00202 metap->hashm_maxbucket,
00203 metap->hashm_highmask,
00204 metap->hashm_lowmask);
00205
00206 blkno = BUCKET_TO_BLKNO(metap, bucket);
00207
00208
00209 _hash_chgbufaccess(rel, metabuf, HASH_READ, HASH_NOLOCK);
00210
00211
00212
00213
00214
00215
00216 if (retry)
00217 {
00218 if (oldblkno == blkno)
00219 break;
00220 _hash_droplock(rel, oldblkno, HASH_SHARE);
00221 }
00222 _hash_getlock(rel, blkno, HASH_SHARE);
00223
00224
00225
00226
00227
00228 _hash_chgbufaccess(rel, metabuf, HASH_NOLOCK, HASH_READ);
00229 oldblkno = blkno;
00230 retry = true;
00231 }
00232
00233
00234 _hash_dropbuf(rel, metabuf);
00235
00236
00237 so->hashso_bucket = bucket;
00238 so->hashso_bucket_valid = true;
00239 so->hashso_bucket_blkno = blkno;
00240
00241
00242 buf = _hash_getbuf(rel, blkno, HASH_READ, LH_BUCKET_PAGE);
00243 page = BufferGetPage(buf);
00244 opaque = (HashPageOpaque) PageGetSpecialPointer(page);
00245 Assert(opaque->hasho_bucket == bucket);
00246
00247
00248 if (ScanDirectionIsBackward(dir))
00249 {
00250 while (BlockNumberIsValid(opaque->hasho_nextblkno))
00251 _hash_readnext(rel, &buf, &page, &opaque);
00252 }
00253
00254
00255 if (!_hash_step(scan, &buf, dir))
00256 return false;
00257
00258
00259 offnum = ItemPointerGetOffsetNumber(current);
00260 _hash_checkpage(rel, buf, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
00261 page = BufferGetPage(buf);
00262 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
00263 so->hashso_heappos = itup->t_tid;
00264
00265 return true;
00266 }
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279 bool
00280 _hash_step(IndexScanDesc scan, Buffer *bufP, ScanDirection dir)
00281 {
00282 Relation rel = scan->indexRelation;
00283 HashScanOpaque so = (HashScanOpaque) scan->opaque;
00284 ItemPointer current;
00285 Buffer buf;
00286 Page page;
00287 HashPageOpaque opaque;
00288 OffsetNumber maxoff;
00289 OffsetNumber offnum;
00290 BlockNumber blkno;
00291 IndexTuple itup;
00292
00293 current = &(so->hashso_curpos);
00294
00295 buf = *bufP;
00296 _hash_checkpage(rel, buf, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
00297 page = BufferGetPage(buf);
00298 opaque = (HashPageOpaque) PageGetSpecialPointer(page);
00299
00300
00301
00302
00303
00304
00305 maxoff = PageGetMaxOffsetNumber(page);
00306 if (ItemPointerIsValid(current))
00307 offnum = ItemPointerGetOffsetNumber(current);
00308 else
00309 offnum = InvalidOffsetNumber;
00310
00311
00312
00313
00314
00315
00316
00317 do
00318 {
00319 switch (dir)
00320 {
00321 case ForwardScanDirection:
00322 if (offnum != InvalidOffsetNumber)
00323 offnum = OffsetNumberNext(offnum);
00324 else
00325 {
00326
00327 offnum = _hash_binsearch(page, so->hashso_sk_hash);
00328 }
00329
00330 for (;;)
00331 {
00332
00333
00334
00335
00336 if (offnum <= maxoff)
00337 {
00338 Assert(offnum >= FirstOffsetNumber);
00339 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
00340 if (so->hashso_sk_hash == _hash_get_indextuple_hashkey(itup))
00341 break;
00342 }
00343
00344
00345
00346
00347 _hash_readnext(rel, &buf, &page, &opaque);
00348 if (BufferIsValid(buf))
00349 {
00350 maxoff = PageGetMaxOffsetNumber(page);
00351 offnum = _hash_binsearch(page, so->hashso_sk_hash);
00352 }
00353 else
00354 {
00355
00356 itup = NULL;
00357 break;
00358 }
00359 }
00360 break;
00361
00362 case BackwardScanDirection:
00363 if (offnum != InvalidOffsetNumber)
00364 offnum = OffsetNumberPrev(offnum);
00365 else
00366 {
00367
00368 offnum = _hash_binsearch_last(page, so->hashso_sk_hash);
00369 }
00370
00371 for (;;)
00372 {
00373
00374
00375
00376
00377 if (offnum >= FirstOffsetNumber)
00378 {
00379 Assert(offnum <= maxoff);
00380 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
00381 if (so->hashso_sk_hash == _hash_get_indextuple_hashkey(itup))
00382 break;
00383 }
00384
00385
00386
00387
00388 _hash_readprev(rel, &buf, &page, &opaque);
00389 if (BufferIsValid(buf))
00390 {
00391 maxoff = PageGetMaxOffsetNumber(page);
00392 offnum = _hash_binsearch_last(page, so->hashso_sk_hash);
00393 }
00394 else
00395 {
00396
00397 itup = NULL;
00398 break;
00399 }
00400 }
00401 break;
00402
00403 default:
00404
00405
00406 itup = NULL;
00407 break;
00408 }
00409
00410 if (itup == NULL)
00411 {
00412
00413 *bufP = so->hashso_curbuf = InvalidBuffer;
00414 ItemPointerSetInvalid(current);
00415 return false;
00416 }
00417
00418
00419 } while (!_hash_checkqual(scan, itup));
00420
00421
00422 blkno = BufferGetBlockNumber(buf);
00423 *bufP = so->hashso_curbuf = buf;
00424 ItemPointerSet(current, blkno, offnum);
00425 return true;
00426 }