19
19
#include "utils/rel.h"
20
20
21
21
/*
22
- * Locks buffer by needed method for search.
22
+ * Lock buffer by needed method for search.
23
23
*/
24
24
static int
25
25
ginTraverseLock (Buffer buffer , bool searchMode )
@@ -53,23 +53,23 @@ ginTraverseLock(Buffer buffer, bool searchMode)
53
53
}
54
54
55
55
/*
56
- * Descends the tree to the leaf page that contains or would contain the
57
- * key we're searching for. The key should already be filled in 'btree',
58
- * in tree-type specific manner. If btree->fullScan is true, descends to the
56
+ * Descend the tree to the leaf page that contains or would contain the key
57
+ * we're searching for. The key should already be filled in 'btree', in
58
+ * tree-type specific manner. If btree->fullScan is true, descends to the
59
59
* leftmost leaf page.
60
60
*
61
61
* If 'searchmode' is false, on return stack->buffer is exclusively locked,
62
62
* and the stack represents the full path to the root. Otherwise stack->buffer
63
63
* is share-locked, and stack->parent is NULL.
64
64
*/
65
65
GinBtreeStack *
66
- ginFindLeafPage (GinBtree btree , BlockNumber rootBlkno , bool searchMode )
66
+ ginFindLeafPage (GinBtree btree , bool searchMode )
67
67
{
68
68
GinBtreeStack * stack ;
69
69
70
70
stack = (GinBtreeStack * ) palloc (sizeof (GinBtreeStack ));
71
- stack -> blkno = rootBlkno ;
72
- stack -> buffer = ReadBuffer (btree -> index , rootBlkno );
71
+ stack -> blkno = btree -> rootBlkno ;
72
+ stack -> buffer = ReadBuffer (btree -> index , btree -> rootBlkno );
73
73
stack -> parent = NULL ;
74
74
stack -> predictNumber = 1 ;
75
75
@@ -89,7 +89,7 @@ ginFindLeafPage(GinBtree btree, BlockNumber rootBlkno, bool searchMode)
89
89
* ok, page is correctly locked, we should check to move right ..,
90
90
* root never has a right link, so small optimization
91
91
*/
92
- while (btree -> fullScan == FALSE && stack -> blkno != rootBlkno &&
92
+ while (btree -> fullScan == FALSE && stack -> blkno != btree -> rootBlkno &&
93
93
btree -> isMoveRight (btree , page ))
94
94
{
95
95
BlockNumber rightlink = GinPageGetOpaque (page )-> rightlink ;
@@ -146,7 +146,7 @@ ginStepRight(Buffer buffer, Relation index, int lockmode)
146
146
Page page = BufferGetPage (buffer );
147
147
bool isLeaf = GinPageIsLeaf (page );
148
148
bool isData = GinPageIsData (page );
149
- BlockNumber blkno = GinPageGetOpaque (page )-> rightlink ;
149
+ BlockNumber blkno = GinPageGetOpaque (page )-> rightlink ;
150
150
151
151
nextbuffer = ReadBuffer (index , blkno );
152
152
LockBuffer (nextbuffer , lockmode );
@@ -158,10 +158,10 @@ ginStepRight(Buffer buffer, Relation index, int lockmode)
158
158
elog (ERROR , "right sibling of GIN page is of different type" );
159
159
160
160
/*
161
- * Given the proper lock sequence above, we should never land on a
162
- * deleted page.
161
+ * Given the proper lock sequence above, we should never land on a deleted
162
+ * page.
163
163
*/
164
- if (GinPageIsDeleted (page ))
164
+ if (GinPageIsDeleted (page ))
165
165
elog (ERROR , "right sibling of GIN page was deleted" );
166
166
167
167
return nextbuffer ;
@@ -183,14 +183,12 @@ freeGinBtreeStack(GinBtreeStack *stack)
183
183
}
184
184
185
185
/*
186
- * Try to find parent for current stack position, returns correct
187
- * parent and child's offset in stack->parent.
188
- * Function should never release root page to prevent conflicts
189
- * with vacuum process
186
+ * Try to find parent for current stack position. Returns correct parent and
187
+ * child's offset in stack->parent. The root page is never released, to
188
+ * to prevent conflict with vacuum process.
190
189
*/
191
190
void
192
- ginFindParents (GinBtree btree , GinBtreeStack * stack ,
193
- BlockNumber rootBlkno )
191
+ ginFindParents (GinBtree btree , GinBtreeStack * stack )
194
192
{
195
193
Page page ;
196
194
Buffer buffer ;
@@ -204,8 +202,8 @@ ginFindParents(GinBtree btree, GinBtreeStack *stack,
204
202
{
205
203
/* XLog mode... */
206
204
root = (GinBtreeStack * ) palloc (sizeof (GinBtreeStack ));
207
- root -> blkno = rootBlkno ;
208
- root -> buffer = ReadBuffer (btree -> index , rootBlkno );
205
+ root -> blkno = btree -> rootBlkno ;
206
+ root -> buffer = ReadBuffer (btree -> index , btree -> rootBlkno );
209
207
LockBuffer (root -> buffer , GIN_EXCLUSIVE );
210
208
root -> parent = NULL ;
211
209
}
@@ -221,8 +219,8 @@ ginFindParents(GinBtree btree, GinBtreeStack *stack,
221
219
root = root -> parent ;
222
220
}
223
221
224
- Assert (root -> blkno == rootBlkno );
225
- Assert (BufferGetBlockNumber (root -> buffer ) == rootBlkno );
222
+ Assert (root -> blkno == btree -> rootBlkno );
223
+ Assert (BufferGetBlockNumber (root -> buffer ) == btree -> rootBlkno );
226
224
LockBuffer (root -> buffer , GIN_EXCLUSIVE );
227
225
}
228
226
root -> off = InvalidOffsetNumber ;
@@ -268,7 +266,7 @@ ginFindParents(GinBtree btree, GinBtreeStack *stack,
268
266
ptr = (GinBtreeStack * ) palloc (sizeof (GinBtreeStack ));
269
267
ptr -> blkno = blkno ;
270
268
ptr -> buffer = buffer ;
271
- ptr -> parent = root ; /* it's may be wrong, but in next call we will
269
+ ptr -> parent = root ; /* it may be wrong, but in next call we will
272
270
* correct */
273
271
ptr -> off = offset ;
274
272
stack -> parent = ptr ;
@@ -280,21 +278,35 @@ ginFindParents(GinBtree btree, GinBtreeStack *stack,
280
278
}
281
279
282
280
/*
283
- * Returns true if the insertion is done, false if the page was split and
284
- * downlink insertion is pending.
281
+ * Insert a new item to a page.
282
+ *
283
+ * Returns true if the insertion was finished. On false, the page was split and
284
+ * the parent needs to be updated. (a root split returns true as it doesn't
285
+ * need any further action by the caller to complete)
286
+ *
287
+ * When inserting a downlink to a internal page, the existing item at the
288
+ * given location is updated to point to 'updateblkno'.
285
289
*
286
290
* stack->buffer is locked on entry, and is kept locked.
287
291
*/
288
292
static bool
289
- ginPlaceToPage (GinBtree btree , BlockNumber rootBlkno , GinBtreeStack * stack ,
293
+ ginPlaceToPage (GinBtree btree , GinBtreeStack * stack ,
294
+ void * insertdata , BlockNumber updateblkno ,
290
295
GinStatsData * buildStats )
291
296
{
292
297
Page page = BufferGetPage (stack -> buffer );
293
298
XLogRecData * rdata ;
294
299
bool fit ;
295
300
301
+ /*
302
+ * Try to put the incoming tuple on the page. If it doesn't fit,
303
+ * placeToPage method will return false and leave the page unmodified, and
304
+ * we'll have to split the page.
305
+ */
296
306
START_CRIT_SECTION ();
297
- fit = btree -> placeToPage (btree , stack -> buffer , stack -> off , & rdata );
307
+ fit = btree -> placeToPage (btree , stack -> buffer , stack -> off ,
308
+ insertdata , updateblkno ,
309
+ & rdata );
298
310
if (fit )
299
311
{
300
312
MarkBufferDirty (stack -> buffer );
@@ -324,18 +336,7 @@ ginPlaceToPage(GinBtree btree, BlockNumber rootBlkno, GinBtreeStack *stack,
324
336
END_CRIT_SECTION ();
325
337
326
338
rbuffer = GinNewBuffer (btree -> index );
327
-
328
- savedRightLink = GinPageGetOpaque (page )-> rightlink ;
329
-
330
- /*
331
- * newlpage is a pointer to memory page, it is not associated with
332
- * a buffer. stack->buffer is not touched yet.
333
- */
334
- newlpage = btree -> splitPage (btree , stack -> buffer , rbuffer , stack -> off , & rdata );
335
-
336
- ((ginxlogSplit * ) (rdata -> data ))-> rootBlkno = rootBlkno ;
337
-
338
- /* During index build, count the newly-split page */
339
+ /* During index build, count the new page */
339
340
if (buildStats )
340
341
{
341
342
if (btree -> isData )
@@ -344,6 +345,18 @@ ginPlaceToPage(GinBtree btree, BlockNumber rootBlkno, GinBtreeStack *stack,
344
345
buildStats -> nEntryPages ++ ;
345
346
}
346
347
348
+ savedRightLink = GinPageGetOpaque (page )-> rightlink ;
349
+
350
+ /*
351
+ * newlpage is a pointer to memory page, it is not associated with a
352
+ * buffer. stack->buffer is not touched yet.
353
+ */
354
+ newlpage = btree -> splitPage (btree , stack -> buffer , rbuffer , stack -> off ,
355
+ insertdata , updateblkno ,
356
+ & rdata );
357
+
358
+ ((ginxlogSplit * ) (rdata -> data ))-> rootBlkno = btree -> rootBlkno ;
359
+
347
360
parent = stack -> parent ;
348
361
349
362
if (parent == NULL )
@@ -354,6 +367,15 @@ ginPlaceToPage(GinBtree btree, BlockNumber rootBlkno, GinBtreeStack *stack,
354
367
*/
355
368
Buffer lbuffer = GinNewBuffer (btree -> index );
356
369
370
+ /* During index build, count the new page */
371
+ if (buildStats )
372
+ {
373
+ if (btree -> isData )
374
+ buildStats -> nDataPages ++ ;
375
+ else
376
+ buildStats -> nEntryPages ++ ;
377
+ }
378
+
357
379
((ginxlogSplit * ) (rdata -> data ))-> isRootSplit = TRUE;
358
380
((ginxlogSplit * ) (rdata -> data ))-> rrlink = InvalidBlockNumber ;
359
381
@@ -434,46 +456,27 @@ ginPlaceToPage(GinBtree btree, BlockNumber rootBlkno, GinBtreeStack *stack,
434
456
}
435
457
436
458
/*
437
- * Insert value (stored in GinBtree) to tree described by stack
459
+ * Finish a split by inserting the downlink for the new page to parent.
438
460
*
439
- * During an index build, buildStats is non-null and the counters
440
- * it contains are incremented as needed.
461
+ * On entry, stack->buffer is exclusively locked.
441
462
*
442
463
* NB: the passed-in stack is freed, as though by freeGinBtreeStack.
443
464
*/
444
465
void
445
- ginInsertValue (GinBtree btree , GinBtreeStack * stack , GinStatsData * buildStats )
466
+ ginFinishSplit (GinBtree btree , GinBtreeStack * stack , GinStatsData * buildStats )
446
467
{
447
- GinBtreeStack * parent ;
448
- BlockNumber rootBlkno ;
449
468
Page page ;
450
-
451
- /* extract root BlockNumber from stack */
452
- Assert (stack != NULL );
453
- parent = stack ;
454
- while (parent -> parent )
455
- parent = parent -> parent ;
456
- rootBlkno = parent -> blkno ;
457
- Assert (BlockNumberIsValid (rootBlkno ));
469
+ bool done ;
458
470
459
471
/* this loop crawls up the stack until the insertion is complete */
460
- for (;;)
472
+ do
461
473
{
462
- bool done ;
463
-
464
- done = ginPlaceToPage (btree , rootBlkno , stack , buildStats );
465
-
466
- /* just to be extra sure we don't delete anything by accident... */
467
- btree -> isDelete = FALSE;
468
-
469
- if (done )
470
- {
471
- LockBuffer (stack -> buffer , GIN_UNLOCK );
472
- freeGinBtreeStack (stack );
473
- break ;
474
- }
474
+ GinBtreeStack * parent = stack -> parent ;
475
+ void * insertdata ;
476
+ BlockNumber updateblkno ;
475
477
476
- btree -> prepareDownlink (btree , stack -> buffer );
478
+ insertdata = btree -> prepareDownlink (btree , stack -> buffer );
479
+ updateblkno = GinPageGetOpaque (BufferGetPage (stack -> buffer ))-> rightlink ;
477
480
478
481
/* search parent to lock */
479
482
LockBuffer (parent -> buffer , GIN_EXCLUSIVE );
@@ -491,7 +494,7 @@ ginInsertValue(GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats)
491
494
* plain search...
492
495
*/
493
496
LockBuffer (parent -> buffer , GIN_UNLOCK );
494
- ginFindParents (btree , stack , rootBlkno );
497
+ ginFindParents (btree , stack );
495
498
parent = stack -> parent ;
496
499
Assert (parent != NULL );
497
500
break ;
@@ -502,8 +505,49 @@ ginInsertValue(GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats)
502
505
page = BufferGetPage (parent -> buffer );
503
506
}
504
507
508
+ /* release the child */
505
509
UnlockReleaseBuffer (stack -> buffer );
506
510
pfree (stack );
507
511
stack = parent ;
512
+
513
+ /* insert the downlink to parent */
514
+ done = ginPlaceToPage (btree , stack ,
515
+ insertdata , updateblkno ,
516
+ buildStats );
517
+ pfree (insertdata );
518
+ } while (!done );
519
+ LockBuffer (stack -> buffer , GIN_UNLOCK );
520
+
521
+ /* free the rest of the stack */
522
+ freeGinBtreeStack (stack );
523
+ }
524
+
525
+ /*
526
+ * Insert a value to tree described by stack.
527
+ *
528
+ * The value to be inserted is given in 'insertdata'. Its format depends
529
+ * on whether this is an entry or data tree, ginInsertValue just passes it
530
+ * through to the tree-specific callback function.
531
+ *
532
+ * During an index build, buildStats is non-null and the counters it contains
533
+ * are incremented as needed.
534
+ *
535
+ * NB: the passed-in stack is freed, as though by freeGinBtreeStack.
536
+ */
537
+ void
538
+ ginInsertValue (GinBtree btree , GinBtreeStack * stack , void * insertdata ,
539
+ GinStatsData * buildStats )
540
+ {
541
+ bool done ;
542
+
543
+ done = ginPlaceToPage (btree , stack ,
544
+ insertdata , InvalidBlockNumber ,
545
+ buildStats );
546
+ if (done )
547
+ {
548
+ LockBuffer (stack -> buffer , GIN_UNLOCK );
549
+ freeGinBtreeStack (stack );
508
550
}
551
+ else
552
+ ginFinishSplit (btree , stack , buildStats );
509
553
}
0 commit comments