00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "postgres.h"
00017
00018 #include "access/genam.h"
00019 #include "access/reloptions.h"
00020 #include "access/spgist_private.h"
00021 #include "access/transam.h"
00022 #include "access/xact.h"
00023 #include "storage/bufmgr.h"
00024 #include "storage/indexfsm.h"
00025 #include "storage/lmgr.h"
00026 #include "utils/lsyscache.h"
00027
00028
00029
00030 static void
00031 fillTypeDesc(SpGistTypeDesc *desc, Oid type)
00032 {
00033 desc->type = type;
00034 get_typlenbyval(type, &desc->attlen, &desc->attbyval);
00035 }
00036
00037
00038
00039
00040
00041 SpGistCache *
00042 spgGetCache(Relation index)
00043 {
00044 SpGistCache *cache;
00045
00046 if (index->rd_amcache == NULL)
00047 {
00048 Oid atttype;
00049 spgConfigIn in;
00050 FmgrInfo *procinfo;
00051 Buffer metabuffer;
00052 SpGistMetaPageData *metadata;
00053
00054 cache = MemoryContextAllocZero(index->rd_indexcxt,
00055 sizeof(SpGistCache));
00056
00057
00058 Assert(index->rd_att->natts == 1);
00059
00060
00061
00062
00063
00064
00065 atttype = index->rd_att->attrs[0]->atttypid;
00066
00067
00068 in.attType = atttype;
00069
00070 procinfo = index_getprocinfo(index, 1, SPGIST_CONFIG_PROC);
00071 FunctionCall2Coll(procinfo,
00072 index->rd_indcollation[0],
00073 PointerGetDatum(&in),
00074 PointerGetDatum(&cache->config));
00075
00076
00077 fillTypeDesc(&cache->attType, atttype);
00078 fillTypeDesc(&cache->attPrefixType, cache->config.prefixType);
00079 fillTypeDesc(&cache->attLabelType, cache->config.labelType);
00080
00081
00082 metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
00083 LockBuffer(metabuffer, BUFFER_LOCK_SHARE);
00084
00085 metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
00086
00087 if (metadata->magicNumber != SPGIST_MAGIC_NUMBER)
00088 elog(ERROR, "index \"%s\" is not an SP-GiST index",
00089 RelationGetRelationName(index));
00090
00091 cache->lastUsedPages = metadata->lastUsedPages;
00092
00093 UnlockReleaseBuffer(metabuffer);
00094
00095 index->rd_amcache = (void *) cache;
00096 }
00097 else
00098 {
00099
00100 cache = (SpGistCache *) index->rd_amcache;
00101 }
00102
00103 return cache;
00104 }
00105
00106
00107 void
00108 initSpGistState(SpGistState *state, Relation index)
00109 {
00110 SpGistCache *cache;
00111
00112
00113 cache = spgGetCache(index);
00114
00115 state->config = cache->config;
00116 state->attType = cache->attType;
00117 state->attPrefixType = cache->attPrefixType;
00118 state->attLabelType = cache->attLabelType;
00119
00120
00121 state->deadTupleStorage = palloc0(SGDTSIZE);
00122
00123
00124 state->myXid = GetTopTransactionIdIfAny();
00125
00126
00127 state->isBuild = false;
00128 }
00129
00130
00131
00132
00133
00134
00135
00136 Buffer
00137 SpGistNewBuffer(Relation index)
00138 {
00139 Buffer buffer;
00140 bool needLock;
00141
00142
00143 for (;;)
00144 {
00145 BlockNumber blkno = GetFreeIndexPage(index);
00146
00147 if (blkno == InvalidBlockNumber)
00148 break;
00149
00150
00151
00152
00153
00154 if (SpGistBlockIsFixed(blkno))
00155 continue;
00156
00157 buffer = ReadBuffer(index, blkno);
00158
00159
00160
00161
00162
00163 if (ConditionalLockBuffer(buffer))
00164 {
00165 Page page = BufferGetPage(buffer);
00166
00167 if (PageIsNew(page))
00168 return buffer;
00169
00170 if (SpGistPageIsDeleted(page) || PageIsEmpty(page))
00171 return buffer;
00172
00173 LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
00174 }
00175
00176
00177 ReleaseBuffer(buffer);
00178 }
00179
00180
00181 needLock = !RELATION_IS_LOCAL(index);
00182 if (needLock)
00183 LockRelationForExtension(index, ExclusiveLock);
00184
00185 buffer = ReadBuffer(index, P_NEW);
00186 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
00187
00188 if (needLock)
00189 UnlockRelationForExtension(index, ExclusiveLock);
00190
00191 return buffer;
00192 }
00193
00194
00195
00196
00197
00198
00199
00200
00201 void
00202 SpGistUpdateMetaPage(Relation index)
00203 {
00204 SpGistCache *cache = (SpGistCache *) index->rd_amcache;
00205
00206 if (cache != NULL)
00207 {
00208 Buffer metabuffer;
00209 SpGistMetaPageData *metadata;
00210
00211 metabuffer = ReadBuffer(index, SPGIST_METAPAGE_BLKNO);
00212
00213 if (ConditionalLockBuffer(metabuffer))
00214 {
00215 metadata = SpGistPageGetMeta(BufferGetPage(metabuffer));
00216 metadata->lastUsedPages = cache->lastUsedPages;
00217
00218 MarkBufferDirty(metabuffer);
00219 UnlockReleaseBuffer(metabuffer);
00220 }
00221 else
00222 {
00223 ReleaseBuffer(metabuffer);
00224 }
00225 }
00226 }
00227
00228
00229
00230 #define GET_LUP(c, f) (&(c)->lastUsedPages.cachedPage[((unsigned int) (f)) % SPGIST_CACHED_PAGES])
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252 static Buffer
00253 allocNewBuffer(Relation index, int flags)
00254 {
00255 SpGistCache *cache = spgGetCache(index);
00256 uint16 pageflags = 0;
00257
00258 if (GBUF_REQ_LEAF(flags))
00259 pageflags |= SPGIST_LEAF;
00260 if (GBUF_REQ_NULLS(flags))
00261 pageflags |= SPGIST_NULLS;
00262
00263 for (;;)
00264 {
00265 Buffer buffer;
00266
00267 buffer = SpGistNewBuffer(index);
00268 SpGistInitBuffer(buffer, pageflags);
00269
00270 if (pageflags & SPGIST_LEAF)
00271 {
00272
00273 return buffer;
00274 }
00275 else
00276 {
00277 BlockNumber blkno = BufferGetBlockNumber(buffer);
00278 int blkFlags = GBUF_INNER_PARITY(blkno);
00279
00280 if ((flags & GBUF_PARITY_MASK) == blkFlags)
00281 {
00282
00283 return buffer;
00284 }
00285 else
00286 {
00287
00288 if (pageflags & SPGIST_NULLS)
00289 blkFlags |= GBUF_NULLS;
00290 cache->lastUsedPages.cachedPage[blkFlags].blkno = blkno;
00291 cache->lastUsedPages.cachedPage[blkFlags].freeSpace =
00292 PageGetExactFreeSpace(BufferGetPage(buffer));
00293 UnlockReleaseBuffer(buffer);
00294 }
00295 }
00296 }
00297 }
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308 Buffer
00309 SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
00310 {
00311 SpGistCache *cache = spgGetCache(index);
00312 SpGistLastUsedPage *lup;
00313
00314
00315 if (needSpace > SPGIST_PAGE_CAPACITY)
00316 elog(ERROR, "desired SPGiST tuple size is too big");
00317
00318
00319
00320
00321
00322
00323
00324
00325 needSpace += RelationGetTargetPageFreeSpace(index,
00326 SPGIST_DEFAULT_FILLFACTOR);
00327 needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
00328
00329
00330 lup = GET_LUP(cache, flags);
00331
00332
00333 if (lup->blkno == InvalidBlockNumber)
00334 {
00335 *isNew = true;
00336 return allocNewBuffer(index, flags);
00337 }
00338
00339
00340 Assert(!SpGistBlockIsFixed(lup->blkno));
00341
00342
00343 if (lup->freeSpace >= needSpace)
00344 {
00345 Buffer buffer;
00346 Page page;
00347
00348 buffer = ReadBuffer(index, lup->blkno);
00349
00350 if (!ConditionalLockBuffer(buffer))
00351 {
00352
00353
00354
00355 ReleaseBuffer(buffer);
00356 *isNew = true;
00357 return allocNewBuffer(index, flags);
00358 }
00359
00360 page = BufferGetPage(buffer);
00361
00362 if (PageIsNew(page) || SpGistPageIsDeleted(page) || PageIsEmpty(page))
00363 {
00364
00365 uint16 pageflags = 0;
00366
00367 if (GBUF_REQ_LEAF(flags))
00368 pageflags |= SPGIST_LEAF;
00369 if (GBUF_REQ_NULLS(flags))
00370 pageflags |= SPGIST_NULLS;
00371 SpGistInitBuffer(buffer, pageflags);
00372 lup->freeSpace = PageGetExactFreeSpace(page) - needSpace;
00373 *isNew = true;
00374 return buffer;
00375 }
00376
00377
00378
00379
00380
00381 if ((GBUF_REQ_LEAF(flags) ? SpGistPageIsLeaf(page) : !SpGistPageIsLeaf(page)) &&
00382 (GBUF_REQ_NULLS(flags) ? SpGistPageStoresNulls(page) : !SpGistPageStoresNulls(page)))
00383 {
00384 int freeSpace = PageGetExactFreeSpace(page);
00385
00386 if (freeSpace >= needSpace)
00387 {
00388
00389 lup->freeSpace = freeSpace - needSpace;
00390 *isNew = false;
00391 return buffer;
00392 }
00393 }
00394
00395
00396
00397
00398 UnlockReleaseBuffer(buffer);
00399 }
00400
00401
00402 *isNew = true;
00403 return allocNewBuffer(index, flags);
00404 }
00405
00406
00407
00408
00409
00410
00411
00412
00413 void
00414 SpGistSetLastUsedPage(Relation index, Buffer buffer)
00415 {
00416 SpGistCache *cache = spgGetCache(index);
00417 SpGistLastUsedPage *lup;
00418 int freeSpace;
00419 Page page = BufferGetPage(buffer);
00420 BlockNumber blkno = BufferGetBlockNumber(buffer);
00421 int flags;
00422
00423
00424 if (SpGistBlockIsFixed(blkno))
00425 return;
00426
00427 if (SpGistPageIsLeaf(page))
00428 flags = GBUF_LEAF;
00429 else
00430 flags = GBUF_INNER_PARITY(blkno);
00431 if (SpGistPageStoresNulls(page))
00432 flags |= GBUF_NULLS;
00433
00434 lup = GET_LUP(cache, flags);
00435
00436 freeSpace = PageGetExactFreeSpace(page);
00437 if (lup->blkno == InvalidBlockNumber || lup->blkno == blkno ||
00438 lup->freeSpace < freeSpace)
00439 {
00440 lup->blkno = blkno;
00441 lup->freeSpace = freeSpace;
00442 }
00443 }
00444
00445
00446
00447
00448 void
00449 SpGistInitPage(Page page, uint16 f)
00450 {
00451 SpGistPageOpaque opaque;
00452
00453 PageInit(page, BLCKSZ, MAXALIGN(sizeof(SpGistPageOpaqueData)));
00454 opaque = SpGistPageGetOpaque(page);
00455 memset(opaque, 0, sizeof(SpGistPageOpaqueData));
00456 opaque->flags = f;
00457 opaque->spgist_page_id = SPGIST_PAGE_ID;
00458 }
00459
00460
00461
00462
00463 void
00464 SpGistInitBuffer(Buffer b, uint16 f)
00465 {
00466 Assert(BufferGetPageSize(b) == BLCKSZ);
00467 SpGistInitPage(BufferGetPage(b), f);
00468 }
00469
00470
00471
00472
00473 void
00474 SpGistInitMetapage(Page page)
00475 {
00476 SpGistMetaPageData *metadata;
00477 int i;
00478
00479 SpGistInitPage(page, SPGIST_META);
00480 metadata = SpGistPageGetMeta(page);
00481 memset(metadata, 0, sizeof(SpGistMetaPageData));
00482 metadata->magicNumber = SPGIST_MAGIC_NUMBER;
00483
00484
00485 for (i = 0; i < SPGIST_CACHED_PAGES; i++)
00486 metadata->lastUsedPages.cachedPage[i].blkno = InvalidBlockNumber;
00487 }
00488
00489
00490
00491
00492 Datum
00493 spgoptions(PG_FUNCTION_ARGS)
00494 {
00495 Datum reloptions = PG_GETARG_DATUM(0);
00496 bool validate = PG_GETARG_BOOL(1);
00497 bytea *result;
00498
00499 result = default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST);
00500
00501 if (result)
00502 PG_RETURN_BYTEA_P(result);
00503 PG_RETURN_NULL();
00504 }
00505
00506
00507
00508
00509
00510
00511
00512 unsigned int
00513 SpGistGetTypeSize(SpGistTypeDesc *att, Datum datum)
00514 {
00515 unsigned int size;
00516
00517 if (att->attbyval)
00518 size = sizeof(Datum);
00519 else if (att->attlen > 0)
00520 size = att->attlen;
00521 else
00522 size = VARSIZE_ANY(datum);
00523
00524 return MAXALIGN(size);
00525 }
00526
00527
00528
00529
00530 static void
00531 memcpyDatum(void *target, SpGistTypeDesc *att, Datum datum)
00532 {
00533 unsigned int size;
00534
00535 if (att->attbyval)
00536 {
00537 memcpy(target, &datum, sizeof(Datum));
00538 }
00539 else
00540 {
00541 size = (att->attlen > 0) ? att->attlen : VARSIZE_ANY(datum);
00542 memcpy(target, DatumGetPointer(datum), size);
00543 }
00544 }
00545
00546
00547
00548
00549 SpGistLeafTuple
00550 spgFormLeafTuple(SpGistState *state, ItemPointer heapPtr,
00551 Datum datum, bool isnull)
00552 {
00553 SpGistLeafTuple tup;
00554 unsigned int size;
00555
00556
00557 size = SGLTHDRSZ;
00558 if (!isnull)
00559 size += SpGistGetTypeSize(&state->attType, datum);
00560
00561
00562
00563
00564
00565 if (size < SGDTSIZE)
00566 size = SGDTSIZE;
00567
00568
00569 tup = (SpGistLeafTuple) palloc0(size);
00570
00571 tup->size = size;
00572 tup->nextOffset = InvalidOffsetNumber;
00573 tup->heapPtr = *heapPtr;
00574 if (!isnull)
00575 memcpyDatum(SGLTDATAPTR(tup), &state->attType, datum);
00576
00577 return tup;
00578 }
00579
00580
00581
00582
00583
00584
00585
00586 SpGistNodeTuple
00587 spgFormNodeTuple(SpGistState *state, Datum label, bool isnull)
00588 {
00589 SpGistNodeTuple tup;
00590 unsigned int size;
00591 unsigned short infomask = 0;
00592
00593
00594 size = SGNTHDRSZ;
00595 if (!isnull)
00596 size += SpGistGetTypeSize(&state->attLabelType, label);
00597
00598
00599
00600
00601
00602 if ((size & INDEX_SIZE_MASK) != size)
00603 ereport(ERROR,
00604 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
00605 errmsg("index row requires %lu bytes, maximum size is %lu",
00606 (unsigned long) size,
00607 (unsigned long) INDEX_SIZE_MASK)));
00608
00609 tup = (SpGistNodeTuple) palloc0(size);
00610
00611 if (isnull)
00612 infomask |= INDEX_NULL_MASK;
00613
00614 infomask |= size;
00615 tup->t_info = infomask;
00616
00617
00618 ItemPointerSetInvalid(&tup->t_tid);
00619
00620 if (!isnull)
00621 memcpyDatum(SGNTDATAPTR(tup), &state->attLabelType, label);
00622
00623 return tup;
00624 }
00625
00626
00627
00628
00629 SpGistInnerTuple
00630 spgFormInnerTuple(SpGistState *state, bool hasPrefix, Datum prefix,
00631 int nNodes, SpGistNodeTuple *nodes)
00632 {
00633 SpGistInnerTuple tup;
00634 unsigned int size;
00635 unsigned int prefixSize;
00636 int i;
00637 char *ptr;
00638
00639
00640 if (hasPrefix)
00641 prefixSize = SpGistGetTypeSize(&state->attPrefixType, prefix);
00642 else
00643 prefixSize = 0;
00644
00645 size = SGITHDRSZ + prefixSize;
00646
00647
00648 for (i = 0; i < nNodes; i++)
00649 size += IndexTupleSize(nodes[i]);
00650
00651
00652
00653
00654
00655 if (size < SGDTSIZE)
00656 size = SGDTSIZE;
00657
00658
00659
00660
00661 if (size > SPGIST_PAGE_CAPACITY - sizeof(ItemIdData))
00662 ereport(ERROR,
00663 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
00664 errmsg("SP-GiST inner tuple size %lu exceeds maximum %lu",
00665 (unsigned long) size,
00666 (unsigned long) (SPGIST_PAGE_CAPACITY - sizeof(ItemIdData))),
00667 errhint("Values larger than a buffer page cannot be indexed.")));
00668
00669
00670
00671
00672
00673 if (size > SGITMAXSIZE ||
00674 prefixSize > SGITMAXPREFIXSIZE ||
00675 nNodes > SGITMAXNNODES)
00676 elog(ERROR, "SPGiST inner tuple header field is too small");
00677
00678
00679 tup = (SpGistInnerTuple) palloc0(size);
00680
00681 tup->nNodes = nNodes;
00682 tup->prefixSize = prefixSize;
00683 tup->size = size;
00684
00685 if (hasPrefix)
00686 memcpyDatum(SGITDATAPTR(tup), &state->attPrefixType, prefix);
00687
00688 ptr = (char *) SGITNODEPTR(tup);
00689
00690 for (i = 0; i < nNodes; i++)
00691 {
00692 SpGistNodeTuple node = nodes[i];
00693
00694 memcpy(ptr, node, IndexTupleSize(node));
00695 ptr += IndexTupleSize(node);
00696 }
00697
00698 return tup;
00699 }
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712 SpGistDeadTuple
00713 spgFormDeadTuple(SpGistState *state, int tupstate,
00714 BlockNumber blkno, OffsetNumber offnum)
00715 {
00716 SpGistDeadTuple tuple = (SpGistDeadTuple) state->deadTupleStorage;
00717
00718 tuple->tupstate = tupstate;
00719 tuple->size = SGDTSIZE;
00720 tuple->nextOffset = InvalidOffsetNumber;
00721
00722 if (tupstate == SPGIST_REDIRECT)
00723 {
00724 ItemPointerSet(&tuple->pointer, blkno, offnum);
00725 Assert(TransactionIdIsValid(state->myXid));
00726 tuple->xid = state->myXid;
00727 }
00728 else
00729 {
00730 ItemPointerSetInvalid(&tuple->pointer);
00731 tuple->xid = InvalidTransactionId;
00732 }
00733
00734 return tuple;
00735 }
00736
00737
00738
00739
00740
00741
00742 Datum *
00743 spgExtractNodeLabels(SpGistState *state, SpGistInnerTuple innerTuple)
00744 {
00745 Datum *nodeLabels;
00746 int i;
00747 SpGistNodeTuple node;
00748
00749
00750 node = SGITNODEPTR(innerTuple);
00751 if (IndexTupleHasNulls(node))
00752 {
00753 SGITITERATE(innerTuple, i, node)
00754 {
00755 if (!IndexTupleHasNulls(node))
00756 elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
00757 }
00758
00759 return NULL;
00760 }
00761 else
00762 {
00763 nodeLabels = (Datum *) palloc(sizeof(Datum) * innerTuple->nNodes);
00764 SGITITERATE(innerTuple, i, node)
00765 {
00766 if (IndexTupleHasNulls(node))
00767 elog(ERROR, "some but not all node labels are null in SPGiST inner tuple");
00768 nodeLabels[i] = SGNTDATUM(node, state);
00769 }
00770 return nodeLabels;
00771 }
00772 }
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785 OffsetNumber
00786 SpGistPageAddNewItem(SpGistState *state, Page page, Item item, Size size,
00787 OffsetNumber *startOffset, bool errorOK)
00788 {
00789 SpGistPageOpaque opaque = SpGistPageGetOpaque(page);
00790 OffsetNumber i,
00791 maxoff,
00792 offnum;
00793
00794 if (opaque->nPlaceholder > 0 &&
00795 PageGetExactFreeSpace(page) + SGDTSIZE >= MAXALIGN(size))
00796 {
00797
00798 maxoff = PageGetMaxOffsetNumber(page);
00799 offnum = InvalidOffsetNumber;
00800
00801 for (;;)
00802 {
00803 if (startOffset && *startOffset != InvalidOffsetNumber)
00804 i = *startOffset;
00805 else
00806 i = FirstOffsetNumber;
00807 for (; i <= maxoff; i++)
00808 {
00809 SpGistDeadTuple it = (SpGistDeadTuple) PageGetItem(page,
00810 PageGetItemId(page, i));
00811
00812 if (it->tupstate == SPGIST_PLACEHOLDER)
00813 {
00814 offnum = i;
00815 break;
00816 }
00817 }
00818
00819
00820 if (offnum != InvalidOffsetNumber)
00821 break;
00822
00823 if (startOffset && *startOffset != InvalidOffsetNumber)
00824 {
00825
00826 *startOffset = InvalidOffsetNumber;
00827 continue;
00828 }
00829
00830
00831 opaque->nPlaceholder = 0;
00832 break;
00833 }
00834
00835 if (offnum != InvalidOffsetNumber)
00836 {
00837
00838 PageIndexTupleDelete(page, offnum);
00839
00840 offnum = PageAddItem(page, item, size, offnum, false, false);
00841
00842
00843
00844
00845
00846
00847
00848 if (offnum != InvalidOffsetNumber)
00849 {
00850 Assert(opaque->nPlaceholder > 0);
00851 opaque->nPlaceholder--;
00852 if (startOffset)
00853 *startOffset = offnum + 1;
00854 }
00855 else
00856 elog(PANIC, "failed to add item of size %u to SPGiST index page",
00857 (int) size);
00858
00859 return offnum;
00860 }
00861 }
00862
00863
00864 offnum = PageAddItem(page, item, size,
00865 InvalidOffsetNumber, false, false);
00866
00867 if (offnum == InvalidOffsetNumber && !errorOK)
00868 elog(ERROR, "failed to add item of size %u to SPGiST index page",
00869 (int) size);
00870
00871 return offnum;
00872 }