gist.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:35k
源码类别:

数据库系统

开发平台:

Unix_Linux

  1. /*-------------------------------------------------------------------------
  2.  *
  3.  * gist.c
  4.  *   interface routines for the postgres GiST index access method.
  5.  *
  6.  *
  7.  *
  8.  * IDENTIFICATION
  9.  *   $Header: /usr/local/cvsroot/pgsql/src/backend/access/gist/gist.c,v 1.38.2.1 1999/08/02 05:24:28 scrappy Exp $
  10.  *
  11.  *-------------------------------------------------------------------------
  12.  */
  13. #include "postgres.h"
  14. #include "access/genam.h"
  15. #include "access/gist.h"
  16. #include "access/gistscan.h"
  17. #include "access/heapam.h"
  18. #include "catalog/index.h"
  19. #include "catalog/pg_index.h"
  20. #include "executor/executor.h"
  21. #include "utils/syscache.h"
  22. /* non-export function prototypes */
  23. static InsertIndexResult gistdoinsert(Relation r, IndexTuple itup,
  24.  GISTSTATE *GISTstate);
  25. static InsertIndexResult gistentryinsert(Relation r, GISTSTACK *stk,
  26. IndexTuple tup,
  27. GISTSTATE *giststate);
  28. static void gistentryinserttwo(Relation r, GISTSTACK *stk, IndexTuple ltup,
  29.    IndexTuple rtup, GISTSTATE *giststate);
  30. static void gistAdjustKeys(Relation r, GISTSTACK *stk, BlockNumber blk,
  31.    char *datum, int att_size, GISTSTATE *giststate);
  32. static void gistintinsert(Relation r, GISTSTACK *stk, IndexTuple ltup,
  33.   IndexTuple rtup, GISTSTATE *giststate);
  34. static InsertIndexResult gistSplit(Relation r, Buffer buffer,
  35.   GISTSTACK *stack, IndexTuple itup,
  36.   GISTSTATE *giststate);
  37. static void gistnewroot(GISTSTATE *giststate, Relation r, IndexTuple lt,
  38. IndexTuple rt);
  39. static void GISTInitBuffer(Buffer b, uint32 f);
  40. static BlockNumber gistChooseSubtree(Relation r, IndexTuple itup, int level,
  41.   GISTSTATE *giststate,
  42.   GISTSTACK **retstack, Buffer *leafbuf);
  43. static OffsetNumber gistchoose(Relation r, Page p, IndexTuple it,
  44.    GISTSTATE *giststate);
  45. static int gistnospace(Page p, IndexTuple it);
  46. void gistdelete(Relation r, ItemPointer tid);
  47. static IndexTuple gist_tuple_replacekey(Relation r, GISTENTRY entry, IndexTuple t);
  48. static void gistcentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr,
  49.    Relation r, Page pg, OffsetNumber o, int b, bool l);
  50. #ifdef GISTDEBUG
  51. static char *int_range_out(INTRANGE *r);
  52. #endif
  53. /*
  54. ** routine to build an index.  Basically calls insert over and over
  55. */
  56. void
  57. gistbuild(Relation heap,
  58.   Relation index,
  59.   int natts,
  60.   AttrNumber *attnum,
  61.   IndexStrategy istrat,
  62.   uint16 pint,
  63.   Datum *params,
  64.   FuncIndexInfo *finfo,
  65.   PredInfo *predInfo)
  66. {
  67. HeapScanDesc scan;
  68. AttrNumber i;
  69. HeapTuple htup;
  70. IndexTuple itup;
  71. TupleDesc hd,
  72. id;
  73. InsertIndexResult res;
  74. Datum    *d;
  75. bool    *nulls;
  76. int nb,
  77. nh,
  78. ni;
  79. #ifndef OMIT_PARTIAL_INDEX
  80. ExprContext *econtext;
  81. TupleTable tupleTable;
  82. TupleTableSlot *slot;
  83. #endif
  84. Oid hrelid,
  85. irelid;
  86. Node    *pred,
  87.    *oldPred;
  88. GISTSTATE giststate;
  89. GISTENTRY tmpcentry;
  90. Buffer buffer = InvalidBuffer;
  91. bool    *compvec;
  92. /* no locking is needed */
  93. setheapoverride(true); /* so we can see the new pg_index tuple */
  94. initGISTstate(&giststate, index);
  95. setheapoverride(false);
  96. pred = predInfo->pred;
  97. oldPred = predInfo->oldPred;
  98. /*
  99.  * We expect to be called exactly once for any index relation. If
  100.  * that's not the case, big trouble's what we have.
  101.  */
  102. if (oldPred == NULL && (nb = RelationGetNumberOfBlocks(index)) != 0)
  103. elog(ERROR, "%s already contains data", index->rd_rel->relname.data);
  104. /* initialize the root page (if this is a new index) */
  105. if (oldPred == NULL)
  106. {
  107. buffer = ReadBuffer(index, P_NEW);
  108. GISTInitBuffer(buffer, F_LEAF);
  109. WriteBuffer(buffer);
  110. }
  111. /* init the tuple descriptors and get set for a heap scan */
  112. hd = RelationGetDescr(heap);
  113. id = RelationGetDescr(index);
  114. d = (Datum *) palloc(natts * sizeof(*d));
  115. nulls = (bool *) palloc(natts * sizeof(*nulls));
  116. /*
  117.  * If this is a predicate (partial) index, we will need to evaluate
  118.  * the predicate using ExecQual, which requires the current tuple to
  119.  * be in a slot of a TupleTable.  In addition, ExecQual must have an
  120.  * ExprContext referring to that slot. Here, we initialize dummy
  121.  * TupleTable and ExprContext objects for this purpose. --Nels, Feb
  122.  * '92
  123.  */
  124. #ifndef OMIT_PARTIAL_INDEX
  125. if (pred != NULL || oldPred != NULL)
  126. {
  127. tupleTable = ExecCreateTupleTable(1);
  128. slot = ExecAllocTableSlot(tupleTable);
  129. econtext = makeNode(ExprContext);
  130. FillDummyExprContext(econtext, slot, hd, buffer);
  131. }
  132. else
  133. /* shut the compiler up */
  134. {
  135. tupleTable = NULL;
  136. slot = NULL;
  137. econtext = NULL;
  138. }
  139. #endif  /* OMIT_PARTIAL_INDEX */
  140. /* int the tuples as we insert them */
  141. nh = ni = 0;
  142. scan = heap_beginscan(heap, 0, SnapshotNow, 0, (ScanKey) NULL);
  143. while (HeapTupleIsValid(htup = heap_getnext(scan, 0)))
  144. {
  145. nh++;
  146. /*
  147.  * If oldPred != NULL, this is an EXTEND INDEX command, so skip
  148.  * this tuple if it was already in the existing partial index
  149.  */
  150. if (oldPred != NULL)
  151. {
  152. #ifndef OMIT_PARTIAL_INDEX
  153. /* SetSlotContents(slot, htup); */
  154. slot->val = htup;
  155. if (ExecQual((List *) oldPred, econtext) == true)
  156. {
  157. ni++;
  158. continue;
  159. }
  160. #endif  /* OMIT_PARTIAL_INDEX */
  161. }
  162. /*
  163.  * Skip this tuple if it doesn't satisfy the partial-index
  164.  * predicate
  165.  */
  166. if (pred != NULL)
  167. {
  168. #ifndef OMIT_PARTIAL_INDEX
  169. /* SetSlotContents(slot, htup); */
  170. slot->val = htup;
  171. if (ExecQual((List *) pred, econtext) == false)
  172. continue;
  173. #endif  /* OMIT_PARTIAL_INDEX */
  174. }
  175. ni++;
  176. /*
  177.  * For the current heap tuple, extract all the attributes we use
  178.  * in this index, and note which are null.
  179.  */
  180. for (i = 1; i <= natts; i++)
  181. {
  182. int attoff;
  183. bool attnull;
  184. /*
  185.  * Offsets are from the start of the tuple, and are
  186.  * zero-based; indices are one-based.  The next call returns i
  187.  * - 1.  That's data hiding for you.
  188.  */
  189. attoff = AttrNumberGetAttrOffset(i);
  190. /*
  191.  * d[attoff] = HeapTupleGetAttributeValue(htup, buffer,
  192.  */
  193. d[attoff] = GetIndexValue(htup,
  194.   hd,
  195.   attoff,
  196.   attnum,
  197.   finfo,
  198.   &attnull);
  199. nulls[attoff] = (attnull ? 'n' : ' ');
  200. }
  201. /* immediately compress keys to normalize */
  202. compvec = (bool *) palloc(sizeof(bool) * natts);
  203. for (i = 0; i < natts; i++)
  204. {
  205. gistcentryinit(&giststate, &tmpcentry, (char *) d[i],
  206.    (Relation) NULL, (Page) NULL, (OffsetNumber) 0,
  207.    -1 /* size is currently bogus */ , TRUE);
  208. if (d[i] != (Datum) tmpcentry.pred && !(giststate.keytypbyval))
  209. compvec[i] = TRUE;
  210. else
  211. compvec[i] = FALSE;
  212. d[i] = (Datum) tmpcentry.pred;
  213. }
  214. /* form an index tuple and point it at the heap tuple */
  215. itup = index_formtuple(id, &d[0], nulls);
  216. itup->t_tid = htup->t_self;
  217. /*
  218.  * Since we already have the index relation locked, we call
  219.  * gistdoinsert directly.  Normal access method calls dispatch
  220.  * through gistinsert, which locks the relation for write. This
  221.  * is the right thing to do if you're inserting single tups, but
  222.  * not when you're initializing the whole index at once.
  223.  */
  224. res = gistdoinsert(index, itup, &giststate);
  225. for (i = 0; i < natts; i++)
  226. if (compvec[i] == TRUE)
  227. pfree((char *) d[i]);
  228. pfree(itup);
  229. pfree(res);
  230. pfree(compvec);
  231. }
  232. /* okay, all heap tuples are indexed */
  233. heap_endscan(scan);
  234. if (pred != NULL || oldPred != NULL)
  235. {
  236. #ifndef OMIT_PARTIAL_INDEX
  237. ExecDestroyTupleTable(tupleTable, true);
  238. pfree(econtext);
  239. #endif  /* OMIT_PARTIAL_INDEX */
  240. }
  241. /*
  242.  * Since we just inted the tuples in the heap, we update its stats in
  243.  * pg_relation to guarantee that the planner takes advantage of the
  244.  * index we just created.  UpdateStats() does a
  245.  * CommandinterIncrement(), which flushes changed entries from the
  246.  * system relcache.  The act of constructing an index changes these
  247.  * heap and index tuples in the system catalogs, so they need to be
  248.  * flushed.  We close them to guarantee that they will be.
  249.  */
  250. hrelid = RelationGetRelid(heap);
  251. irelid = RelationGetRelid(index);
  252. heap_close(heap);
  253. index_close(index);
  254. UpdateStats(hrelid, nh, true);
  255. UpdateStats(irelid, ni, false);
  256. if (oldPred != NULL)
  257. {
  258. if (ni == nh)
  259. pred = NULL;
  260. UpdateIndexPredicate(irelid, oldPred, pred);
  261. }
  262. /* be tidy */
  263. pfree(nulls);
  264. pfree(d);
  265. }
  266. /*
  267.  * gistinsert -- wrapper for GiST tuple insertion.
  268.  *
  269.  *   This is the public interface routine for tuple insertion in GiSTs.
  270.  *   It doesn't do any work; just locks the relation and passes the buck.
  271.  */
  272. InsertIndexResult
  273. gistinsert(Relation r, Datum *datum, char *nulls, ItemPointer ht_ctid, Relation heapRel)
  274. {
  275. InsertIndexResult res;
  276. IndexTuple itup;
  277. GISTSTATE giststate;
  278. GISTENTRY tmpentry;
  279. int i;
  280. bool    *compvec;
  281. initGISTstate(&giststate, r);
  282. /* immediately compress keys to normalize */
  283. compvec = (bool *) palloc(sizeof(bool) * r->rd_att->natts);
  284. for (i = 0; i < r->rd_att->natts; i++)
  285. {
  286. gistcentryinit(&giststate, &tmpentry, (char *) datum[i],
  287.    (Relation) NULL, (Page) NULL, (OffsetNumber) 0,
  288.    -1 /* size is currently bogus */ , TRUE);
  289. if (datum[i] != (Datum) tmpentry.pred && !(giststate.keytypbyval))
  290. compvec[i] = TRUE;
  291. else
  292. compvec[i] = FALSE;
  293. datum[i] = (Datum) tmpentry.pred;
  294. }
  295. itup = index_formtuple(RelationGetDescr(r), datum, nulls);
  296. itup->t_tid = *ht_ctid;
  297. /*
  298.  * Notes in ExecUtils:ExecOpenIndices()
  299.  *
  300.  * RelationSetLockForWrite(r);
  301.  */
  302. res = gistdoinsert(r, itup, &giststate);
  303. for (i = 0; i < r->rd_att->natts; i++)
  304. if (compvec[i] == TRUE)
  305. pfree((char *) datum[i]);
  306. pfree(itup);
  307. pfree(compvec);
  308. return res;
  309. }
  310. /*
  311. ** Take a compressed entry, and install it on a page.  Since we now know
  312. ** where the entry will live, we decompress it and recompress it using
  313. ** that knowledge (some compression routines may want to fish around
  314. ** on the page, for example, or do something special for leaf nodes.)
  315. */
  316. static OffsetNumber
  317. gistPageAddItem(GISTSTATE *giststate,
  318. Relation r,
  319. Page page,
  320. Item item,
  321. Size size,
  322. OffsetNumber offsetNumber,
  323. ItemIdFlags flags,
  324. GISTENTRY *dentry,
  325. IndexTuple *newtup)
  326. {
  327. GISTENTRY tmpcentry;
  328. IndexTuple itup = (IndexTuple) item;
  329. /*
  330.  * recompress the item given that we now know the exact page and
  331.  * offset for insertion
  332.  */
  333. gistdentryinit(giststate, dentry,
  334.    (((char *) itup) + sizeof(IndexTupleData)),
  335.   (Relation) 0, (Page) 0, (OffsetNumber) InvalidOffsetNumber,
  336.    IndexTupleSize(itup) - sizeof(IndexTupleData), FALSE);
  337. gistcentryinit(giststate, &tmpcentry, dentry->pred, r, page,
  338.    offsetNumber, dentry->bytes, FALSE);
  339. *newtup = gist_tuple_replacekey(r, *dentry, itup);
  340. /* be tidy */
  341. if (tmpcentry.pred != dentry->pred
  342. && tmpcentry.pred != (((char *) itup) + sizeof(IndexTupleData)))
  343. pfree(tmpcentry.pred);
  344. return (PageAddItem(page, (Item) *newtup, IndexTupleSize(*newtup),
  345. offsetNumber, flags));
  346. }
  347. static InsertIndexResult
  348. gistdoinsert(Relation r,
  349.  IndexTuple itup, /* itup contains compressed entry */
  350.  GISTSTATE *giststate)
  351. {
  352. GISTENTRY tmpdentry;
  353. InsertIndexResult res;
  354. OffsetNumber l;
  355. GISTSTACK  *stack;
  356. Buffer buffer;
  357. BlockNumber blk;
  358. Page page;
  359. OffsetNumber off;
  360. IndexTuple newtup;
  361. /* 3rd arg is ignored for now */
  362. blk = gistChooseSubtree(r, itup, 0, giststate, &stack, &buffer);
  363. page = (Page) BufferGetPage(buffer);
  364. if (gistnospace(page, itup))
  365. {
  366. /* need to do a split */
  367. res = gistSplit(r, buffer, stack, itup, giststate);
  368. gistfreestack(stack);
  369. WriteBuffer(buffer); /* don't forget to release buffer! */
  370. return res;
  371. }
  372. if (PageIsEmpty(page))
  373. off = FirstOffsetNumber;
  374. else
  375. off = OffsetNumberNext(PageGetMaxOffsetNumber(page));
  376. /* add the item and write the buffer */
  377. l = gistPageAddItem(giststate, r, page, (Item) itup, IndexTupleSize(itup),
  378. off, LP_USED, &tmpdentry, &newtup);
  379. WriteBuffer(buffer);
  380. /* now expand the page boundary in the parent to include the new child */
  381. gistAdjustKeys(r, stack, blk, tmpdentry.pred, tmpdentry.bytes, giststate);
  382. gistfreestack(stack);
  383. /* be tidy */
  384. if (itup != newtup)
  385. pfree(newtup);
  386. if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData)))
  387. pfree(tmpdentry.pred);
  388. /* build and return an InsertIndexResult for this insertion */
  389. res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
  390. ItemPointerSet(&(res->pointerData), blk, l);
  391. return res;
  392. }
  393. static BlockNumber
  394. gistChooseSubtree(Relation r, IndexTuple itup, /* itup has compressed
  395.  * entry */
  396.   int level,
  397.   GISTSTATE *giststate,
  398.   GISTSTACK **retstack /* out */ ,
  399.   Buffer *leafbuf /* out */ )
  400. {
  401. Buffer buffer;
  402. BlockNumber blk;
  403. GISTSTACK  *stack;
  404. Page page;
  405. GISTPageOpaque opaque;
  406. IndexTuple which;
  407. blk = GISTP_ROOT;
  408. buffer = InvalidBuffer;
  409. stack = (GISTSTACK *) NULL;
  410. do
  411. {
  412. /* let go of current buffer before getting next */
  413. if (buffer != InvalidBuffer)
  414. ReleaseBuffer(buffer);
  415. /* get next buffer */
  416. buffer = ReadBuffer(r, blk);
  417. page = (Page) BufferGetPage(buffer);
  418. opaque = (GISTPageOpaque) PageGetSpecialPointer(page);
  419. if (!(opaque->flags & F_LEAF))
  420. {
  421. GISTSTACK  *n;
  422. ItemId iid;
  423. n = (GISTSTACK *) palloc(sizeof(GISTSTACK));
  424. n->gs_parent = stack;
  425. n->gs_blk = blk;
  426. n->gs_child = gistchoose(r, page, itup, giststate);
  427. stack = n;
  428. iid = PageGetItemId(page, n->gs_child);
  429. which = (IndexTuple) PageGetItem(page, iid);
  430. blk = ItemPointerGetBlockNumber(&(which->t_tid));
  431. }
  432. } while (!(opaque->flags & F_LEAF));
  433. *retstack = stack;
  434. *leafbuf = buffer;
  435. return blk;
  436. }
  437. static void
  438. gistAdjustKeys(Relation r,
  439.    GISTSTACK *stk,
  440.    BlockNumber blk,
  441.    char *datum, /* datum is uncompressed */
  442.    int att_size,
  443.    GISTSTATE *giststate)
  444. {
  445. char    *oldud;
  446. Page p;
  447. Buffer b;
  448. bool result;
  449. bytea    *evec;
  450. GISTENTRY centry,
  451.    *ev0p,
  452.    *ev1p;
  453. int size,
  454. datumsize;
  455. IndexTuple tid;
  456. if (stk == (GISTSTACK *) NULL)
  457. return;
  458. b = ReadBuffer(r, stk->gs_blk);
  459. p = BufferGetPage(b);
  460. oldud = (char *) PageGetItem(p, PageGetItemId(p, stk->gs_child));
  461. tid = (IndexTuple) oldud;
  462. size = IndexTupleSize((IndexTuple) oldud) - sizeof(IndexTupleData);
  463. oldud += sizeof(IndexTupleData);
  464. evec = (bytea *) palloc(2 * sizeof(GISTENTRY) + VARHDRSZ);
  465. VARSIZE(evec) = 2 * sizeof(GISTENTRY) + VARHDRSZ;
  466. /* insert decompressed oldud into entry vector */
  467. gistdentryinit(giststate, &((GISTENTRY *) VARDATA(evec))[0],
  468.    oldud, r, p, stk->gs_child,
  469.    size, FALSE);
  470. ev0p = &((GISTENTRY *) VARDATA(evec))[0];
  471. /* insert datum entry into entry vector */
  472. gistentryinit(((GISTENTRY *) VARDATA(evec))[1], datum,
  473. (Relation) NULL, (Page) NULL, (OffsetNumber) 0, att_size, FALSE);
  474. ev1p = &((GISTENTRY *) VARDATA(evec))[1];
  475. /* form union of decompressed entries */
  476. datum = (*fmgr_faddr(&giststate->unionFn)) (evec, &datumsize);
  477. /* did union leave decompressed version of oldud unchanged? */
  478. (*fmgr_faddr(&giststate->equalFn)) (ev0p->pred, datum, &result);
  479. if (!result)
  480. {
  481. TupleDesc td = RelationGetDescr(r);
  482. /* compress datum for storage on page */
  483. gistcentryinit(giststate, &centry, datum, ev0p->rel, ev0p->page,
  484.    ev0p->offset, datumsize, FALSE);
  485. if (td->attrs[0]->attlen >= 0)
  486. {
  487. memmove(oldud, centry.pred, att_size);
  488. gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, datum, att_size,
  489.    giststate);
  490. }
  491. else if (VARSIZE(centry.pred) == VARSIZE(oldud))
  492. {
  493. memmove(oldud, centry.pred, VARSIZE(centry.pred));
  494. gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, datum, att_size,
  495.    giststate);
  496. }
  497. else
  498. {
  499. /*
  500.  * * new datum is not the same size as the old. * We have to
  501.  * delete the old entry and insert the new * one.  Note that
  502.  * this may cause a split here!
  503.  */
  504. IndexTuple newtup;
  505. ItemPointerData oldtid;
  506. char    *isnull;
  507. TupleDesc tupDesc;
  508. InsertIndexResult res;
  509. /* delete old tuple */
  510. ItemPointerSet(&oldtid, stk->gs_blk, stk->gs_child);
  511. gistdelete(r, (ItemPointer) &oldtid);
  512. /* generate and insert new tuple */
  513. tupDesc = r->rd_att;
  514. isnull = (char *) palloc(r->rd_rel->relnatts);
  515. MemSet(isnull, ' ', r->rd_rel->relnatts);
  516. newtup = (IndexTuple) index_formtuple(tupDesc,
  517.  (Datum *) &centry.pred, isnull);
  518. pfree(isnull);
  519. /* set pointer in new tuple to point to current child */
  520. ItemPointerSet(&oldtid, blk, 1);
  521. newtup->t_tid = oldtid;
  522. /* inserting the new entry also adjust keys above */
  523. res = gistentryinsert(r, stk, newtup, giststate);
  524. /* in stack, set info to point to new tuple */
  525. stk->gs_blk = ItemPointerGetBlockNumber(&(res->pointerData));
  526. stk->gs_child = ItemPointerGetOffsetNumber(&(res->pointerData));
  527. pfree(res);
  528. }
  529. WriteBuffer(b);
  530. if (centry.pred != datum)
  531. pfree(datum);
  532. }
  533. else
  534. ReleaseBuffer(b);
  535. pfree(evec);
  536. }
  537. /*
  538.  * gistSplit -- split a page in the tree.
  539.  *
  540.  */
  541. static InsertIndexResult
  542. gistSplit(Relation r,
  543.   Buffer buffer,
  544.   GISTSTACK *stack,
  545.   IndexTuple itup, /* contains compressed entry */
  546.   GISTSTATE *giststate)
  547. {
  548. Page p;
  549. Buffer leftbuf,
  550. rightbuf;
  551. Page left,
  552. right;
  553. ItemId itemid;
  554. IndexTuple item;
  555. IndexTuple ltup,
  556. rtup,
  557. newtup;
  558. OffsetNumber maxoff;
  559. OffsetNumber i;
  560. OffsetNumber leftoff,
  561. rightoff;
  562. BlockNumber lbknum,
  563. rbknum;
  564. BlockNumber bufblock;
  565. GISTPageOpaque opaque;
  566. int blank;
  567. InsertIndexResult res;
  568. char    *isnull;
  569. GIST_SPLITVEC v;
  570. TupleDesc tupDesc;
  571. bytea    *entryvec;
  572. bool    *decompvec;
  573. IndexTuple item_1;
  574. GISTENTRY tmpdentry,
  575. tmpentry;
  576. isnull = (char *) palloc(r->rd_rel->relnatts);
  577. for (blank = 0; blank < r->rd_rel->relnatts; blank++)
  578. isnull[blank] = ' ';
  579. p = (Page) BufferGetPage(buffer);
  580. opaque = (GISTPageOpaque) PageGetSpecialPointer(p);
  581. /*
  582.  * The root of the tree is the first block in the relation.  If we're
  583.  * about to split the root, we need to do some hocus-pocus to enforce
  584.  * this guarantee.
  585.  */
  586. if (BufferGetBlockNumber(buffer) == GISTP_ROOT)
  587. {
  588. leftbuf = ReadBuffer(r, P_NEW);
  589. GISTInitBuffer(leftbuf, opaque->flags);
  590. lbknum = BufferGetBlockNumber(leftbuf);
  591. left = (Page) BufferGetPage(leftbuf);
  592. }
  593. else
  594. {
  595. leftbuf = buffer;
  596. IncrBufferRefCount(buffer);
  597. lbknum = BufferGetBlockNumber(buffer);
  598. left = (Page) PageGetTempPage(p, sizeof(GISTPageOpaqueData));
  599. }
  600. rightbuf = ReadBuffer(r, P_NEW);
  601. GISTInitBuffer(rightbuf, opaque->flags);
  602. rbknum = BufferGetBlockNumber(rightbuf);
  603. right = (Page) BufferGetPage(rightbuf);
  604. /* generate the item array */
  605. maxoff = PageGetMaxOffsetNumber(p);
  606. entryvec = (bytea *) palloc(VARHDRSZ + (maxoff + 2) * sizeof(GISTENTRY));
  607. decompvec = (bool *) palloc(VARHDRSZ + (maxoff + 2) * sizeof(bool));
  608. for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
  609. {
  610. item_1 = (IndexTuple) PageGetItem(p, PageGetItemId(p, i));
  611. gistdentryinit(giststate, &((GISTENTRY *) VARDATA(entryvec))[i],
  612.    (((char *) item_1) + sizeof(IndexTupleData)),
  613.    r, p, i,
  614.  IndexTupleSize(item_1) - sizeof(IndexTupleData), FALSE);
  615. if ((char *) (((GISTENTRY *) VARDATA(entryvec))[i].pred)
  616. == (((char *) item_1) + sizeof(IndexTupleData)))
  617. decompvec[i] = FALSE;
  618. else
  619. decompvec[i] = TRUE;
  620. }
  621. /* add the new datum as the last entry */
  622. gistdentryinit(giststate, &(((GISTENTRY *) VARDATA(entryvec))[maxoff + 1]),
  623.    (((char *) itup) + sizeof(IndexTupleData)),
  624.    (Relation) NULL, (Page) NULL,
  625.    (OffsetNumber) 0, tmpentry.bytes, FALSE);
  626. if ((char *) (((GISTENTRY *) VARDATA(entryvec))[maxoff + 1]).pred !=
  627. (((char *) itup) + sizeof(IndexTupleData)))
  628. decompvec[maxoff + 1] = TRUE;
  629. else
  630. decompvec[maxoff + 1] = FALSE;
  631. VARSIZE(entryvec) = (maxoff + 2) * sizeof(GISTENTRY) + VARHDRSZ;
  632. /* now let the user-defined picksplit function set up the split vector */
  633. (*fmgr_faddr(&giststate->picksplitFn)) (entryvec, &v);
  634. /* compress ldatum and rdatum */
  635. gistcentryinit(giststate, &tmpentry, v.spl_ldatum, (Relation) NULL,
  636.    (Page) NULL, (OffsetNumber) 0,
  637.    ((GISTENTRY *) VARDATA(entryvec))[i].bytes, FALSE);
  638. if (v.spl_ldatum != tmpentry.pred)
  639. pfree(v.spl_ldatum);
  640. v.spl_ldatum = tmpentry.pred;
  641. gistcentryinit(giststate, &tmpentry, v.spl_rdatum, (Relation) NULL,
  642.    (Page) NULL, (OffsetNumber) 0,
  643.    ((GISTENTRY *) VARDATA(entryvec))[i].bytes, FALSE);
  644. if (v.spl_rdatum != tmpentry.pred)
  645. pfree(v.spl_rdatum);
  646. v.spl_rdatum = tmpentry.pred;
  647. /* clean up the entry vector: its preds need to be deleted, too */
  648. for (i = FirstOffsetNumber; i <= maxoff + 1; i = OffsetNumberNext(i))
  649. if (decompvec[i])
  650. pfree(((GISTENTRY *) VARDATA(entryvec))[i].pred);
  651. pfree(entryvec);
  652. pfree(decompvec);
  653. leftoff = rightoff = FirstOffsetNumber;
  654. maxoff = PageGetMaxOffsetNumber(p);
  655. for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
  656. {
  657. itemid = PageGetItemId(p, i);
  658. item = (IndexTuple) PageGetItem(p, itemid);
  659. if (i == *(v.spl_left))
  660. {
  661. gistPageAddItem(giststate, r, left, (Item) item,
  662. IndexTupleSize(item),
  663. leftoff, LP_USED, &tmpdentry, &newtup);
  664. leftoff = OffsetNumberNext(leftoff);
  665. v.spl_left++; /* advance in left split vector */
  666. /* be tidy */
  667. if (tmpdentry.pred != (((char *) item) + sizeof(IndexTupleData)))
  668. pfree(tmpdentry.pred);
  669. if ((IndexTuple) item != newtup)
  670. pfree(newtup);
  671. }
  672. else
  673. {
  674. gistPageAddItem(giststate, r, right, (Item) item,
  675. IndexTupleSize(item),
  676. rightoff, LP_USED, &tmpdentry, &newtup);
  677. rightoff = OffsetNumberNext(rightoff);
  678. v.spl_right++; /* advance in right split vector */
  679. /* be tidy */
  680. if (tmpdentry.pred != (((char *) item) + sizeof(IndexTupleData)))
  681. pfree(tmpdentry.pred);
  682. if (item != newtup)
  683. pfree(newtup);
  684. }
  685. }
  686. /* build an InsertIndexResult for this insertion */
  687. res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
  688. /* now insert the new index tuple */
  689. if (*(v.spl_left) != FirstOffsetNumber)
  690. {
  691. gistPageAddItem(giststate, r, left, (Item) itup,
  692. IndexTupleSize(itup),
  693. leftoff, LP_USED, &tmpdentry, &newtup);
  694. leftoff = OffsetNumberNext(leftoff);
  695. ItemPointerSet(&(res->pointerData), lbknum, leftoff);
  696. /* be tidy */
  697. if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData)))
  698. pfree(tmpdentry.pred);
  699. if (itup != newtup)
  700. pfree(newtup);
  701. }
  702. else
  703. {
  704. gistPageAddItem(giststate, r, right, (Item) itup,
  705. IndexTupleSize(itup),
  706. rightoff, LP_USED, &tmpdentry, &newtup);
  707. rightoff = OffsetNumberNext(rightoff);
  708. ItemPointerSet(&(res->pointerData), rbknum, rightoff);
  709. /* be tidy */
  710. if (tmpdentry.pred != (((char *) itup) + sizeof(IndexTupleData)))
  711. pfree(tmpdentry.pred);
  712. if (itup != newtup)
  713. pfree(newtup);
  714. }
  715. if ((bufblock = BufferGetBlockNumber(buffer)) != GISTP_ROOT)
  716. PageRestoreTempPage(left, p);
  717. WriteBuffer(leftbuf);
  718. WriteBuffer(rightbuf);
  719. /*
  720.  * Okay, the page is split.  We have three things left to do:
  721.  *
  722.  * 1)  Adjust any active scans on this index to cope with changes we
  723.  * introduced in its structure by splitting this page.
  724.  *
  725.  * 2)  "Tighten" the bounding box of the pointer to the left page in the
  726.  * parent node in the tree, if any.  Since we moved a bunch of stuff
  727.  * off the left page, we expect it to get smaller. This happens in
  728.  * the internal insertion routine.
  729.  *
  730.  * 3)  Insert a pointer to the right page in the parent.  This may cause
  731.  * the parent to split.  If it does, we need to repeat steps one and
  732.  * two for each split node in the tree.
  733.  */
  734. /* adjust active scans */
  735. gistadjscans(r, GISTOP_SPLIT, bufblock, FirstOffsetNumber);
  736. tupDesc = r->rd_att;
  737. ltup = (IndexTuple) index_formtuple(tupDesc,
  738.   (Datum *) &(v.spl_ldatum), isnull);
  739. rtup = (IndexTuple) index_formtuple(tupDesc,
  740.   (Datum *) &(v.spl_rdatum), isnull);
  741. pfree(isnull);
  742. /* set pointers to new child pages in the internal index tuples */
  743. ItemPointerSet(&(ltup->t_tid), lbknum, 1);
  744. ItemPointerSet(&(rtup->t_tid), rbknum, 1);
  745. gistintinsert(r, stack, ltup, rtup, giststate);
  746. pfree(ltup);
  747. pfree(rtup);
  748. return res;
  749. }
  750. /*
  751. ** After a split, we need to overwrite the old entry's key in the parent,
  752. ** and install install an entry for the new key into the parent.
  753. */
  754. static void
  755. gistintinsert(Relation r,
  756.   GISTSTACK *stk,
  757.   IndexTuple ltup, /* new version of entry for old page */
  758.   IndexTuple rtup, /* entry for new page */
  759.   GISTSTATE *giststate)
  760. {
  761. ItemPointerData ltid;
  762. if (stk == (GISTSTACK *) NULL)
  763. {
  764. gistnewroot(giststate, r, ltup, rtup);
  765. return;
  766. }
  767. /* remove old left pointer, insert the 2 new entries */
  768. ItemPointerSet(&ltid, stk->gs_blk, stk->gs_child);
  769. gistdelete(r, (ItemPointer) &ltid);
  770. gistentryinserttwo(r, stk, ltup, rtup, giststate);
  771. }
  772. /*
  773. ** Insert two entries onto one page, handling a split for either one!
  774. */
  775. static void
  776. gistentryinserttwo(Relation r, GISTSTACK *stk, IndexTuple ltup,
  777.    IndexTuple rtup, GISTSTATE *giststate)
  778. {
  779. Buffer b;
  780. Page p;
  781. InsertIndexResult res;
  782. GISTENTRY tmpentry;
  783. IndexTuple newtup;
  784. b = ReadBuffer(r, stk->gs_blk);
  785. p = BufferGetPage(b);
  786. if (gistnospace(p, ltup))
  787. {
  788. res = gistSplit(r, b, stk->gs_parent, ltup, giststate);
  789. WriteBuffer(b); /* don't forget to release buffer!  -
  790.  * 01/31/94 */
  791. pfree(res);
  792. gistdoinsert(r, rtup, giststate);
  793. }
  794. else
  795. {
  796. gistPageAddItem(giststate, r, p, (Item) ltup,
  797. IndexTupleSize(ltup), InvalidOffsetNumber,
  798. LP_USED, &tmpentry, &newtup);
  799. WriteBuffer(b);
  800. gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, tmpentry.pred,
  801.    tmpentry.bytes, giststate);
  802. /* be tidy */
  803. if (tmpentry.pred != (((char *) ltup) + sizeof(IndexTupleData)))
  804. pfree(tmpentry.pred);
  805. if (ltup != newtup)
  806. pfree(newtup);
  807. gistentryinsert(r, stk, rtup, giststate);
  808. }
  809. }
  810. /*
  811. ** Insert an entry onto a page
  812. */
  813. static InsertIndexResult
  814. gistentryinsert(Relation r, GISTSTACK *stk, IndexTuple tup,
  815. GISTSTATE *giststate)
  816. {
  817. Buffer b;
  818. Page p;
  819. InsertIndexResult res;
  820. OffsetNumber off;
  821. GISTENTRY tmpentry;
  822. IndexTuple newtup;
  823. b = ReadBuffer(r, stk->gs_blk);
  824. p = BufferGetPage(b);
  825. if (gistnospace(p, tup))
  826. {
  827. res = gistSplit(r, b, stk->gs_parent, tup, giststate);
  828. WriteBuffer(b); /* don't forget to release buffer!  -
  829.  * 01/31/94 */
  830. return res;
  831. }
  832. else
  833. {
  834. res = (InsertIndexResult) palloc(sizeof(InsertIndexResultData));
  835. off = gistPageAddItem(giststate, r, p, (Item) tup, IndexTupleSize(tup),
  836.    InvalidOffsetNumber, LP_USED, &tmpentry, &newtup);
  837. WriteBuffer(b);
  838. ItemPointerSet(&(res->pointerData), stk->gs_blk, off);
  839. gistAdjustKeys(r, stk->gs_parent, stk->gs_blk, tmpentry.pred,
  840.    tmpentry.bytes, giststate);
  841. /* be tidy */
  842. if (tmpentry.pred != (((char *) tup) + sizeof(IndexTupleData)))
  843. pfree(tmpentry.pred);
  844. if (tup != newtup)
  845. pfree(newtup);
  846. return res;
  847. }
  848. }
  849. static void
  850. gistnewroot(GISTSTATE *giststate, Relation r, IndexTuple lt, IndexTuple rt)
  851. {
  852. Buffer b;
  853. Page p;
  854. GISTENTRY tmpentry;
  855. IndexTuple newtup;
  856. b = ReadBuffer(r, GISTP_ROOT);
  857. GISTInitBuffer(b, 0);
  858. p = BufferGetPage(b);
  859. gistPageAddItem(giststate, r, p, (Item) lt, IndexTupleSize(lt),
  860. FirstOffsetNumber,
  861. LP_USED, &tmpentry, &newtup);
  862. /* be tidy */
  863. if (tmpentry.pred != (((char *) lt) + sizeof(IndexTupleData)))
  864. pfree(tmpentry.pred);
  865. if (lt != newtup)
  866. pfree(newtup);
  867. gistPageAddItem(giststate, r, p, (Item) rt, IndexTupleSize(rt),
  868. OffsetNumberNext(FirstOffsetNumber), LP_USED,
  869. &tmpentry, &newtup);
  870. /* be tidy */
  871. if (tmpentry.pred != (((char *) rt) + sizeof(IndexTupleData)))
  872. pfree(tmpentry.pred);
  873. if (rt != newtup)
  874. pfree(newtup);
  875. WriteBuffer(b);
  876. }
  877. static void
  878. GISTInitBuffer(Buffer b, uint32 f)
  879. {
  880. GISTPageOpaque opaque;
  881. Page page;
  882. Size pageSize;
  883. pageSize = BufferGetPageSize(b);
  884. page = BufferGetPage(b);
  885. MemSet(page, 0, (int) pageSize);
  886. PageInit(page, pageSize, sizeof(GISTPageOpaqueData));
  887. opaque = (GISTPageOpaque) PageGetSpecialPointer(page);
  888. opaque->flags = f;
  889. }
  890. /*
  891. ** find entry with lowest penalty
  892. */
  893. static OffsetNumber
  894. gistchoose(Relation r, Page p, IndexTuple it, /* it has compressed entry */
  895.    GISTSTATE *giststate)
  896. {
  897. OffsetNumber maxoff;
  898. OffsetNumber i;
  899. char    *id;
  900. char    *datum;
  901. float usize;
  902. OffsetNumber which;
  903. float which_grow;
  904. GISTENTRY entry,
  905. identry;
  906. int size,
  907. idsize;
  908. idsize = IndexTupleSize(it) - sizeof(IndexTupleData);
  909. id = ((char *) it) + sizeof(IndexTupleData);
  910. maxoff = PageGetMaxOffsetNumber(p);
  911. which_grow = -1.0;
  912. which = -1;
  913. gistdentryinit(giststate, &identry, id, (Relation) NULL, (Page) NULL,
  914.    (OffsetNumber) 0, idsize, FALSE);
  915. for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
  916. {
  917. datum = (char *) PageGetItem(p, PageGetItemId(p, i));
  918. size = IndexTupleSize(datum) - sizeof(IndexTupleData);
  919. datum += sizeof(IndexTupleData);
  920. gistdentryinit(giststate, &entry, datum, r, p, i, size, FALSE);
  921. (*fmgr_faddr(&giststate->penaltyFn)) (&entry, &identry, &usize);
  922. if (which_grow < 0 || usize < which_grow)
  923. {
  924. which = i;
  925. which_grow = usize;
  926. if (which_grow == 0)
  927. break;
  928. }
  929. if (entry.pred != datum)
  930. pfree(entry.pred);
  931. }
  932. if (identry.pred != id)
  933. pfree(identry.pred);
  934. return which;
  935. }
  936. static int
  937. gistnospace(Page p, IndexTuple it)
  938. {
  939. return PageGetFreeSpace(p) < IndexTupleSize(it);
  940. }
  941. void
  942. gistfreestack(GISTSTACK *s)
  943. {
  944. GISTSTACK  *p;
  945. while (s != (GISTSTACK *) NULL)
  946. {
  947. p = s->gs_parent;
  948. pfree(s);
  949. s = p;
  950. }
  951. }
  952. /*
  953. ** remove an entry from a page
  954. */
  955. void
  956. gistdelete(Relation r, ItemPointer tid)
  957. {
  958. BlockNumber blkno;
  959. OffsetNumber offnum;
  960. Buffer buf;
  961. Page page;
  962. /*
  963.  * Notes in ExecUtils:ExecOpenIndices() Also note that only vacuum
  964.  * deletes index tuples now...
  965.  *
  966.  * RelationSetLockForWrite(r);
  967.  */
  968. blkno = ItemPointerGetBlockNumber(tid);
  969. offnum = ItemPointerGetOffsetNumber(tid);
  970. /* adjust any scans that will be affected by this deletion */
  971. gistadjscans(r, GISTOP_DEL, blkno, offnum);
  972. /* delete the index tuple */
  973. buf = ReadBuffer(r, blkno);
  974. page = BufferGetPage(buf);
  975. PageIndexTupleDelete(page, offnum);
  976. WriteBuffer(buf);
  977. }
  978. void
  979. initGISTstate(GISTSTATE *giststate, Relation index)
  980. {
  981. RegProcedure consistent_proc,
  982. union_proc,
  983. compress_proc,
  984. decompress_proc;
  985. RegProcedure penalty_proc,
  986. picksplit_proc,
  987. equal_proc;
  988. HeapTuple htup;
  989. Form_pg_index itupform;
  990. consistent_proc = index_getprocid(index, 1, GIST_CONSISTENT_PROC);
  991. union_proc = index_getprocid(index, 1, GIST_UNION_PROC);
  992. compress_proc = index_getprocid(index, 1, GIST_COMPRESS_PROC);
  993. decompress_proc = index_getprocid(index, 1, GIST_DECOMPRESS_PROC);
  994. penalty_proc = index_getprocid(index, 1, GIST_PENALTY_PROC);
  995. picksplit_proc = index_getprocid(index, 1, GIST_PICKSPLIT_PROC);
  996. equal_proc = index_getprocid(index, 1, GIST_EQUAL_PROC);
  997. fmgr_info(consistent_proc, &giststate->consistentFn);
  998. fmgr_info(union_proc, &giststate->unionFn);
  999. fmgr_info(compress_proc, &giststate->compressFn);
  1000. fmgr_info(decompress_proc, &giststate->decompressFn);
  1001. fmgr_info(penalty_proc, &giststate->penaltyFn);
  1002. fmgr_info(picksplit_proc, &giststate->picksplitFn);
  1003. fmgr_info(equal_proc, &giststate->equalFn);
  1004. /* see if key type is different from type of attribute being indexed */
  1005. htup = SearchSysCacheTuple(INDEXRELID,
  1006.    ObjectIdGetDatum(RelationGetRelid(index)),
  1007.    0, 0, 0);
  1008. itupform = (Form_pg_index) GETSTRUCT(htup);
  1009. if (!HeapTupleIsValid(htup))
  1010. elog(ERROR, "initGISTstate: index %u not found",
  1011.  RelationGetRelid(index));
  1012. giststate->haskeytype = itupform->indhaskeytype;
  1013. if (giststate->haskeytype)
  1014. {
  1015. /* key type is different -- is it byval? */
  1016. htup = SearchSysCacheTuple(ATTNUM,
  1017.    ObjectIdGetDatum(itupform->indexrelid),
  1018.    UInt16GetDatum(FirstOffsetNumber),
  1019.    0, 0);
  1020. if (!HeapTupleIsValid(htup))
  1021. {
  1022. elog(ERROR, "initGISTstate: no attribute tuple %u %d",
  1023.  itupform->indexrelid, FirstOffsetNumber);
  1024. return;
  1025. }
  1026. giststate->keytypbyval = (((Form_pg_attribute) htup)->attbyval);
  1027. }
  1028. else
  1029. giststate->keytypbyval = FALSE;
  1030. return;
  1031. }
  1032. /*
  1033. ** Given an IndexTuple to be inserted on a page, this routine replaces
  1034. ** the key with another key, which may involve generating a new IndexTuple
  1035. ** if the sizes don't match
  1036. */
  1037. static IndexTuple
  1038. gist_tuple_replacekey(Relation r, GISTENTRY entry, IndexTuple t)
  1039. {
  1040. char    *datum = (((char *) t) + sizeof(IndexTupleData));
  1041. /* if new entry fits in index tuple, copy it in */
  1042. if (entry.bytes < IndexTupleSize(t) - sizeof(IndexTupleData))
  1043. {
  1044. memcpy(datum, entry.pred, entry.bytes);
  1045. /* clear out old size */
  1046. t->t_info &= 0xe000;
  1047. /* or in new size */
  1048. t->t_info |= MAXALIGN(entry.bytes + sizeof(IndexTupleData));
  1049. return t;
  1050. }
  1051. else
  1052. {
  1053. /* generate a new index tuple for the compressed entry */
  1054. TupleDesc tupDesc = r->rd_att;
  1055. IndexTuple newtup;
  1056. char    *isnull;
  1057. int blank;
  1058. isnull = (char *) palloc(r->rd_rel->relnatts);
  1059. for (blank = 0; blank < r->rd_rel->relnatts; blank++)
  1060. isnull[blank] = ' ';
  1061. newtup = (IndexTuple) index_formtuple(tupDesc,
  1062.   (Datum *) &(entry.pred),
  1063.   isnull);
  1064. newtup->t_tid = t->t_tid;
  1065. pfree(isnull);
  1066. return newtup;
  1067. }
  1068. }
  1069. /*
  1070. ** initialize a GiST entry with a decompressed version of pred
  1071. */
  1072. void
  1073. gistdentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr, Relation r,
  1074.    Page pg, OffsetNumber o, int b, bool l)
  1075. {
  1076. GISTENTRY  *dep;
  1077. gistentryinit(*e, pr, r, pg, o, b, l);
  1078. if (giststate->haskeytype)
  1079. {
  1080. dep = (GISTENTRY *) ((*fmgr_faddr(&giststate->decompressFn)) (e));
  1081. gistentryinit(*e, dep->pred, dep->rel, dep->page, dep->offset, dep->bytes,
  1082.   dep->leafkey);
  1083. if (dep != e)
  1084. pfree(dep);
  1085. }
  1086. }
  1087. /*
  1088. ** initialize a GiST entry with a compressed version of pred
  1089. */
  1090. static void
  1091. gistcentryinit(GISTSTATE *giststate, GISTENTRY *e, char *pr, Relation r,
  1092.    Page pg, OffsetNumber o, int b, bool l)
  1093. {
  1094. GISTENTRY  *cep;
  1095. gistentryinit(*e, pr, r, pg, o, b, l);
  1096. if (giststate->haskeytype)
  1097. {
  1098. cep = (GISTENTRY *) ((*fmgr_faddr(&giststate->compressFn)) (e));
  1099. gistentryinit(*e, cep->pred, cep->rel, cep->page, cep->offset, cep->bytes,
  1100.   cep->leafkey);
  1101. if (cep != e)
  1102. pfree(cep);
  1103. }
  1104. }
  1105. #ifdef GISTDEBUG
  1106. /*
  1107. ** sloppy debugging support routine, requires recompilation with appropriate
  1108. ** "out" method for the index keys.  Could be fixed to find that info
  1109. ** in the catalogs...
  1110. */
  1111. void
  1112. _gistdump(Relation r)
  1113. {
  1114. Buffer buf;
  1115. Page page;
  1116. OffsetNumber offnum,
  1117. maxoff;
  1118. BlockNumber blkno;
  1119. BlockNumber nblocks;
  1120. GISTPageOpaque po;
  1121. IndexTuple itup;
  1122. BlockNumber itblkno;
  1123. OffsetNumber itoffno;
  1124. char    *datum;
  1125. char    *itkey;
  1126. nblocks = RelationGetNumberOfBlocks(r);
  1127. for (blkno = 0; blkno < nblocks; blkno++)
  1128. {
  1129. buf = ReadBuffer(r, blkno);
  1130. page = BufferGetPage(buf);
  1131. po = (GISTPageOpaque) PageGetSpecialPointer(page);
  1132. maxoff = PageGetMaxOffsetNumber(page);
  1133. printf("Page %d maxoff %d <%s>n", blkno, maxoff,
  1134.    (po->flags & F_LEAF ? "LEAF" : "INTERNAL"));
  1135. if (PageIsEmpty(page))
  1136. {
  1137. ReleaseBuffer(buf);
  1138. continue;
  1139. }
  1140. for (offnum = FirstOffsetNumber;
  1141.  offnum <= maxoff;
  1142.  offnum = OffsetNumberNext(offnum))
  1143. {
  1144. itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
  1145. itblkno = ItemPointerGetBlockNumber(&(itup->t_tid));
  1146. itoffno = ItemPointerGetOffsetNumber(&(itup->t_tid));
  1147. datum = ((char *) itup);
  1148. datum += sizeof(IndexTupleData);
  1149. /* get out function for type of key, and out it! */
  1150. itkey = (char *) int_range_out((INTRANGE *) datum);
  1151. /* itkey = " unable to print"; */
  1152. printf("t[%d] size %d heap <%d,%d> key:%sn",
  1153.    offnum, IndexTupleSize(itup), itblkno, itoffno, itkey);
  1154. pfree(itkey);
  1155. }
  1156. ReleaseBuffer(buf);
  1157. }
  1158. }
  1159. static char *
  1160. int_range_out(INTRANGE *r)
  1161. {
  1162. char    *result;
  1163. if (r == NULL)
  1164. return NULL;
  1165. result = (char *) palloc(80);
  1166. snprintf(result, 80, "[%d,%d): %d", r->lower, r->upper, r->flag);
  1167. return result;
  1168. }
  1169. #endif  /* defined GISTDEBUG */