You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ginentrypage.c 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. /*-------------------------------------------------------------------------
  2. *
  3. * ginentrypage.c
  4. * routines for handling GIN entry tree pages.
  5. *
  6. *
  7. * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
  8. * Portions Copyright (c) 1994, Regents of the University of California
  9. *
  10. * IDENTIFICATION
  11. * src/backend/access/gin/ginentrypage.c
  12. *-------------------------------------------------------------------------
  13. */
  14. #include "postgres.h"
  15. #include "access/gin_private.h"
  16. #include "access/ginxlog.h"
  17. #include "access/xloginsert.h"
  18. #include "miscadmin.h"
  19. #include "utils/rel.h"
  20. static void entrySplitPage(GinBtree btree, Buffer origbuf,
  21. GinBtreeStack *stack,
  22. GinBtreeEntryInsertData *insertData,
  23. BlockNumber updateblkno,
  24. Page *newlpage, Page *newrpage);
  25. /*
  26. * Form a tuple for entry tree.
  27. *
  28. * If the tuple would be too big to be stored, function throws a suitable
  29. * error if errorTooBig is true, or returns NULL if errorTooBig is false.
  30. *
  31. * See src/backend/access/gin/README for a description of the index tuple
  32. * format that is being built here. We build on the assumption that we
  33. * are making a leaf-level key entry containing a posting list of nipd items.
  34. * If the caller is actually trying to make a posting-tree entry, non-leaf
  35. * entry, or pending-list entry, it should pass dataSize = 0 and then overwrite
  36. * the t_tid fields as necessary. In any case, 'data' can be NULL to skip
  37. * filling in the posting list; the caller is responsible for filling it
  38. * afterwards if data = NULL and nipd > 0.
  39. */
  40. IndexTuple
  41. GinFormTuple(GinState *ginstate,
  42. OffsetNumber attnum, Datum key, GinNullCategory category,
  43. Pointer data, Size dataSize, int nipd,
  44. bool errorTooBig)
  45. {
  46. Datum datums[2];
  47. bool isnull[2];
  48. IndexTuple itup;
  49. uint32 newsize;
  50. /* Build the basic tuple: optional column number, plus key datum */
  51. if (ginstate->oneCol)
  52. {
  53. datums[0] = key;
  54. isnull[0] = (category != GIN_CAT_NORM_KEY);
  55. }
  56. else
  57. {
  58. datums[0] = UInt16GetDatum(attnum);
  59. isnull[0] = false;
  60. datums[1] = key;
  61. isnull[1] = (category != GIN_CAT_NORM_KEY);
  62. }
  63. itup = index_form_tuple(ginstate->tupdesc[attnum - 1], datums, isnull);
  64. /*
  65. * Determine and store offset to the posting list, making sure there is
  66. * room for the category byte if needed.
  67. *
  68. * Note: because index_form_tuple MAXALIGNs the tuple size, there may well
  69. * be some wasted pad space. Is it worth recomputing the data length to
  70. * prevent that? That would also allow us to Assert that the real data
  71. * doesn't overlap the GinNullCategory byte, which this code currently
  72. * takes on faith.
  73. */
  74. newsize = IndexTupleSize(itup);
  75. if (IndexTupleHasNulls(itup))
  76. {
  77. uint32 minsize;
  78. Assert(category != GIN_CAT_NORM_KEY);
  79. minsize = GinCategoryOffset(itup, ginstate) + sizeof(GinNullCategory);
  80. newsize = Max(newsize, minsize);
  81. }
  82. newsize = SHORTALIGN(newsize);
  83. GinSetPostingOffset(itup, newsize);
  84. GinSetNPosting(itup, nipd);
  85. /*
  86. * Add space needed for posting list, if any. Then check that the tuple
  87. * won't be too big to store.
  88. */
  89. newsize += dataSize;
  90. newsize = MAXALIGN(newsize);
  91. if (newsize > GinMaxItemSize)
  92. {
  93. if (errorTooBig)
  94. ereport(ERROR,
  95. (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
  96. errmsg("index row size %zu exceeds maximum %zu for index \"%s\"",
  97. (Size) newsize, (Size) GinMaxItemSize,
  98. RelationGetRelationName(ginstate->index))));
  99. pfree(itup);
  100. return NULL;
  101. }
  102. /*
  103. * Resize tuple if needed
  104. */
  105. if (newsize != IndexTupleSize(itup))
  106. {
  107. itup = repalloc(itup, newsize);
  108. /*
  109. * PostgreSQL 9.3 and earlier did not clear this new space, so we
  110. * might find uninitialized padding when reading tuples from disk.
  111. */
  112. memset((char *) itup + IndexTupleSize(itup),
  113. 0, newsize - IndexTupleSize(itup));
  114. /* set new size in tuple header */
  115. itup->t_info &= ~INDEX_SIZE_MASK;
  116. itup->t_info |= newsize;
  117. }
  118. /*
  119. * Copy in the posting list, if provided
  120. */
  121. if (data)
  122. {
  123. char *ptr = GinGetPosting(itup);
  124. memcpy(ptr, data, dataSize);
  125. }
  126. /*
  127. * Insert category byte, if needed
  128. */
  129. if (category != GIN_CAT_NORM_KEY)
  130. {
  131. Assert(IndexTupleHasNulls(itup));
  132. GinSetNullCategory(itup, ginstate, category);
  133. }
  134. return itup;
  135. }
  136. /*
  137. * Read item pointers from leaf entry tuple.
  138. *
  139. * Returns a palloc'd array of ItemPointers. The number of items is returned
  140. * in *nitems.
  141. */
  142. ItemPointer
  143. ginReadTuple(GinState *ginstate, OffsetNumber attnum, IndexTuple itup,
  144. int *nitems)
  145. {
  146. Pointer ptr = GinGetPosting(itup);
  147. int nipd = GinGetNPosting(itup);
  148. ItemPointer ipd;
  149. int ndecoded;
  150. if (GinItupIsCompressed(itup))
  151. {
  152. if (nipd > 0)
  153. {
  154. ipd = ginPostingListDecode((GinPostingList *) ptr, &ndecoded);
  155. if (nipd != ndecoded)
  156. elog(ERROR, "number of items mismatch in GIN entry tuple, %d in tuple header, %d decoded",
  157. nipd, ndecoded);
  158. }
  159. else
  160. {
  161. ipd = palloc(0);
  162. }
  163. }
  164. else
  165. {
  166. ipd = (ItemPointer) palloc(sizeof(ItemPointerData) * nipd);
  167. memcpy(ipd, ptr, sizeof(ItemPointerData) * nipd);
  168. }
  169. *nitems = nipd;
  170. return ipd;
  171. }
  172. /*
  173. * Form a non-leaf entry tuple by copying the key data from the given tuple,
  174. * which can be either a leaf or non-leaf entry tuple.
  175. *
  176. * Any posting list in the source tuple is not copied. The specified child
  177. * block number is inserted into t_tid.
  178. */
  179. static IndexTuple
  180. GinFormInteriorTuple(IndexTuple itup, Page page, BlockNumber childblk)
  181. {
  182. IndexTuple nitup;
  183. if (GinPageIsLeaf(page) && !GinIsPostingTree(itup))
  184. {
  185. /* Tuple contains a posting list, just copy stuff before that */
  186. uint32 origsize = GinGetPostingOffset(itup);
  187. origsize = MAXALIGN(origsize);
  188. nitup = (IndexTuple) palloc(origsize);
  189. memcpy(nitup, itup, origsize);
  190. /* ... be sure to fix the size header field ... */
  191. nitup->t_info &= ~INDEX_SIZE_MASK;
  192. nitup->t_info |= origsize;
  193. }
  194. else
  195. {
  196. /* Copy the tuple as-is */
  197. nitup = (IndexTuple) palloc(IndexTupleSize(itup));
  198. memcpy(nitup, itup, IndexTupleSize(itup));
  199. }
  200. /* Now insert the correct downlink */
  201. GinSetDownlink(nitup, childblk);
  202. return nitup;
  203. }
  204. /*
  205. * Entry tree is a "static", ie tuple never deletes from it,
  206. * so we don't use right bound, we use rightmost key instead.
  207. */
  208. static IndexTuple
  209. getRightMostTuple(Page page)
  210. {
  211. OffsetNumber maxoff = PageGetMaxOffsetNumber(page);
  212. return (IndexTuple) PageGetItem(page, PageGetItemId(page, maxoff));
  213. }
  214. static bool
  215. entryIsMoveRight(GinBtree btree, Page page)
  216. {
  217. IndexTuple itup;
  218. OffsetNumber attnum;
  219. Datum key;
  220. GinNullCategory category;
  221. if (GinPageRightMost(page))
  222. return false;
  223. itup = getRightMostTuple(page);
  224. attnum = gintuple_get_attrnum(btree->ginstate, itup);
  225. key = gintuple_get_key(btree->ginstate, itup, &category);
  226. if (ginCompareAttEntries(btree->ginstate,
  227. btree->entryAttnum, btree->entryKey, btree->entryCategory,
  228. attnum, key, category) > 0)
  229. return true;
  230. return false;
  231. }
  232. /*
  233. * Find correct tuple in non-leaf page. It supposed that
  234. * page correctly chosen and searching value SHOULD be on page
  235. */
  236. static BlockNumber
  237. entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
  238. {
  239. OffsetNumber low,
  240. high,
  241. maxoff;
  242. IndexTuple itup = NULL;
  243. int result;
  244. Page page = BufferGetPage(stack->buffer);
  245. Assert(!GinPageIsLeaf(page));
  246. Assert(!GinPageIsData(page));
  247. if (btree->fullScan)
  248. {
  249. stack->off = FirstOffsetNumber;
  250. stack->predictNumber *= PageGetMaxOffsetNumber(page);
  251. return btree->getLeftMostChild(btree, page);
  252. }
  253. low = FirstOffsetNumber;
  254. maxoff = high = PageGetMaxOffsetNumber(page);
  255. Assert(high >= low);
  256. high++;
  257. while (high > low)
  258. {
  259. OffsetNumber mid = low + ((high - low) / 2);
  260. if (mid == maxoff && GinPageRightMost(page))
  261. {
  262. /* Right infinity */
  263. result = -1;
  264. }
  265. else
  266. {
  267. OffsetNumber attnum;
  268. Datum key;
  269. GinNullCategory category;
  270. itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
  271. attnum = gintuple_get_attrnum(btree->ginstate, itup);
  272. key = gintuple_get_key(btree->ginstate, itup, &category);
  273. result = ginCompareAttEntries(btree->ginstate,
  274. btree->entryAttnum,
  275. btree->entryKey,
  276. btree->entryCategory,
  277. attnum, key, category);
  278. }
  279. if (result == 0)
  280. {
  281. stack->off = mid;
  282. Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
  283. return GinGetDownlink(itup);
  284. }
  285. else if (result > 0)
  286. low = mid + 1;
  287. else
  288. high = mid;
  289. }
  290. Assert(high >= FirstOffsetNumber && high <= maxoff);
  291. stack->off = high;
  292. itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, high));
  293. Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
  294. return GinGetDownlink(itup);
  295. }
  296. /*
  297. * Searches correct position for value on leaf page.
  298. * Page should be correctly chosen.
  299. * Returns true if value found on page.
  300. */
  301. static bool
  302. entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack)
  303. {
  304. Page page = BufferGetPage(stack->buffer);
  305. OffsetNumber low,
  306. high;
  307. Assert(GinPageIsLeaf(page));
  308. Assert(!GinPageIsData(page));
  309. if (btree->fullScan)
  310. {
  311. stack->off = FirstOffsetNumber;
  312. return true;
  313. }
  314. low = FirstOffsetNumber;
  315. high = PageGetMaxOffsetNumber(page);
  316. if (high < low)
  317. {
  318. stack->off = FirstOffsetNumber;
  319. return false;
  320. }
  321. high++;
  322. while (high > low)
  323. {
  324. OffsetNumber mid = low + ((high - low) / 2);
  325. IndexTuple itup;
  326. OffsetNumber attnum;
  327. Datum key;
  328. GinNullCategory category;
  329. int result;
  330. itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
  331. attnum = gintuple_get_attrnum(btree->ginstate, itup);
  332. key = gintuple_get_key(btree->ginstate, itup, &category);
  333. result = ginCompareAttEntries(btree->ginstate,
  334. btree->entryAttnum,
  335. btree->entryKey,
  336. btree->entryCategory,
  337. attnum, key, category);
  338. if (result == 0)
  339. {
  340. stack->off = mid;
  341. return true;
  342. }
  343. else if (result > 0)
  344. low = mid + 1;
  345. else
  346. high = mid;
  347. }
  348. stack->off = high;
  349. return false;
  350. }
  351. static OffsetNumber
  352. entryFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff)
  353. {
  354. OffsetNumber i,
  355. maxoff = PageGetMaxOffsetNumber(page);
  356. IndexTuple itup;
  357. Assert(!GinPageIsLeaf(page));
  358. Assert(!GinPageIsData(page));
  359. /* if page isn't changed, we returns storedOff */
  360. if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
  361. {
  362. itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, storedOff));
  363. if (GinGetDownlink(itup) == blkno)
  364. return storedOff;
  365. /*
  366. * we hope, that needed pointer goes to right. It's true if there
  367. * wasn't a deletion
  368. */
  369. for (i = storedOff + 1; i <= maxoff; i++)
  370. {
  371. itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
  372. if (GinGetDownlink(itup) == blkno)
  373. return i;
  374. }
  375. maxoff = storedOff - 1;
  376. }
  377. /* last chance */
  378. for (i = FirstOffsetNumber; i <= maxoff; i++)
  379. {
  380. itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
  381. if (GinGetDownlink(itup) == blkno)
  382. return i;
  383. }
  384. return InvalidOffsetNumber;
  385. }
  386. static BlockNumber
  387. entryGetLeftMostPage(GinBtree btree, Page page)
  388. {
  389. IndexTuple itup;
  390. Assert(!GinPageIsLeaf(page));
  391. Assert(!GinPageIsData(page));
  392. Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);
  393. itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
  394. return GinGetDownlink(itup);
  395. }
  396. static bool
  397. entryIsEnoughSpace(GinBtree btree, Buffer buf, OffsetNumber off,
  398. GinBtreeEntryInsertData *insertData)
  399. {
  400. Size releasedsz = 0;
  401. Size addedsz;
  402. Page page = BufferGetPage(buf);
  403. Assert(insertData->entry);
  404. Assert(!GinPageIsData(page));
  405. if (insertData->isDelete)
  406. {
  407. IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
  408. releasedsz = MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
  409. }
  410. addedsz = MAXALIGN(IndexTupleSize(insertData->entry)) + sizeof(ItemIdData);
  411. if (PageGetFreeSpace(page) + releasedsz >= addedsz)
  412. return true;
  413. return false;
  414. }
  415. /*
  416. * Delete tuple on leaf page if tuples existed and we
  417. * should update it, update old child blkno to new right page
  418. * if child split occurred
  419. */
  420. static void
  421. entryPreparePage(GinBtree btree, Page page, OffsetNumber off,
  422. GinBtreeEntryInsertData *insertData, BlockNumber updateblkno)
  423. {
  424. Assert(insertData->entry);
  425. Assert(!GinPageIsData(page));
  426. if (insertData->isDelete)
  427. {
  428. Assert(GinPageIsLeaf(page));
  429. PageIndexTupleDelete(page, off);
  430. }
  431. if (!GinPageIsLeaf(page) && updateblkno != InvalidBlockNumber)
  432. {
  433. IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
  434. GinSetDownlink(itup, updateblkno);
  435. }
  436. }
  437. /*
  438. * Prepare to insert data on an entry page.
  439. *
  440. * If it will fit, return GPTP_INSERT after doing whatever setup is needed
  441. * before we enter the insertion critical section. *ptp_workspace can be
  442. * set to pass information along to the execPlaceToPage function.
  443. *
  444. * If it won't fit, perform a page split and return two temporary page
  445. * images into *newlpage and *newrpage, with result GPTP_SPLIT.
  446. *
  447. * In neither case should the given page buffer be modified here.
  448. *
  449. * Note: on insertion to an internal node, in addition to inserting the given
  450. * item, the downlink of the existing item at stack->off will be updated to
  451. * point to updateblkno.
  452. */
  453. static GinPlaceToPageRC
  454. entryBeginPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
  455. void *insertPayload, BlockNumber updateblkno,
  456. void **ptp_workspace,
  457. Page *newlpage, Page *newrpage)
  458. {
  459. GinBtreeEntryInsertData *insertData = insertPayload;
  460. OffsetNumber off = stack->off;
  461. /* If it doesn't fit, deal with split case */
  462. if (!entryIsEnoughSpace(btree, buf, off, insertData))
  463. {
  464. entrySplitPage(btree, buf, stack, insertData, updateblkno,
  465. newlpage, newrpage);
  466. return GPTP_SPLIT;
  467. }
  468. /* Else, we're ready to proceed with insertion */
  469. return GPTP_INSERT;
  470. }
  471. /*
  472. * Perform data insertion after beginPlaceToPage has decided it will fit.
  473. *
  474. * This is invoked within a critical section, and XLOG record creation (if
  475. * needed) is already started. The target buffer is registered in slot 0.
  476. */
  477. static void
  478. entryExecPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
  479. void *insertPayload, BlockNumber updateblkno,
  480. void *ptp_workspace)
  481. {
  482. GinBtreeEntryInsertData *insertData = insertPayload;
  483. Page page = BufferGetPage(buf);
  484. OffsetNumber off = stack->off;
  485. OffsetNumber placed;
  486. entryPreparePage(btree, page, off, insertData, updateblkno);
  487. placed = PageAddItem(page,
  488. (Item) insertData->entry,
  489. IndexTupleSize(insertData->entry),
  490. off, false, false);
  491. if (placed != off)
  492. elog(ERROR, "failed to add item to index page in \"%s\"",
  493. RelationGetRelationName(btree->index));
  494. if (RelationNeedsWAL(btree->index))
  495. {
  496. /*
  497. * This must be static, because it has to survive until XLogInsert,
  498. * and we can't palloc here. Ugly, but the XLogInsert infrastructure
  499. * isn't reentrant anyway.
  500. */
  501. static ginxlogInsertEntry data;
  502. data.isDelete = insertData->isDelete;
  503. data.offset = off;
  504. XLogRegisterBufData(0, (char *) &data,
  505. offsetof(ginxlogInsertEntry, tuple));
  506. XLogRegisterBufData(0, (char *) insertData->entry,
  507. IndexTupleSize(insertData->entry));
  508. }
  509. }
  510. /*
  511. * Split entry page and insert new data.
  512. *
  513. * Returns new temp pages to *newlpage and *newrpage.
  514. * The original buffer is left untouched.
  515. */
  516. static void
  517. entrySplitPage(GinBtree btree, Buffer origbuf,
  518. GinBtreeStack *stack,
  519. GinBtreeEntryInsertData *insertData,
  520. BlockNumber updateblkno,
  521. Page *newlpage, Page *newrpage)
  522. {
  523. OffsetNumber off = stack->off;
  524. OffsetNumber i,
  525. maxoff,
  526. separator = InvalidOffsetNumber;
  527. Size totalsize = 0;
  528. Size lsize = 0,
  529. size;
  530. char *ptr;
  531. IndexTuple itup;
  532. Page page;
  533. Page lpage = PageGetTempPageCopy(BufferGetPage(origbuf));
  534. Page rpage = PageGetTempPageCopy(BufferGetPage(origbuf));
  535. Size pageSize = PageGetPageSize(lpage);
  536. PGAlignedBlock tupstore[2]; /* could need 2 pages' worth of tuples */
  537. entryPreparePage(btree, lpage, off, insertData, updateblkno);
  538. /*
  539. * First, append all the existing tuples and the new tuple we're inserting
  540. * one after another in a temporary workspace.
  541. */
  542. maxoff = PageGetMaxOffsetNumber(lpage);
  543. ptr = tupstore[0].data;
  544. for (i = FirstOffsetNumber; i <= maxoff; i++)
  545. {
  546. if (i == off)
  547. {
  548. size = MAXALIGN(IndexTupleSize(insertData->entry));
  549. memcpy(ptr, insertData->entry, size);
  550. ptr += size;
  551. totalsize += size + sizeof(ItemIdData);
  552. }
  553. itup = (IndexTuple) PageGetItem(lpage, PageGetItemId(lpage, i));
  554. size = MAXALIGN(IndexTupleSize(itup));
  555. memcpy(ptr, itup, size);
  556. ptr += size;
  557. totalsize += size + sizeof(ItemIdData);
  558. }
  559. if (off == maxoff + 1)
  560. {
  561. size = MAXALIGN(IndexTupleSize(insertData->entry));
  562. memcpy(ptr, insertData->entry, size);
  563. ptr += size;
  564. totalsize += size + sizeof(ItemIdData);
  565. }
  566. /*
  567. * Initialize the left and right pages, and copy all the tuples back to
  568. * them.
  569. */
  570. GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
  571. GinInitPage(lpage, GinPageGetOpaque(rpage)->flags, pageSize);
  572. ptr = tupstore[0].data;
  573. maxoff++;
  574. lsize = 0;
  575. page = lpage;
  576. for (i = FirstOffsetNumber; i <= maxoff; i++)
  577. {
  578. itup = (IndexTuple) ptr;
  579. /*
  580. * Decide where to split. We try to equalize the pages' total data
  581. * size, not number of tuples.
  582. */
  583. if (lsize > totalsize / 2)
  584. {
  585. if (separator == InvalidOffsetNumber)
  586. separator = i - 1;
  587. page = rpage;
  588. }
  589. else
  590. {
  591. lsize += MAXALIGN(IndexTupleSize(itup)) + sizeof(ItemIdData);
  592. }
  593. if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
  594. elog(ERROR, "failed to add item to index page in \"%s\"",
  595. RelationGetRelationName(btree->index));
  596. ptr += MAXALIGN(IndexTupleSize(itup));
  597. }
  598. /* return temp pages to caller */
  599. *newlpage = lpage;
  600. *newrpage = rpage;
  601. }
  602. /*
  603. * Construct insertion payload for inserting the downlink for given buffer.
  604. */
  605. static void *
  606. entryPrepareDownlink(GinBtree btree, Buffer lbuf)
  607. {
  608. GinBtreeEntryInsertData *insertData;
  609. Page lpage = BufferGetPage(lbuf);
  610. BlockNumber lblkno = BufferGetBlockNumber(lbuf);
  611. IndexTuple itup;
  612. itup = getRightMostTuple(lpage);
  613. insertData = palloc(sizeof(GinBtreeEntryInsertData));
  614. insertData->entry = GinFormInteriorTuple(itup, lpage, lblkno);
  615. insertData->isDelete = false;
  616. return insertData;
  617. }
  618. /*
  619. * Fills new root by rightest values from child.
  620. * Also called from ginxlog, should not use btree
  621. */
  622. void
  623. ginEntryFillRoot(GinBtree btree, Page root,
  624. BlockNumber lblkno, Page lpage,
  625. BlockNumber rblkno, Page rpage)
  626. {
  627. IndexTuple itup;
  628. itup = GinFormInteriorTuple(getRightMostTuple(lpage), lpage, lblkno);
  629. if (PageAddItem(root, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
  630. elog(ERROR, "failed to add item to index root page");
  631. pfree(itup);
  632. itup = GinFormInteriorTuple(getRightMostTuple(rpage), rpage, rblkno);
  633. if (PageAddItem(root, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
  634. elog(ERROR, "failed to add item to index root page");
  635. pfree(itup);
  636. }
  637. /*
  638. * Set up GinBtree for entry page access
  639. *
  640. * Note: during WAL recovery, there may be no valid data in ginstate
  641. * other than a faked-up Relation pointer; the key datum is bogus too.
  642. */
  643. void
  644. ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum,
  645. Datum key, GinNullCategory category,
  646. GinState *ginstate)
  647. {
  648. memset(btree, 0, sizeof(GinBtreeData));
  649. btree->index = ginstate->index;
  650. btree->rootBlkno = GIN_ROOT_BLKNO;
  651. btree->ginstate = ginstate;
  652. btree->findChildPage = entryLocateEntry;
  653. btree->getLeftMostChild = entryGetLeftMostPage;
  654. btree->isMoveRight = entryIsMoveRight;
  655. btree->findItem = entryLocateLeafEntry;
  656. btree->findChildPtr = entryFindChildPtr;
  657. btree->beginPlaceToPage = entryBeginPlaceToPage;
  658. btree->execPlaceToPage = entryExecPlaceToPage;
  659. btree->fillRoot = ginEntryFillRoot;
  660. btree->prepareDownlink = entryPrepareDownlink;
  661. btree->isData = false;
  662. btree->fullScan = false;
  663. btree->isBuild = false;
  664. btree->entryAttnum = attnum;
  665. btree->entryKey = key;
  666. btree->entryCategory = category;
  667. }