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.

gindatapage.c 52KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938
  1. /*-------------------------------------------------------------------------
  2. *
  3. * gindatapage.c
  4. * routines for handling GIN posting 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/gindatapage.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 "lib/ilist.h"
  19. #include "miscadmin.h"
  20. #include "storage/predicate.h"
  21. #include "utils/rel.h"
  22. /*
  23. * Min, Max and Target size of posting lists stored on leaf pages, in bytes.
  24. *
  25. * The code can deal with any size, but random access is more efficient when
  26. * a number of smaller lists are stored, rather than one big list. If a
  27. * posting list would become larger than Max size as a result of insertions,
  28. * it is split into two. If a posting list would be smaller than minimum
  29. * size, it is merged with the next posting list.
  30. */
  31. #define GinPostingListSegmentMaxSize 384
  32. #define GinPostingListSegmentTargetSize 256
  33. #define GinPostingListSegmentMinSize 128
  34. /*
  35. * At least this many items fit in a GinPostingListSegmentMaxSize-bytes
  36. * long segment. This is used when estimating how much space is required
  37. * for N items, at minimum.
  38. */
  39. #define MinTuplesPerSegment ((GinPostingListSegmentMaxSize - 2) / 6)
  40. /*
  41. * A working struct for manipulating a posting tree leaf page.
  42. */
  43. typedef struct
  44. {
  45. dlist_head segments; /* a list of leafSegmentInfos */
  46. /*
  47. * The following fields represent how the segments are split across pages,
  48. * if a page split is required. Filled in by leafRepackItems.
  49. */
  50. dlist_node *lastleft; /* last segment on left page */
  51. int lsize; /* total size on left page */
  52. int rsize; /* total size on right page */
  53. bool oldformat; /* page is in pre-9.4 format on disk */
  54. /*
  55. * If we need WAL data representing the reconstructed leaf page, it's
  56. * stored here by computeLeafRecompressWALData.
  57. */
  58. char *walinfo; /* buffer start */
  59. int walinfolen; /* and length */
  60. } disassembledLeaf;
  61. typedef struct
  62. {
  63. dlist_node node; /* linked list pointers */
  64. /*-------------
  65. * 'action' indicates the status of this in-memory segment, compared to
  66. * what's on disk. It is one of the GIN_SEGMENT_* action codes:
  67. *
  68. * UNMODIFIED no changes
  69. * DELETE the segment is to be removed. 'seg' and 'items' are
  70. * ignored
  71. * INSERT this is a completely new segment
  72. * REPLACE this replaces an existing segment with new content
  73. * ADDITEMS like REPLACE, but no items have been removed, and we track
  74. * in detail what items have been added to this segment, in
  75. * 'modifieditems'
  76. *-------------
  77. */
  78. char action;
  79. ItemPointerData *modifieditems;
  80. uint16 nmodifieditems;
  81. /*
  82. * The following fields represent the items in this segment. If 'items' is
  83. * not NULL, it contains a palloc'd array of the itemsin this segment. If
  84. * 'seg' is not NULL, it contains the items in an already-compressed
  85. * format. It can point to an on-disk page (!modified), or a palloc'd
  86. * segment in memory. If both are set, they must represent the same items.
  87. */
  88. GinPostingList *seg;
  89. ItemPointer items;
  90. int nitems; /* # of items in 'items', if items != NULL */
  91. } leafSegmentInfo;
  92. static ItemPointer dataLeafPageGetUncompressed(Page page, int *nitems);
  93. static void dataSplitPageInternal(GinBtree btree, Buffer origbuf,
  94. GinBtreeStack *stack,
  95. void *insertdata, BlockNumber updateblkno,
  96. Page *newlpage, Page *newrpage);
  97. static disassembledLeaf *disassembleLeaf(Page page);
  98. static bool leafRepackItems(disassembledLeaf *leaf, ItemPointer remaining);
  99. static bool addItemsToLeaf(disassembledLeaf *leaf, ItemPointer newItems,
  100. int nNewItems);
  101. static void computeLeafRecompressWALData(disassembledLeaf *leaf);
  102. static void dataPlaceToPageLeafRecompress(Buffer buf, disassembledLeaf *leaf);
  103. static void dataPlaceToPageLeafSplit(disassembledLeaf *leaf,
  104. ItemPointerData lbound, ItemPointerData rbound,
  105. Page lpage, Page rpage);
  106. /*
  107. * Read TIDs from leaf data page to single uncompressed array. The TIDs are
  108. * returned in ascending order.
  109. *
  110. * advancePast is a hint, indicating that the caller is only interested in
  111. * TIDs > advancePast. To return all items, use ItemPointerSetMin.
  112. *
  113. * Note: This function can still return items smaller than advancePast that
  114. * are in the same posting list as the items of interest, so the caller must
  115. * still check all the returned items. But passing it allows this function to
  116. * skip whole posting lists.
  117. */
  118. ItemPointer
  119. GinDataLeafPageGetItems(Page page, int *nitems, ItemPointerData advancePast)
  120. {
  121. ItemPointer result;
  122. if (GinPageIsCompressed(page))
  123. {
  124. GinPostingList *seg = GinDataLeafPageGetPostingList(page);
  125. Size len = GinDataLeafPageGetPostingListSize(page);
  126. Pointer endptr = ((Pointer) seg) + len;
  127. GinPostingList *next;
  128. /* Skip to the segment containing advancePast+1 */
  129. if (ItemPointerIsValid(&advancePast))
  130. {
  131. next = GinNextPostingListSegment(seg);
  132. while ((Pointer) next < endptr &&
  133. ginCompareItemPointers(&next->first, &advancePast) <= 0)
  134. {
  135. seg = next;
  136. next = GinNextPostingListSegment(seg);
  137. }
  138. len = endptr - (Pointer) seg;
  139. }
  140. if (len > 0)
  141. result = ginPostingListDecodeAllSegments(seg, len, nitems);
  142. else
  143. {
  144. result = NULL;
  145. *nitems = 0;
  146. }
  147. }
  148. else
  149. {
  150. ItemPointer tmp = dataLeafPageGetUncompressed(page, nitems);
  151. result = palloc((*nitems) * sizeof(ItemPointerData));
  152. memcpy(result, tmp, (*nitems) * sizeof(ItemPointerData));
  153. }
  154. return result;
  155. }
  156. /*
  157. * Places all TIDs from leaf data page to bitmap.
  158. */
  159. int
  160. GinDataLeafPageGetItemsToTbm(Page page, TIDBitmap *tbm)
  161. {
  162. ItemPointer uncompressed;
  163. int nitems;
  164. if (GinPageIsCompressed(page))
  165. {
  166. GinPostingList *segment = GinDataLeafPageGetPostingList(page);
  167. Size len = GinDataLeafPageGetPostingListSize(page);
  168. nitems = ginPostingListDecodeAllSegmentsToTbm(segment, len, tbm);
  169. }
  170. else
  171. {
  172. uncompressed = dataLeafPageGetUncompressed(page, &nitems);
  173. if (nitems > 0)
  174. tbm_add_tuples(tbm, uncompressed, nitems, false);
  175. }
  176. return nitems;
  177. }
  178. /*
  179. * Get pointer to the uncompressed array of items on a pre-9.4 format
  180. * uncompressed leaf page. The number of items in the array is returned in
  181. * *nitems.
  182. */
  183. static ItemPointer
  184. dataLeafPageGetUncompressed(Page page, int *nitems)
  185. {
  186. ItemPointer items;
  187. Assert(!GinPageIsCompressed(page));
  188. /*
  189. * In the old pre-9.4 page format, the whole page content is used for
  190. * uncompressed items, and the number of items is stored in 'maxoff'
  191. */
  192. items = (ItemPointer) GinDataPageGetData(page);
  193. *nitems = GinPageGetOpaque(page)->maxoff;
  194. return items;
  195. }
  196. /*
  197. * Check if we should follow the right link to find the item we're searching
  198. * for.
  199. *
  200. * Compares inserting item pointer with the right bound of the current page.
  201. */
  202. static bool
  203. dataIsMoveRight(GinBtree btree, Page page)
  204. {
  205. ItemPointer iptr = GinDataPageGetRightBound(page);
  206. if (GinPageRightMost(page))
  207. return false;
  208. return (ginCompareItemPointers(&btree->itemptr, iptr) > 0) ? true : false;
  209. }
  210. /*
  211. * Find correct PostingItem in non-leaf page. It is assumed that this is
  212. * the correct page, and the searched value SHOULD be on the page.
  213. */
  214. static BlockNumber
  215. dataLocateItem(GinBtree btree, GinBtreeStack *stack)
  216. {
  217. OffsetNumber low,
  218. high,
  219. maxoff;
  220. PostingItem *pitem = NULL;
  221. int result;
  222. Page page = BufferGetPage(stack->buffer);
  223. Assert(!GinPageIsLeaf(page));
  224. Assert(GinPageIsData(page));
  225. if (btree->fullScan)
  226. {
  227. stack->off = FirstOffsetNumber;
  228. stack->predictNumber *= GinPageGetOpaque(page)->maxoff;
  229. return btree->getLeftMostChild(btree, page);
  230. }
  231. low = FirstOffsetNumber;
  232. maxoff = high = GinPageGetOpaque(page)->maxoff;
  233. Assert(high >= low);
  234. high++;
  235. while (high > low)
  236. {
  237. OffsetNumber mid = low + ((high - low) / 2);
  238. pitem = GinDataPageGetPostingItem(page, mid);
  239. if (mid == maxoff)
  240. {
  241. /*
  242. * Right infinity, page already correctly chosen with a help of
  243. * dataIsMoveRight
  244. */
  245. result = -1;
  246. }
  247. else
  248. {
  249. pitem = GinDataPageGetPostingItem(page, mid);
  250. result = ginCompareItemPointers(&btree->itemptr, &(pitem->key));
  251. }
  252. if (result == 0)
  253. {
  254. stack->off = mid;
  255. return PostingItemGetBlockNumber(pitem);
  256. }
  257. else if (result > 0)
  258. low = mid + 1;
  259. else
  260. high = mid;
  261. }
  262. Assert(high >= FirstOffsetNumber && high <= maxoff);
  263. stack->off = high;
  264. pitem = GinDataPageGetPostingItem(page, high);
  265. return PostingItemGetBlockNumber(pitem);
  266. }
  267. /*
  268. * Find link to blkno on non-leaf page, returns offset of PostingItem
  269. */
  270. static OffsetNumber
  271. dataFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff)
  272. {
  273. OffsetNumber i,
  274. maxoff = GinPageGetOpaque(page)->maxoff;
  275. PostingItem *pitem;
  276. Assert(!GinPageIsLeaf(page));
  277. Assert(GinPageIsData(page));
  278. /* if page isn't changed, we return storedOff */
  279. if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
  280. {
  281. pitem = GinDataPageGetPostingItem(page, storedOff);
  282. if (PostingItemGetBlockNumber(pitem) == blkno)
  283. return storedOff;
  284. /*
  285. * we hope, that needed pointer goes to right. It's true if there
  286. * wasn't a deletion
  287. */
  288. for (i = storedOff + 1; i <= maxoff; i++)
  289. {
  290. pitem = GinDataPageGetPostingItem(page, i);
  291. if (PostingItemGetBlockNumber(pitem) == blkno)
  292. return i;
  293. }
  294. maxoff = storedOff - 1;
  295. }
  296. /* last chance */
  297. for (i = FirstOffsetNumber; i <= maxoff; i++)
  298. {
  299. pitem = GinDataPageGetPostingItem(page, i);
  300. if (PostingItemGetBlockNumber(pitem) == blkno)
  301. return i;
  302. }
  303. return InvalidOffsetNumber;
  304. }
  305. /*
  306. * Return blkno of leftmost child
  307. */
  308. static BlockNumber
  309. dataGetLeftMostPage(GinBtree btree, Page page)
  310. {
  311. PostingItem *pitem;
  312. Assert(!GinPageIsLeaf(page));
  313. Assert(GinPageIsData(page));
  314. Assert(GinPageGetOpaque(page)->maxoff >= FirstOffsetNumber);
  315. pitem = GinDataPageGetPostingItem(page, FirstOffsetNumber);
  316. return PostingItemGetBlockNumber(pitem);
  317. }
  318. /*
  319. * Add PostingItem to a non-leaf page.
  320. */
  321. void
  322. GinDataPageAddPostingItem(Page page, PostingItem *data, OffsetNumber offset)
  323. {
  324. OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
  325. char *ptr;
  326. Assert(PostingItemGetBlockNumber(data) != InvalidBlockNumber);
  327. Assert(!GinPageIsLeaf(page));
  328. if (offset == InvalidOffsetNumber)
  329. {
  330. ptr = (char *) GinDataPageGetPostingItem(page, maxoff + 1);
  331. }
  332. else
  333. {
  334. ptr = (char *) GinDataPageGetPostingItem(page, offset);
  335. if (offset != maxoff + 1)
  336. memmove(ptr + sizeof(PostingItem),
  337. ptr,
  338. (maxoff - offset + 1) * sizeof(PostingItem));
  339. }
  340. memcpy(ptr, data, sizeof(PostingItem));
  341. maxoff++;
  342. GinPageGetOpaque(page)->maxoff = maxoff;
  343. /*
  344. * Also set pd_lower to the end of the posting items, to follow the
  345. * "standard" page layout, so that we can squeeze out the unused space
  346. * from full-page images.
  347. */
  348. GinDataPageSetDataSize(page, maxoff * sizeof(PostingItem));
  349. }
  350. /*
  351. * Delete posting item from non-leaf page
  352. */
  353. void
  354. GinPageDeletePostingItem(Page page, OffsetNumber offset)
  355. {
  356. OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
  357. Assert(!GinPageIsLeaf(page));
  358. Assert(offset >= FirstOffsetNumber && offset <= maxoff);
  359. if (offset != maxoff)
  360. memmove(GinDataPageGetPostingItem(page, offset),
  361. GinDataPageGetPostingItem(page, offset + 1),
  362. sizeof(PostingItem) * (maxoff - offset));
  363. maxoff--;
  364. GinPageGetOpaque(page)->maxoff = maxoff;
  365. GinDataPageSetDataSize(page, maxoff * sizeof(PostingItem));
  366. }
  367. /*
  368. * Prepare to insert data on a leaf data page.
  369. *
  370. * If it will fit, return GPTP_INSERT after doing whatever setup is needed
  371. * before we enter the insertion critical section. *ptp_workspace can be
  372. * set to pass information along to the execPlaceToPage function.
  373. *
  374. * If it won't fit, perform a page split and return two temporary page
  375. * images into *newlpage and *newrpage, with result GPTP_SPLIT.
  376. *
  377. * In neither case should the given page buffer be modified here.
  378. */
  379. static GinPlaceToPageRC
  380. dataBeginPlaceToPageLeaf(GinBtree btree, Buffer buf, GinBtreeStack *stack,
  381. void *insertdata,
  382. void **ptp_workspace,
  383. Page *newlpage, Page *newrpage)
  384. {
  385. GinBtreeDataLeafInsertData *items = insertdata;
  386. ItemPointer newItems = &items->items[items->curitem];
  387. int maxitems = items->nitem - items->curitem;
  388. Page page = BufferGetPage(buf);
  389. int i;
  390. ItemPointerData rbound;
  391. ItemPointerData lbound;
  392. bool needsplit;
  393. bool append;
  394. int segsize;
  395. Size freespace;
  396. disassembledLeaf *leaf;
  397. leafSegmentInfo *lastleftinfo;
  398. ItemPointerData maxOldItem;
  399. ItemPointerData remaining;
  400. rbound = *GinDataPageGetRightBound(page);
  401. /*
  402. * Count how many of the new items belong to this page.
  403. */
  404. if (!GinPageRightMost(page))
  405. {
  406. for (i = 0; i < maxitems; i++)
  407. {
  408. if (ginCompareItemPointers(&newItems[i], &rbound) > 0)
  409. {
  410. /*
  411. * This needs to go to some other location in the tree. (The
  412. * caller should've chosen the insert location so that at
  413. * least the first item goes here.)
  414. */
  415. Assert(i > 0);
  416. break;
  417. }
  418. }
  419. maxitems = i;
  420. }
  421. /* Disassemble the data on the page */
  422. leaf = disassembleLeaf(page);
  423. /*
  424. * Are we appending to the end of the page? IOW, are all the new items
  425. * larger than any of the existing items.
  426. */
  427. if (!dlist_is_empty(&leaf->segments))
  428. {
  429. lastleftinfo = dlist_container(leafSegmentInfo, node,
  430. dlist_tail_node(&leaf->segments));
  431. if (!lastleftinfo->items)
  432. lastleftinfo->items = ginPostingListDecode(lastleftinfo->seg,
  433. &lastleftinfo->nitems);
  434. maxOldItem = lastleftinfo->items[lastleftinfo->nitems - 1];
  435. if (ginCompareItemPointers(&newItems[0], &maxOldItem) >= 0)
  436. append = true;
  437. else
  438. append = false;
  439. }
  440. else
  441. {
  442. ItemPointerSetMin(&maxOldItem);
  443. append = true;
  444. }
  445. /*
  446. * If we're appending to the end of the page, we will append as many items
  447. * as we can fit (after splitting), and stop when the pages becomes full.
  448. * Otherwise we have to limit the number of new items to insert, because
  449. * once we start packing we can't just stop when we run out of space,
  450. * because we must make sure that all the old items still fit.
  451. */
  452. if (GinPageIsCompressed(page))
  453. freespace = GinDataLeafPageGetFreeSpace(page);
  454. else
  455. freespace = 0;
  456. if (append)
  457. {
  458. /*
  459. * Even when appending, trying to append more items than will fit is
  460. * not completely free, because we will merge the new items and old
  461. * items into an array below. In the best case, every new item fits in
  462. * a single byte, and we can use all the free space on the old page as
  463. * well as the new page. For simplicity, ignore segment overhead etc.
  464. */
  465. maxitems = Min(maxitems, freespace + GinDataPageMaxDataSize);
  466. }
  467. else
  468. {
  469. /*
  470. * Calculate a conservative estimate of how many new items we can fit
  471. * on the two pages after splitting.
  472. *
  473. * We can use any remaining free space on the old page to store full
  474. * segments, as well as the new page. Each full-sized segment can hold
  475. * at least MinTuplesPerSegment items
  476. */
  477. int nnewsegments;
  478. nnewsegments = freespace / GinPostingListSegmentMaxSize;
  479. nnewsegments += GinDataPageMaxDataSize / GinPostingListSegmentMaxSize;
  480. maxitems = Min(maxitems, nnewsegments * MinTuplesPerSegment);
  481. }
  482. /* Add the new items to the segment list */
  483. if (!addItemsToLeaf(leaf, newItems, maxitems))
  484. {
  485. /* all items were duplicates, we have nothing to do */
  486. items->curitem += maxitems;
  487. return GPTP_NO_WORK;
  488. }
  489. /*
  490. * Pack the items back to compressed segments, ready for writing to disk.
  491. */
  492. needsplit = leafRepackItems(leaf, &remaining);
  493. /*
  494. * Did all the new items fit?
  495. *
  496. * If we're appending, it's OK if they didn't. But as a sanity check,
  497. * verify that all the old items fit.
  498. */
  499. if (ItemPointerIsValid(&remaining))
  500. {
  501. if (!append || ItemPointerCompare(&maxOldItem, &remaining) >= 0)
  502. elog(ERROR, "could not split GIN page; all old items didn't fit");
  503. /* Count how many of the new items did fit. */
  504. for (i = 0; i < maxitems; i++)
  505. {
  506. if (ginCompareItemPointers(&newItems[i], &remaining) >= 0)
  507. break;
  508. }
  509. if (i == 0)
  510. elog(ERROR, "could not split GIN page; no new items fit");
  511. maxitems = i;
  512. }
  513. if (!needsplit)
  514. {
  515. /*
  516. * Great, all the items fit on a single page. If needed, prepare data
  517. * for a WAL record describing the changes we'll make.
  518. */
  519. if (RelationNeedsWAL(btree->index))
  520. computeLeafRecompressWALData(leaf);
  521. /*
  522. * We're ready to enter the critical section, but
  523. * dataExecPlaceToPageLeaf will need access to the "leaf" data.
  524. */
  525. *ptp_workspace = leaf;
  526. if (append)
  527. elog(DEBUG2, "appended %d new items to block %u; %d bytes (%d to go)",
  528. maxitems, BufferGetBlockNumber(buf), (int) leaf->lsize,
  529. items->nitem - items->curitem - maxitems);
  530. else
  531. elog(DEBUG2, "inserted %d new items to block %u; %d bytes (%d to go)",
  532. maxitems, BufferGetBlockNumber(buf), (int) leaf->lsize,
  533. items->nitem - items->curitem - maxitems);
  534. }
  535. else
  536. {
  537. /*
  538. * Have to split.
  539. *
  540. * leafRepackItems already divided the segments between the left and
  541. * the right page. It filled the left page as full as possible, and
  542. * put the rest to the right page. When building a new index, that's
  543. * good, because the table is scanned from beginning to end and there
  544. * won't be any more insertions to the left page during the build.
  545. * This packs the index as tight as possible. But otherwise, split
  546. * 50/50, by moving segments from the left page to the right page
  547. * until they're balanced.
  548. *
  549. * As a further heuristic, when appending items to the end of the
  550. * page, try to make the left page 75% full, on the assumption that
  551. * subsequent insertions will probably also go to the end. This packs
  552. * the index somewhat tighter when appending to a table, which is very
  553. * common.
  554. */
  555. if (!btree->isBuild)
  556. {
  557. while (dlist_has_prev(&leaf->segments, leaf->lastleft))
  558. {
  559. lastleftinfo = dlist_container(leafSegmentInfo, node, leaf->lastleft);
  560. /* ignore deleted segments */
  561. if (lastleftinfo->action != GIN_SEGMENT_DELETE)
  562. {
  563. segsize = SizeOfGinPostingList(lastleftinfo->seg);
  564. /*
  565. * Note that we check that the right page doesn't become
  566. * more full than the left page even when appending. It's
  567. * possible that we added enough items to make both pages
  568. * more than 75% full.
  569. */
  570. if ((leaf->lsize - segsize) - (leaf->rsize + segsize) < 0)
  571. break;
  572. if (append)
  573. {
  574. if ((leaf->lsize - segsize) < (BLCKSZ * 3) / 4)
  575. break;
  576. }
  577. leaf->lsize -= segsize;
  578. leaf->rsize += segsize;
  579. }
  580. leaf->lastleft = dlist_prev_node(&leaf->segments, leaf->lastleft);
  581. }
  582. }
  583. Assert(leaf->lsize <= GinDataPageMaxDataSize);
  584. Assert(leaf->rsize <= GinDataPageMaxDataSize);
  585. /*
  586. * Fetch the max item in the left page's last segment; it becomes the
  587. * right bound of the page.
  588. */
  589. lastleftinfo = dlist_container(leafSegmentInfo, node, leaf->lastleft);
  590. if (!lastleftinfo->items)
  591. lastleftinfo->items = ginPostingListDecode(lastleftinfo->seg,
  592. &lastleftinfo->nitems);
  593. lbound = lastleftinfo->items[lastleftinfo->nitems - 1];
  594. /*
  595. * Now allocate a couple of temporary page images, and fill them.
  596. */
  597. *newlpage = palloc(BLCKSZ);
  598. *newrpage = palloc(BLCKSZ);
  599. dataPlaceToPageLeafSplit(leaf, lbound, rbound,
  600. *newlpage, *newrpage);
  601. Assert(GinPageRightMost(page) ||
  602. ginCompareItemPointers(GinDataPageGetRightBound(*newlpage),
  603. GinDataPageGetRightBound(*newrpage)) < 0);
  604. if (append)
  605. elog(DEBUG2, "appended %d items to block %u; split %d/%d (%d to go)",
  606. maxitems, BufferGetBlockNumber(buf), (int) leaf->lsize, (int) leaf->rsize,
  607. items->nitem - items->curitem - maxitems);
  608. else
  609. elog(DEBUG2, "inserted %d items to block %u; split %d/%d (%d to go)",
  610. maxitems, BufferGetBlockNumber(buf), (int) leaf->lsize, (int) leaf->rsize,
  611. items->nitem - items->curitem - maxitems);
  612. }
  613. items->curitem += maxitems;
  614. return needsplit ? GPTP_SPLIT : GPTP_INSERT;
  615. }
  616. /*
  617. * Perform data insertion after beginPlaceToPage has decided it will fit.
  618. *
  619. * This is invoked within a critical section, and XLOG record creation (if
  620. * needed) is already started. The target buffer is registered in slot 0.
  621. */
  622. static void
  623. dataExecPlaceToPageLeaf(GinBtree btree, Buffer buf, GinBtreeStack *stack,
  624. void *insertdata, void *ptp_workspace)
  625. {
  626. disassembledLeaf *leaf = (disassembledLeaf *) ptp_workspace;
  627. /* Apply changes to page */
  628. dataPlaceToPageLeafRecompress(buf, leaf);
  629. /* If needed, register WAL data built by computeLeafRecompressWALData */
  630. if (RelationNeedsWAL(btree->index))
  631. {
  632. XLogRegisterBufData(0, leaf->walinfo, leaf->walinfolen);
  633. }
  634. }
  635. /*
  636. * Vacuum a posting tree leaf page.
  637. */
  638. void
  639. ginVacuumPostingTreeLeaf(Relation indexrel, Buffer buffer, GinVacuumState *gvs)
  640. {
  641. Page page = BufferGetPage(buffer);
  642. disassembledLeaf *leaf;
  643. bool removedsomething = false;
  644. dlist_iter iter;
  645. leaf = disassembleLeaf(page);
  646. /* Vacuum each segment. */
  647. dlist_foreach(iter, &leaf->segments)
  648. {
  649. leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node, iter.cur);
  650. int oldsegsize;
  651. ItemPointer cleaned;
  652. int ncleaned;
  653. if (!seginfo->items)
  654. seginfo->items = ginPostingListDecode(seginfo->seg,
  655. &seginfo->nitems);
  656. if (seginfo->seg)
  657. oldsegsize = SizeOfGinPostingList(seginfo->seg);
  658. else
  659. oldsegsize = GinDataPageMaxDataSize;
  660. cleaned = ginVacuumItemPointers(gvs,
  661. seginfo->items,
  662. seginfo->nitems,
  663. &ncleaned);
  664. pfree(seginfo->items);
  665. seginfo->items = NULL;
  666. seginfo->nitems = 0;
  667. if (cleaned)
  668. {
  669. if (ncleaned > 0)
  670. {
  671. int npacked;
  672. seginfo->seg = ginCompressPostingList(cleaned,
  673. ncleaned,
  674. oldsegsize,
  675. &npacked);
  676. /* Removing an item never increases the size of the segment */
  677. if (npacked != ncleaned)
  678. elog(ERROR, "could not fit vacuumed posting list");
  679. seginfo->action = GIN_SEGMENT_REPLACE;
  680. }
  681. else
  682. {
  683. seginfo->seg = NULL;
  684. seginfo->items = NULL;
  685. seginfo->action = GIN_SEGMENT_DELETE;
  686. }
  687. seginfo->nitems = ncleaned;
  688. removedsomething = true;
  689. }
  690. }
  691. /*
  692. * If we removed any items, reconstruct the page from the pieces.
  693. *
  694. * We don't try to re-encode the segments here, even though some of them
  695. * might be really small now that we've removed some items from them. It
  696. * seems like a waste of effort, as there isn't really any benefit from
  697. * larger segments per se; larger segments only help to pack more items in
  698. * the same space. We might as well delay doing that until the next
  699. * insertion, which will need to re-encode at least part of the page
  700. * anyway.
  701. *
  702. * Also note if the page was in uncompressed, pre-9.4 format before, it is
  703. * now represented as one huge segment that contains all the items. It
  704. * might make sense to split that, to speed up random access, but we don't
  705. * bother. You'll have to REINDEX anyway if you want the full gain of the
  706. * new tighter index format.
  707. */
  708. if (removedsomething)
  709. {
  710. bool modified;
  711. /*
  712. * Make sure we have a palloc'd copy of all segments, after the first
  713. * segment that is modified. (dataPlaceToPageLeafRecompress requires
  714. * this).
  715. */
  716. modified = false;
  717. dlist_foreach(iter, &leaf->segments)
  718. {
  719. leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
  720. iter.cur);
  721. if (seginfo->action != GIN_SEGMENT_UNMODIFIED)
  722. modified = true;
  723. if (modified && seginfo->action != GIN_SEGMENT_DELETE)
  724. {
  725. int segsize = SizeOfGinPostingList(seginfo->seg);
  726. GinPostingList *tmp = (GinPostingList *) palloc(segsize);
  727. memcpy(tmp, seginfo->seg, segsize);
  728. seginfo->seg = tmp;
  729. }
  730. }
  731. if (RelationNeedsWAL(indexrel))
  732. computeLeafRecompressWALData(leaf);
  733. /* Apply changes to page */
  734. START_CRIT_SECTION();
  735. dataPlaceToPageLeafRecompress(buffer, leaf);
  736. MarkBufferDirty(buffer);
  737. if (RelationNeedsWAL(indexrel))
  738. {
  739. XLogRecPtr recptr;
  740. XLogBeginInsert();
  741. XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
  742. XLogRegisterBufData(0, leaf->walinfo, leaf->walinfolen);
  743. recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_VACUUM_DATA_LEAF_PAGE);
  744. PageSetLSN(page, recptr);
  745. }
  746. END_CRIT_SECTION();
  747. }
  748. }
  749. /*
  750. * Construct a ginxlogRecompressDataLeaf record representing the changes
  751. * in *leaf. (Because this requires a palloc, we have to do it before
  752. * we enter the critical section that actually updates the page.)
  753. */
  754. static void
  755. computeLeafRecompressWALData(disassembledLeaf *leaf)
  756. {
  757. int nmodified = 0;
  758. char *walbufbegin;
  759. char *walbufend;
  760. dlist_iter iter;
  761. int segno;
  762. ginxlogRecompressDataLeaf *recompress_xlog;
  763. /* Count the modified segments */
  764. dlist_foreach(iter, &leaf->segments)
  765. {
  766. leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
  767. iter.cur);
  768. if (seginfo->action != GIN_SEGMENT_UNMODIFIED)
  769. nmodified++;
  770. }
  771. walbufbegin =
  772. palloc(sizeof(ginxlogRecompressDataLeaf) +
  773. BLCKSZ + /* max size needed to hold the segment data */
  774. nmodified * 2 /* (segno + action) per action */
  775. );
  776. walbufend = walbufbegin;
  777. recompress_xlog = (ginxlogRecompressDataLeaf *) walbufend;
  778. walbufend += sizeof(ginxlogRecompressDataLeaf);
  779. recompress_xlog->nactions = nmodified;
  780. segno = 0;
  781. dlist_foreach(iter, &leaf->segments)
  782. {
  783. leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
  784. iter.cur);
  785. int segsize = 0;
  786. int datalen;
  787. uint8 action = seginfo->action;
  788. if (action == GIN_SEGMENT_UNMODIFIED)
  789. {
  790. segno++;
  791. continue;
  792. }
  793. if (action != GIN_SEGMENT_DELETE)
  794. segsize = SizeOfGinPostingList(seginfo->seg);
  795. /*
  796. * If storing the uncompressed list of added item pointers would take
  797. * more space than storing the compressed segment as is, do that
  798. * instead.
  799. */
  800. if (action == GIN_SEGMENT_ADDITEMS &&
  801. seginfo->nmodifieditems * sizeof(ItemPointerData) > segsize)
  802. {
  803. action = GIN_SEGMENT_REPLACE;
  804. }
  805. *((uint8 *) (walbufend++)) = segno;
  806. *(walbufend++) = action;
  807. switch (action)
  808. {
  809. case GIN_SEGMENT_DELETE:
  810. datalen = 0;
  811. break;
  812. case GIN_SEGMENT_ADDITEMS:
  813. datalen = seginfo->nmodifieditems * sizeof(ItemPointerData);
  814. memcpy(walbufend, &seginfo->nmodifieditems, sizeof(uint16));
  815. memcpy(walbufend + sizeof(uint16), seginfo->modifieditems, datalen);
  816. datalen += sizeof(uint16);
  817. break;
  818. case GIN_SEGMENT_INSERT:
  819. case GIN_SEGMENT_REPLACE:
  820. datalen = SHORTALIGN(segsize);
  821. memcpy(walbufend, seginfo->seg, segsize);
  822. break;
  823. default:
  824. elog(ERROR, "unexpected GIN leaf action %d", action);
  825. }
  826. walbufend += datalen;
  827. if (action != GIN_SEGMENT_INSERT)
  828. segno++;
  829. }
  830. /* Pass back the constructed info via *leaf */
  831. leaf->walinfo = walbufbegin;
  832. leaf->walinfolen = walbufend - walbufbegin;
  833. }
  834. /*
  835. * Assemble a disassembled posting tree leaf page back to a buffer.
  836. *
  837. * This just updates the target buffer; WAL stuff is caller's responsibility.
  838. *
  839. * NOTE: The segment pointers must not point directly to the same buffer,
  840. * except for segments that have not been modified and whose preceding
  841. * segments have not been modified either.
  842. */
  843. static void
  844. dataPlaceToPageLeafRecompress(Buffer buf, disassembledLeaf *leaf)
  845. {
  846. Page page = BufferGetPage(buf);
  847. char *ptr;
  848. int newsize;
  849. bool modified = false;
  850. dlist_iter iter;
  851. int segsize;
  852. /*
  853. * If the page was in pre-9.4 format before, convert the header, and force
  854. * all segments to be copied to the page whether they were modified or
  855. * not.
  856. */
  857. if (!GinPageIsCompressed(page))
  858. {
  859. Assert(leaf->oldformat);
  860. GinPageSetCompressed(page);
  861. GinPageGetOpaque(page)->maxoff = InvalidOffsetNumber;
  862. modified = true;
  863. }
  864. ptr = (char *) GinDataLeafPageGetPostingList(page);
  865. newsize = 0;
  866. dlist_foreach(iter, &leaf->segments)
  867. {
  868. leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node, iter.cur);
  869. if (seginfo->action != GIN_SEGMENT_UNMODIFIED)
  870. modified = true;
  871. if (seginfo->action != GIN_SEGMENT_DELETE)
  872. {
  873. segsize = SizeOfGinPostingList(seginfo->seg);
  874. if (modified)
  875. memcpy(ptr, seginfo->seg, segsize);
  876. ptr += segsize;
  877. newsize += segsize;
  878. }
  879. }
  880. Assert(newsize <= GinDataPageMaxDataSize);
  881. GinDataPageSetDataSize(page, newsize);
  882. }
  883. /*
  884. * Like dataPlaceToPageLeafRecompress, but writes the disassembled leaf
  885. * segments to two pages instead of one.
  886. *
  887. * This is different from the non-split cases in that this does not modify
  888. * the original page directly, but writes to temporary in-memory copies of
  889. * the new left and right pages.
  890. */
  891. static void
  892. dataPlaceToPageLeafSplit(disassembledLeaf *leaf,
  893. ItemPointerData lbound, ItemPointerData rbound,
  894. Page lpage, Page rpage)
  895. {
  896. char *ptr;
  897. int segsize;
  898. int lsize;
  899. int rsize;
  900. dlist_node *node;
  901. dlist_node *firstright;
  902. leafSegmentInfo *seginfo;
  903. /* Initialize temporary pages to hold the new left and right pages */
  904. GinInitPage(lpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
  905. GinInitPage(rpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
  906. /*
  907. * Copy the segments that go to the left page.
  908. *
  909. * XXX: We should skip copying the unmodified part of the left page, like
  910. * we do when recompressing.
  911. */
  912. lsize = 0;
  913. ptr = (char *) GinDataLeafPageGetPostingList(lpage);
  914. firstright = dlist_next_node(&leaf->segments, leaf->lastleft);
  915. for (node = dlist_head_node(&leaf->segments);
  916. node != firstright;
  917. node = dlist_next_node(&leaf->segments, node))
  918. {
  919. seginfo = dlist_container(leafSegmentInfo, node, node);
  920. if (seginfo->action != GIN_SEGMENT_DELETE)
  921. {
  922. segsize = SizeOfGinPostingList(seginfo->seg);
  923. memcpy(ptr, seginfo->seg, segsize);
  924. ptr += segsize;
  925. lsize += segsize;
  926. }
  927. }
  928. Assert(lsize == leaf->lsize);
  929. GinDataPageSetDataSize(lpage, lsize);
  930. *GinDataPageGetRightBound(lpage) = lbound;
  931. /* Copy the segments that go to the right page */
  932. ptr = (char *) GinDataLeafPageGetPostingList(rpage);
  933. rsize = 0;
  934. for (node = firstright;
  935. ;
  936. node = dlist_next_node(&leaf->segments, node))
  937. {
  938. seginfo = dlist_container(leafSegmentInfo, node, node);
  939. if (seginfo->action != GIN_SEGMENT_DELETE)
  940. {
  941. segsize = SizeOfGinPostingList(seginfo->seg);
  942. memcpy(ptr, seginfo->seg, segsize);
  943. ptr += segsize;
  944. rsize += segsize;
  945. }
  946. if (!dlist_has_next(&leaf->segments, node))
  947. break;
  948. }
  949. Assert(rsize == leaf->rsize);
  950. GinDataPageSetDataSize(rpage, rsize);
  951. *GinDataPageGetRightBound(rpage) = rbound;
  952. }
  953. /*
  954. * Prepare to insert data on an internal data page.
  955. *
  956. * If it will fit, return GPTP_INSERT after doing whatever setup is needed
  957. * before we enter the insertion critical section. *ptp_workspace can be
  958. * set to pass information along to the execPlaceToPage function.
  959. *
  960. * If it won't fit, perform a page split and return two temporary page
  961. * images into *newlpage and *newrpage, with result GPTP_SPLIT.
  962. *
  963. * In neither case should the given page buffer be modified here.
  964. *
  965. * Note: on insertion to an internal node, in addition to inserting the given
  966. * item, the downlink of the existing item at stack->off will be updated to
  967. * point to updateblkno.
  968. */
  969. static GinPlaceToPageRC
  970. dataBeginPlaceToPageInternal(GinBtree btree, Buffer buf, GinBtreeStack *stack,
  971. void *insertdata, BlockNumber updateblkno,
  972. void **ptp_workspace,
  973. Page *newlpage, Page *newrpage)
  974. {
  975. Page page = BufferGetPage(buf);
  976. /* If it doesn't fit, deal with split case */
  977. if (GinNonLeafDataPageGetFreeSpace(page) < sizeof(PostingItem))
  978. {
  979. dataSplitPageInternal(btree, buf, stack, insertdata, updateblkno,
  980. newlpage, newrpage);
  981. return GPTP_SPLIT;
  982. }
  983. /* Else, we're ready to proceed with insertion */
  984. return GPTP_INSERT;
  985. }
  986. /*
  987. * Perform data insertion after beginPlaceToPage has decided it will fit.
  988. *
  989. * This is invoked within a critical section, and XLOG record creation (if
  990. * needed) is already started. The target buffer is registered in slot 0.
  991. */
  992. static void
  993. dataExecPlaceToPageInternal(GinBtree btree, Buffer buf, GinBtreeStack *stack,
  994. void *insertdata, BlockNumber updateblkno,
  995. void *ptp_workspace)
  996. {
  997. Page page = BufferGetPage(buf);
  998. OffsetNumber off = stack->off;
  999. PostingItem *pitem;
  1000. /* Update existing downlink to point to next page (on internal page) */
  1001. pitem = GinDataPageGetPostingItem(page, off);
  1002. PostingItemSetBlockNumber(pitem, updateblkno);
  1003. /* Add new item */
  1004. pitem = (PostingItem *) insertdata;
  1005. GinDataPageAddPostingItem(page, pitem, off);
  1006. if (RelationNeedsWAL(btree->index))
  1007. {
  1008. /*
  1009. * This must be static, because it has to survive until XLogInsert,
  1010. * and we can't palloc here. Ugly, but the XLogInsert infrastructure
  1011. * isn't reentrant anyway.
  1012. */
  1013. static ginxlogInsertDataInternal data;
  1014. data.offset = off;
  1015. data.newitem = *pitem;
  1016. XLogRegisterBufData(0, (char *) &data,
  1017. sizeof(ginxlogInsertDataInternal));
  1018. }
  1019. }
  1020. /*
  1021. * Prepare to insert data on a posting-tree data page.
  1022. *
  1023. * If it will fit, return GPTP_INSERT after doing whatever setup is needed
  1024. * before we enter the insertion critical section. *ptp_workspace can be
  1025. * set to pass information along to the execPlaceToPage function.
  1026. *
  1027. * If it won't fit, perform a page split and return two temporary page
  1028. * images into *newlpage and *newrpage, with result GPTP_SPLIT.
  1029. *
  1030. * In neither case should the given page buffer be modified here.
  1031. *
  1032. * Note: on insertion to an internal node, in addition to inserting the given
  1033. * item, the downlink of the existing item at stack->off will be updated to
  1034. * point to updateblkno.
  1035. *
  1036. * Calls relevant function for internal or leaf page because they are handled
  1037. * very differently.
  1038. */
  1039. static GinPlaceToPageRC
  1040. dataBeginPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
  1041. void *insertdata, BlockNumber updateblkno,
  1042. void **ptp_workspace,
  1043. Page *newlpage, Page *newrpage)
  1044. {
  1045. Page page = BufferGetPage(buf);
  1046. Assert(GinPageIsData(page));
  1047. if (GinPageIsLeaf(page))
  1048. return dataBeginPlaceToPageLeaf(btree, buf, stack, insertdata,
  1049. ptp_workspace,
  1050. newlpage, newrpage);
  1051. else
  1052. return dataBeginPlaceToPageInternal(btree, buf, stack,
  1053. insertdata, updateblkno,
  1054. ptp_workspace,
  1055. newlpage, newrpage);
  1056. }
  1057. /*
  1058. * Perform data insertion after beginPlaceToPage has decided it will fit.
  1059. *
  1060. * This is invoked within a critical section, and XLOG record creation (if
  1061. * needed) is already started. The target buffer is registered in slot 0.
  1062. *
  1063. * Calls relevant function for internal or leaf page because they are handled
  1064. * very differently.
  1065. */
  1066. static void
  1067. dataExecPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
  1068. void *insertdata, BlockNumber updateblkno,
  1069. void *ptp_workspace)
  1070. {
  1071. Page page = BufferGetPage(buf);
  1072. if (GinPageIsLeaf(page))
  1073. dataExecPlaceToPageLeaf(btree, buf, stack, insertdata,
  1074. ptp_workspace);
  1075. else
  1076. dataExecPlaceToPageInternal(btree, buf, stack, insertdata,
  1077. updateblkno, ptp_workspace);
  1078. }
  1079. /*
  1080. * Split internal page and insert new data.
  1081. *
  1082. * Returns new temp pages to *newlpage and *newrpage.
  1083. * The original buffer is left untouched.
  1084. */
  1085. static void
  1086. dataSplitPageInternal(GinBtree btree, Buffer origbuf,
  1087. GinBtreeStack *stack,
  1088. void *insertdata, BlockNumber updateblkno,
  1089. Page *newlpage, Page *newrpage)
  1090. {
  1091. Page oldpage = BufferGetPage(origbuf);
  1092. OffsetNumber off = stack->off;
  1093. int nitems = GinPageGetOpaque(oldpage)->maxoff;
  1094. int nleftitems;
  1095. int nrightitems;
  1096. Size pageSize = PageGetPageSize(oldpage);
  1097. ItemPointerData oldbound = *GinDataPageGetRightBound(oldpage);
  1098. ItemPointer bound;
  1099. Page lpage;
  1100. Page rpage;
  1101. OffsetNumber separator;
  1102. PostingItem allitems[(BLCKSZ / sizeof(PostingItem)) + 1];
  1103. lpage = PageGetTempPage(oldpage);
  1104. rpage = PageGetTempPage(oldpage);
  1105. GinInitPage(lpage, GinPageGetOpaque(oldpage)->flags, pageSize);
  1106. GinInitPage(rpage, GinPageGetOpaque(oldpage)->flags, pageSize);
  1107. /*
  1108. * First construct a new list of PostingItems, which includes all the old
  1109. * items, and the new item.
  1110. */
  1111. memcpy(allitems, GinDataPageGetPostingItem(oldpage, FirstOffsetNumber),
  1112. (off - 1) * sizeof(PostingItem));
  1113. allitems[off - 1] = *((PostingItem *) insertdata);
  1114. memcpy(&allitems[off], GinDataPageGetPostingItem(oldpage, off),
  1115. (nitems - (off - 1)) * sizeof(PostingItem));
  1116. nitems++;
  1117. /* Update existing downlink to point to next page */
  1118. PostingItemSetBlockNumber(&allitems[off], updateblkno);
  1119. /*
  1120. * When creating a new index, fit as many tuples as possible on the left
  1121. * page, on the assumption that the table is scanned from beginning to
  1122. * end. This packs the index as tight as possible.
  1123. */
  1124. if (btree->isBuild && GinPageRightMost(oldpage))
  1125. separator = GinNonLeafDataPageGetFreeSpace(rpage) / sizeof(PostingItem);
  1126. else
  1127. separator = nitems / 2;
  1128. nleftitems = separator;
  1129. nrightitems = nitems - separator;
  1130. memcpy(GinDataPageGetPostingItem(lpage, FirstOffsetNumber),
  1131. allitems,
  1132. nleftitems * sizeof(PostingItem));
  1133. GinPageGetOpaque(lpage)->maxoff = nleftitems;
  1134. memcpy(GinDataPageGetPostingItem(rpage, FirstOffsetNumber),
  1135. &allitems[separator],
  1136. nrightitems * sizeof(PostingItem));
  1137. GinPageGetOpaque(rpage)->maxoff = nrightitems;
  1138. /*
  1139. * Also set pd_lower for both pages, like GinDataPageAddPostingItem does.
  1140. */
  1141. GinDataPageSetDataSize(lpage, nleftitems * sizeof(PostingItem));
  1142. GinDataPageSetDataSize(rpage, nrightitems * sizeof(PostingItem));
  1143. /* set up right bound for left page */
  1144. bound = GinDataPageGetRightBound(lpage);
  1145. *bound = GinDataPageGetPostingItem(lpage, nleftitems)->key;
  1146. /* set up right bound for right page */
  1147. *GinDataPageGetRightBound(rpage) = oldbound;
  1148. /* return temp pages to caller */
  1149. *newlpage = lpage;
  1150. *newrpage = rpage;
  1151. }
  1152. /*
  1153. * Construct insertion payload for inserting the downlink for given buffer.
  1154. */
  1155. static void *
  1156. dataPrepareDownlink(GinBtree btree, Buffer lbuf)
  1157. {
  1158. PostingItem *pitem = palloc(sizeof(PostingItem));
  1159. Page lpage = BufferGetPage(lbuf);
  1160. PostingItemSetBlockNumber(pitem, BufferGetBlockNumber(lbuf));
  1161. pitem->key = *GinDataPageGetRightBound(lpage);
  1162. return pitem;
  1163. }
  1164. /*
  1165. * Fills new root by right bound values from child.
  1166. * Also called from ginxlog, should not use btree
  1167. */
  1168. void
  1169. ginDataFillRoot(GinBtree btree, Page root, BlockNumber lblkno, Page lpage, BlockNumber rblkno, Page rpage)
  1170. {
  1171. PostingItem li,
  1172. ri;
  1173. li.key = *GinDataPageGetRightBound(lpage);
  1174. PostingItemSetBlockNumber(&li, lblkno);
  1175. GinDataPageAddPostingItem(root, &li, InvalidOffsetNumber);
  1176. ri.key = *GinDataPageGetRightBound(rpage);
  1177. PostingItemSetBlockNumber(&ri, rblkno);
  1178. GinDataPageAddPostingItem(root, &ri, InvalidOffsetNumber);
  1179. }
  1180. /*** Functions to work with disassembled leaf pages ***/
  1181. /*
  1182. * Disassemble page into a disassembledLeaf struct.
  1183. */
  1184. static disassembledLeaf *
  1185. disassembleLeaf(Page page)
  1186. {
  1187. disassembledLeaf *leaf;
  1188. GinPostingList *seg;
  1189. Pointer segbegin;
  1190. Pointer segend;
  1191. leaf = palloc0(sizeof(disassembledLeaf));
  1192. dlist_init(&leaf->segments);
  1193. if (GinPageIsCompressed(page))
  1194. {
  1195. /*
  1196. * Create a leafSegment entry for each segment.
  1197. */
  1198. seg = GinDataLeafPageGetPostingList(page);
  1199. segbegin = (Pointer) seg;
  1200. segend = segbegin + GinDataLeafPageGetPostingListSize(page);
  1201. while ((Pointer) seg < segend)
  1202. {
  1203. leafSegmentInfo *seginfo = palloc(sizeof(leafSegmentInfo));
  1204. seginfo->action = GIN_SEGMENT_UNMODIFIED;
  1205. seginfo->seg = seg;
  1206. seginfo->items = NULL;
  1207. seginfo->nitems = 0;
  1208. dlist_push_tail(&leaf->segments, &seginfo->node);
  1209. seg = GinNextPostingListSegment(seg);
  1210. }
  1211. leaf->oldformat = false;
  1212. }
  1213. else
  1214. {
  1215. /*
  1216. * A pre-9.4 format uncompressed page is represented by a single
  1217. * segment, with an array of items. The corner case is uncompressed
  1218. * page containing no items, which is represented as no segments.
  1219. */
  1220. ItemPointer uncompressed;
  1221. int nuncompressed;
  1222. leafSegmentInfo *seginfo;
  1223. uncompressed = dataLeafPageGetUncompressed(page, &nuncompressed);
  1224. if (nuncompressed > 0)
  1225. {
  1226. seginfo = palloc(sizeof(leafSegmentInfo));
  1227. seginfo->action = GIN_SEGMENT_REPLACE;
  1228. seginfo->seg = NULL;
  1229. seginfo->items = palloc(nuncompressed * sizeof(ItemPointerData));
  1230. memcpy(seginfo->items, uncompressed, nuncompressed * sizeof(ItemPointerData));
  1231. seginfo->nitems = nuncompressed;
  1232. dlist_push_tail(&leaf->segments, &seginfo->node);
  1233. }
  1234. leaf->oldformat = true;
  1235. }
  1236. return leaf;
  1237. }
  1238. /*
  1239. * Distribute newItems to the segments.
  1240. *
  1241. * Any segments that acquire new items are decoded, and the new items are
  1242. * merged with the old items.
  1243. *
  1244. * Returns true if any new items were added. False means they were all
  1245. * duplicates of existing items on the page.
  1246. */
  1247. static bool
  1248. addItemsToLeaf(disassembledLeaf *leaf, ItemPointer newItems, int nNewItems)
  1249. {
  1250. dlist_iter iter;
  1251. ItemPointer nextnew = newItems;
  1252. int newleft = nNewItems;
  1253. bool modified = false;
  1254. leafSegmentInfo *newseg;
  1255. /*
  1256. * If the page is completely empty, just construct one new segment to hold
  1257. * all the new items.
  1258. */
  1259. if (dlist_is_empty(&leaf->segments))
  1260. {
  1261. newseg = palloc(sizeof(leafSegmentInfo));
  1262. newseg->seg = NULL;
  1263. newseg->items = newItems;
  1264. newseg->nitems = nNewItems;
  1265. newseg->action = GIN_SEGMENT_INSERT;
  1266. dlist_push_tail(&leaf->segments, &newseg->node);
  1267. return true;
  1268. }
  1269. dlist_foreach(iter, &leaf->segments)
  1270. {
  1271. leafSegmentInfo *cur = (leafSegmentInfo *) dlist_container(leafSegmentInfo, node, iter.cur);
  1272. int nthis;
  1273. ItemPointer tmpitems;
  1274. int ntmpitems;
  1275. /*
  1276. * How many of the new items fall into this segment?
  1277. */
  1278. if (!dlist_has_next(&leaf->segments, iter.cur))
  1279. nthis = newleft;
  1280. else
  1281. {
  1282. leafSegmentInfo *next;
  1283. ItemPointerData next_first;
  1284. next = (leafSegmentInfo *) dlist_container(leafSegmentInfo, node,
  1285. dlist_next_node(&leaf->segments, iter.cur));
  1286. if (next->items)
  1287. next_first = next->items[0];
  1288. else
  1289. {
  1290. Assert(next->seg != NULL);
  1291. next_first = next->seg->first;
  1292. }
  1293. nthis = 0;
  1294. while (nthis < newleft && ginCompareItemPointers(&nextnew[nthis], &next_first) < 0)
  1295. nthis++;
  1296. }
  1297. if (nthis == 0)
  1298. continue;
  1299. /* Merge the new items with the existing items. */
  1300. if (!cur->items)
  1301. cur->items = ginPostingListDecode(cur->seg, &cur->nitems);
  1302. /*
  1303. * Fast path for the important special case that we're appending to
  1304. * the end of the page: don't let the last segment on the page grow
  1305. * larger than the target, create a new segment before that happens.
  1306. */
  1307. if (!dlist_has_next(&leaf->segments, iter.cur) &&
  1308. ginCompareItemPointers(&cur->items[cur->nitems - 1], &nextnew[0]) < 0 &&
  1309. cur->seg != NULL &&
  1310. SizeOfGinPostingList(cur->seg) >= GinPostingListSegmentTargetSize)
  1311. {
  1312. newseg = palloc(sizeof(leafSegmentInfo));
  1313. newseg->seg = NULL;
  1314. newseg->items = nextnew;
  1315. newseg->nitems = nthis;
  1316. newseg->action = GIN_SEGMENT_INSERT;
  1317. dlist_push_tail(&leaf->segments, &newseg->node);
  1318. modified = true;
  1319. break;
  1320. }
  1321. tmpitems = ginMergeItemPointers(cur->items, cur->nitems,
  1322. nextnew, nthis,
  1323. &ntmpitems);
  1324. if (ntmpitems != cur->nitems)
  1325. {
  1326. /*
  1327. * If there are no duplicates, track the added items so that we
  1328. * can emit a compact ADDITEMS WAL record later on. (it doesn't
  1329. * seem worth re-checking which items were duplicates, if there
  1330. * were any)
  1331. */
  1332. if (ntmpitems == nthis + cur->nitems &&
  1333. cur->action == GIN_SEGMENT_UNMODIFIED)
  1334. {
  1335. cur->action = GIN_SEGMENT_ADDITEMS;
  1336. cur->modifieditems = nextnew;
  1337. cur->nmodifieditems = nthis;
  1338. }
  1339. else
  1340. cur->action = GIN_SEGMENT_REPLACE;
  1341. cur->items = tmpitems;
  1342. cur->nitems = ntmpitems;
  1343. cur->seg = NULL;
  1344. modified = true;
  1345. }
  1346. nextnew += nthis;
  1347. newleft -= nthis;
  1348. if (newleft == 0)
  1349. break;
  1350. }
  1351. return modified;
  1352. }
  1353. /*
  1354. * Recompresses all segments that have been modified.
  1355. *
  1356. * If not all the items fit on two pages (ie. after split), we store as
  1357. * many items as fit, and set *remaining to the first item that didn't fit.
  1358. * If all items fit, *remaining is set to invalid.
  1359. *
  1360. * Returns true if the page has to be split.
  1361. */
  1362. static bool
  1363. leafRepackItems(disassembledLeaf *leaf, ItemPointer remaining)
  1364. {
  1365. int pgused = 0;
  1366. bool needsplit = false;
  1367. dlist_iter iter;
  1368. int segsize;
  1369. leafSegmentInfo *nextseg;
  1370. int npacked;
  1371. bool modified;
  1372. dlist_node *cur_node;
  1373. dlist_node *next_node;
  1374. ItemPointerSetInvalid(remaining);
  1375. /*
  1376. * cannot use dlist_foreach_modify here because we insert adjacent items
  1377. * while iterating.
  1378. */
  1379. for (cur_node = dlist_head_node(&leaf->segments);
  1380. cur_node != NULL;
  1381. cur_node = next_node)
  1382. {
  1383. leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
  1384. cur_node);
  1385. if (dlist_has_next(&leaf->segments, cur_node))
  1386. next_node = dlist_next_node(&leaf->segments, cur_node);
  1387. else
  1388. next_node = NULL;
  1389. /* Compress the posting list, if necessary */
  1390. if (seginfo->action != GIN_SEGMENT_DELETE)
  1391. {
  1392. if (seginfo->seg == NULL)
  1393. {
  1394. if (seginfo->nitems > GinPostingListSegmentMaxSize)
  1395. npacked = 0; /* no chance that it would fit. */
  1396. else
  1397. {
  1398. seginfo->seg = ginCompressPostingList(seginfo->items,
  1399. seginfo->nitems,
  1400. GinPostingListSegmentMaxSize,
  1401. &npacked);
  1402. }
  1403. if (npacked != seginfo->nitems)
  1404. {
  1405. /*
  1406. * Too large. Compress again to the target size, and
  1407. * create a new segment to represent the remaining items.
  1408. * The new segment is inserted after this one, so it will
  1409. * be processed in the next iteration of this loop.
  1410. */
  1411. if (seginfo->seg)
  1412. pfree(seginfo->seg);
  1413. seginfo->seg = ginCompressPostingList(seginfo->items,
  1414. seginfo->nitems,
  1415. GinPostingListSegmentTargetSize,
  1416. &npacked);
  1417. if (seginfo->action != GIN_SEGMENT_INSERT)
  1418. seginfo->action = GIN_SEGMENT_REPLACE;
  1419. nextseg = palloc(sizeof(leafSegmentInfo));
  1420. nextseg->action = GIN_SEGMENT_INSERT;
  1421. nextseg->seg = NULL;
  1422. nextseg->items = &seginfo->items[npacked];
  1423. nextseg->nitems = seginfo->nitems - npacked;
  1424. next_node = &nextseg->node;
  1425. dlist_insert_after(cur_node, next_node);
  1426. }
  1427. }
  1428. /*
  1429. * If the segment is very small, merge it with the next segment.
  1430. */
  1431. if (SizeOfGinPostingList(seginfo->seg) < GinPostingListSegmentMinSize && next_node)
  1432. {
  1433. int nmerged;
  1434. nextseg = dlist_container(leafSegmentInfo, node, next_node);
  1435. if (seginfo->items == NULL)
  1436. seginfo->items = ginPostingListDecode(seginfo->seg,
  1437. &seginfo->nitems);
  1438. if (nextseg->items == NULL)
  1439. nextseg->items = ginPostingListDecode(nextseg->seg,
  1440. &nextseg->nitems);
  1441. nextseg->items =
  1442. ginMergeItemPointers(seginfo->items, seginfo->nitems,
  1443. nextseg->items, nextseg->nitems,
  1444. &nmerged);
  1445. Assert(nmerged == seginfo->nitems + nextseg->nitems);
  1446. nextseg->nitems = nmerged;
  1447. nextseg->seg = NULL;
  1448. nextseg->action = GIN_SEGMENT_REPLACE;
  1449. nextseg->modifieditems = NULL;
  1450. nextseg->nmodifieditems = 0;
  1451. if (seginfo->action == GIN_SEGMENT_INSERT)
  1452. {
  1453. dlist_delete(cur_node);
  1454. continue;
  1455. }
  1456. else
  1457. {
  1458. seginfo->action = GIN_SEGMENT_DELETE;
  1459. seginfo->seg = NULL;
  1460. }
  1461. }
  1462. seginfo->items = NULL;
  1463. seginfo->nitems = 0;
  1464. }
  1465. if (seginfo->action == GIN_SEGMENT_DELETE)
  1466. continue;
  1467. /*
  1468. * OK, we now have a compressed version of this segment ready for
  1469. * copying to the page. Did we exceed the size that fits on one page?
  1470. */
  1471. segsize = SizeOfGinPostingList(seginfo->seg);
  1472. if (pgused + segsize > GinDataPageMaxDataSize)
  1473. {
  1474. if (!needsplit)
  1475. {
  1476. /* switch to right page */
  1477. Assert(pgused > 0);
  1478. leaf->lastleft = dlist_prev_node(&leaf->segments, cur_node);
  1479. needsplit = true;
  1480. leaf->lsize = pgused;
  1481. pgused = 0;
  1482. }
  1483. else
  1484. {
  1485. /*
  1486. * Filled both pages. The last segment we constructed did not
  1487. * fit.
  1488. */
  1489. *remaining = seginfo->seg->first;
  1490. /*
  1491. * remove all segments that did not fit from the list.
  1492. */
  1493. while (dlist_has_next(&leaf->segments, cur_node))
  1494. dlist_delete(dlist_next_node(&leaf->segments, cur_node));
  1495. dlist_delete(cur_node);
  1496. break;
  1497. }
  1498. }
  1499. pgused += segsize;
  1500. }
  1501. if (!needsplit)
  1502. {
  1503. leaf->lsize = pgused;
  1504. leaf->rsize = 0;
  1505. }
  1506. else
  1507. leaf->rsize = pgused;
  1508. Assert(leaf->lsize <= GinDataPageMaxDataSize);
  1509. Assert(leaf->rsize <= GinDataPageMaxDataSize);
  1510. /*
  1511. * Make a palloc'd copy of every segment after the first modified one,
  1512. * because as we start copying items to the original page, we might
  1513. * overwrite an existing segment.
  1514. */
  1515. modified = false;
  1516. dlist_foreach(iter, &leaf->segments)
  1517. {
  1518. leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
  1519. iter.cur);
  1520. if (!modified && seginfo->action != GIN_SEGMENT_UNMODIFIED)
  1521. {
  1522. modified = true;
  1523. }
  1524. else if (modified && seginfo->action == GIN_SEGMENT_UNMODIFIED)
  1525. {
  1526. GinPostingList *tmp;
  1527. segsize = SizeOfGinPostingList(seginfo->seg);
  1528. tmp = palloc(segsize);
  1529. memcpy(tmp, seginfo->seg, segsize);
  1530. seginfo->seg = tmp;
  1531. }
  1532. }
  1533. return needsplit;
  1534. }
  1535. /*** Functions that are exported to the rest of the GIN code ***/
  1536. /*
  1537. * Creates new posting tree containing the given TIDs. Returns the page
  1538. * number of the root of the new posting tree.
  1539. *
  1540. * items[] must be in sorted order with no duplicates.
  1541. */
  1542. BlockNumber
  1543. createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
  1544. GinStatsData *buildStats, Buffer entrybuffer)
  1545. {
  1546. BlockNumber blkno;
  1547. Buffer buffer;
  1548. Page tmppage;
  1549. Page page;
  1550. Pointer ptr;
  1551. int nrootitems;
  1552. int rootsize;
  1553. /* Construct the new root page in memory first. */
  1554. tmppage = (Page) palloc(BLCKSZ);
  1555. GinInitPage(tmppage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
  1556. GinPageGetOpaque(tmppage)->rightlink = InvalidBlockNumber;
  1557. /*
  1558. * Write as many of the items to the root page as fit. In segments of max
  1559. * GinPostingListSegmentMaxSize bytes each.
  1560. */
  1561. nrootitems = 0;
  1562. rootsize = 0;
  1563. ptr = (Pointer) GinDataLeafPageGetPostingList(tmppage);
  1564. while (nrootitems < nitems)
  1565. {
  1566. GinPostingList *segment;
  1567. int npacked;
  1568. int segsize;
  1569. segment = ginCompressPostingList(&items[nrootitems],
  1570. nitems - nrootitems,
  1571. GinPostingListSegmentMaxSize,
  1572. &npacked);
  1573. segsize = SizeOfGinPostingList(segment);
  1574. if (rootsize + segsize > GinDataPageMaxDataSize)
  1575. break;
  1576. memcpy(ptr, segment, segsize);
  1577. ptr += segsize;
  1578. rootsize += segsize;
  1579. nrootitems += npacked;
  1580. pfree(segment);
  1581. }
  1582. GinDataPageSetDataSize(tmppage, rootsize);
  1583. /*
  1584. * All set. Get a new physical page, and copy the in-memory page to it.
  1585. */
  1586. buffer = GinNewBuffer(index);
  1587. page = BufferGetPage(buffer);
  1588. blkno = BufferGetBlockNumber(buffer);
  1589. /*
  1590. * Copy any predicate locks from the entry tree leaf (containing posting
  1591. * list) to the posting tree.
  1592. */
  1593. PredicateLockPageSplit(index, BufferGetBlockNumber(entrybuffer), blkno);
  1594. START_CRIT_SECTION();
  1595. PageRestoreTempPage(tmppage, page);
  1596. MarkBufferDirty(buffer);
  1597. if (RelationNeedsWAL(index))
  1598. {
  1599. XLogRecPtr recptr;
  1600. ginxlogCreatePostingTree data;
  1601. data.size = rootsize;
  1602. XLogBeginInsert();
  1603. XLogRegisterData((char *) &data, sizeof(ginxlogCreatePostingTree));
  1604. XLogRegisterData((char *) GinDataLeafPageGetPostingList(page),
  1605. rootsize);
  1606. XLogRegisterBuffer(0, buffer, REGBUF_WILL_INIT);
  1607. recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_PTREE);
  1608. PageSetLSN(page, recptr);
  1609. }
  1610. UnlockReleaseBuffer(buffer);
  1611. END_CRIT_SECTION();
  1612. /* During index build, count the newly-added data page */
  1613. if (buildStats)
  1614. buildStats->nDataPages++;
  1615. elog(DEBUG2, "created GIN posting tree with %d items", nrootitems);
  1616. /*
  1617. * Add any remaining TIDs to the newly-created posting tree.
  1618. */
  1619. if (nitems > nrootitems)
  1620. {
  1621. ginInsertItemPointers(index, blkno,
  1622. items + nrootitems,
  1623. nitems - nrootitems,
  1624. buildStats);
  1625. }
  1626. return blkno;
  1627. }
  1628. static void
  1629. ginPrepareDataScan(GinBtree btree, Relation index, BlockNumber rootBlkno)
  1630. {
  1631. memset(btree, 0, sizeof(GinBtreeData));
  1632. btree->index = index;
  1633. btree->rootBlkno = rootBlkno;
  1634. btree->findChildPage = dataLocateItem;
  1635. btree->getLeftMostChild = dataGetLeftMostPage;
  1636. btree->isMoveRight = dataIsMoveRight;
  1637. btree->findItem = NULL;
  1638. btree->findChildPtr = dataFindChildPtr;
  1639. btree->beginPlaceToPage = dataBeginPlaceToPage;
  1640. btree->execPlaceToPage = dataExecPlaceToPage;
  1641. btree->fillRoot = ginDataFillRoot;
  1642. btree->prepareDownlink = dataPrepareDownlink;
  1643. btree->isData = true;
  1644. btree->fullScan = false;
  1645. btree->isBuild = false;
  1646. }
  1647. /*
  1648. * Inserts array of item pointers, may execute several tree scan (very rare)
  1649. */
  1650. void
  1651. ginInsertItemPointers(Relation index, BlockNumber rootBlkno,
  1652. ItemPointerData *items, uint32 nitem,
  1653. GinStatsData *buildStats)
  1654. {
  1655. GinBtreeData btree;
  1656. GinBtreeDataLeafInsertData insertdata;
  1657. GinBtreeStack *stack;
  1658. ginPrepareDataScan(&btree, index, rootBlkno);
  1659. btree.isBuild = (buildStats != NULL);
  1660. insertdata.items = items;
  1661. insertdata.nitem = nitem;
  1662. insertdata.curitem = 0;
  1663. while (insertdata.curitem < insertdata.nitem)
  1664. {
  1665. /* search for the leaf page where the first item should go to */
  1666. btree.itemptr = insertdata.items[insertdata.curitem];
  1667. stack = ginFindLeafPage(&btree, false, true, NULL);
  1668. ginInsertValue(&btree, stack, &insertdata, buildStats);
  1669. }
  1670. }
  1671. /*
  1672. * Starts a new scan on a posting tree.
  1673. */
  1674. GinBtreeStack *
  1675. ginScanBeginPostingTree(GinBtree btree, Relation index, BlockNumber rootBlkno,
  1676. Snapshot snapshot)
  1677. {
  1678. GinBtreeStack *stack;
  1679. ginPrepareDataScan(btree, index, rootBlkno);
  1680. btree->fullScan = true;
  1681. stack = ginFindLeafPage(btree, true, false, snapshot);
  1682. return stack;
  1683. }