1 : /*-------------------------------------------------------------------------
2 : *
3 : * rewriteheap.c
4 : * Support functions to rewrite tables.
5 : *
6 : * These functions provide a facility to completely rewrite a heap, while
7 : * preserving visibility information and update chains.
8 : *
9 : * INTERFACE
10 : *
11 : * The caller is responsible for creating the new heap, all catalog
12 : * changes, supplying the tuples to be written to the new heap, and
13 : * rebuilding indexes. The caller must hold AccessExclusiveLock on the
14 : * target table, because we assume no one else is writing into it.
15 : *
16 : * To use the facility:
17 : *
18 : * begin_heap_rewrite
19 : * while (fetch next tuple)
20 : * {
21 : * if (tuple is dead)
22 : * rewrite_heap_dead_tuple
23 : * else
24 : * {
25 : * // do any transformations here if required
26 : * rewrite_heap_tuple
27 : * }
28 : * }
29 : * end_heap_rewrite
30 : *
31 : * The contents of the new relation shouldn't be relied on until after
32 : * end_heap_rewrite is called.
33 : *
34 : *
35 : * IMPLEMENTATION
36 : *
37 : * This would be a fairly trivial affair, except that we need to maintain
38 : * the ctid chains that link versions of an updated tuple together.
39 : * Since the newly stored tuples will have tids different from the original
40 : * ones, if we just copied t_ctid fields to the new table the links would
41 : * be wrong. When we are required to copy a (presumably recently-dead or
42 : * delete-in-progress) tuple whose ctid doesn't point to itself, we have
43 : * to substitute the correct ctid instead.
44 : *
45 : * For each ctid reference from A -> B, we might encounter either A first
46 : * or B first. (Note that a tuple in the middle of a chain is both A and B
47 : * of different pairs.)
48 : *
49 : * If we encounter A first, we'll store the tuple in the unresolved_tups
50 : * hash table. When we later encounter B, we remove A from the hash table,
51 : * fix the ctid to point to the new location of B, and insert both A and B
52 : * to the new heap.
53 : *
54 : * If we encounter B first, we can insert B to the new heap right away.
55 : * We then add an entry to the old_new_tid_map hash table showing B's
56 : * original tid (in the old heap) and new tid (in the new heap).
57 : * When we later encounter A, we get the new location of B from the table,
58 : * and can write A immediately with the correct ctid.
59 : *
60 : * Entries in the hash tables can be removed as soon as the later tuple
61 : * is encountered. That helps to keep the memory usage down. At the end,
62 : * both tables are usually empty; we should have encountered both A and B
63 : * of each pair. However, it's possible for A to be RECENTLY_DEAD and B
64 : * entirely DEAD according to HeapTupleSatisfiesVacuum, because the test
65 : * for deadness using OldestXmin is not exact. In such a case we might
66 : * encounter B first, and skip it, and find A later. Then A would be added
67 : * to unresolved_tups, and stay there until end of the rewrite. Since
68 : * this case is very unusual, we don't worry about the memory usage.
69 : *
70 : * Using in-memory hash tables means that we use some memory for each live
71 : * update chain in the table, from the time we find one end of the
72 : * reference until we find the other end. That shouldn't be a problem in
73 : * practice, but if you do something like an UPDATE without a where-clause
74 : * on a large table, and then run CLUSTER in the same transaction, you
75 : * could run out of memory. It doesn't seem worthwhile to add support for
76 : * spill-to-disk, as there shouldn't be that many RECENTLY_DEAD tuples in a
77 : * table under normal circumstances. Furthermore, in the typical scenario
78 : * of CLUSTERing on an unchanging key column, we'll see all the versions
79 : * of a given tuple together anyway, and so the peak memory usage is only
80 : * proportional to the number of RECENTLY_DEAD versions of a single row, not
81 : * in the whole table. Note that if we do fail halfway through a CLUSTER,
82 : * the old table is still valid, so failure is not catastrophic.
83 : *
84 : * We can't use the normal heap_insert function to insert into the new
85 : * heap, because heap_insert overwrites the visibility information.
86 : * We use a special-purpose raw_heap_insert function instead, which
87 : * is optimized for bulk inserting a lot of tuples, knowing that we have
88 : * exclusive access to the heap. raw_heap_insert builds new pages in
89 : * local storage. When a page is full, or at the end of the process,
90 : * we insert it to WAL as a single record and then write it to disk
91 : * directly through smgr. Note, however, that any data sent to the new
92 : * heap's TOAST table will go through the normal bufmgr.
93 : *
94 : *
95 : * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
96 : * Portions Copyright (c) 1994-5, Regents of the University of California
97 : *
98 : * IDENTIFICATION
99 : * $PostgreSQL: pgsql/src/backend/access/heap/rewriteheap.c,v 1.9 2007/11/15 22:25:15 momjian Exp $
100 : *
101 : *-------------------------------------------------------------------------
102 : */
103 : #include "postgres.h"
104 :
105 : #include "access/heapam.h"
106 : #include "access/rewriteheap.h"
107 : #include "access/transam.h"
108 : #include "access/tuptoaster.h"
109 : #include "storage/smgr.h"
110 : #include "utils/memutils.h"
111 :
112 :
113 : /*
114 : * State associated with a rewrite operation. This is opaque to the user
115 : * of the rewrite facility.
116 : */
117 : typedef struct RewriteStateData
118 : {
119 : Relation rs_new_rel; /* destination heap */
120 : Page rs_buffer; /* page currently being built */
121 : BlockNumber rs_blockno; /* block where page will go */
122 : bool rs_buffer_valid; /* T if any tuples in buffer */
123 : bool rs_use_wal; /* must we WAL-log inserts? */
124 : TransactionId rs_oldest_xmin; /* oldest xmin used by caller to
125 : * determine tuple visibility */
126 : TransactionId rs_freeze_xid;/* Xid that will be used as freeze cutoff
127 : * point */
128 : MemoryContext rs_cxt; /* for hash tables and entries and tuples in
129 : * them */
130 : HTAB *rs_unresolved_tups; /* unmatched A tuples */
131 : HTAB *rs_old_new_tid_map; /* unmatched B tuples */
132 : } RewriteStateData;
133 :
134 : /*
135 : * The lookup keys for the hash tables are tuple TID and xmin (we must check
136 : * both to avoid false matches from dead tuples). Beware that there is
137 : * probably some padding space in this struct; it must be zeroed out for
138 : * correct hashtable operation.
139 : */
140 : typedef struct
141 : {
142 : TransactionId xmin; /* tuple xmin */
143 : ItemPointerData tid; /* tuple location in old heap */
144 : } TidHashKey;
145 :
146 : /*
147 : * Entry structures for the hash tables
148 : */
149 : typedef struct
150 : {
151 : TidHashKey key; /* expected xmin/old location of B tuple */
152 : ItemPointerData old_tid; /* A's location in the old heap */
153 : HeapTuple tuple; /* A's tuple contents */
154 : } UnresolvedTupData;
155 :
156 : typedef UnresolvedTupData *UnresolvedTup;
157 :
158 : typedef struct
159 : {
160 : TidHashKey key; /* actual xmin/old location of B tuple */
161 : ItemPointerData new_tid; /* where we put it in the new heap */
162 : } OldToNewMappingData;
163 :
164 : typedef OldToNewMappingData *OldToNewMapping;
165 :
166 :
167 : /* prototypes for internal functions */
168 : static void raw_heap_insert(RewriteState state, HeapTuple tup);
169 :
170 :
171 : /*
172 : * Begin a rewrite of a table
173 : *
174 : * new_heap new, locked heap relation to insert tuples to
175 : * oldest_xmin xid used by the caller to determine which tuples are dead
176 : * freeze_xid xid before which tuples will be frozen
177 : * use_wal should the inserts to the new heap be WAL-logged?
178 : *
179 : * Returns an opaque RewriteState, allocated in current memory context,
180 : * to be used in subsequent calls to the other functions.
181 : */
182 : RewriteState
183 : begin_heap_rewrite(Relation new_heap, TransactionId oldest_xmin,
184 : TransactionId freeze_xid, bool use_wal)
185 6 : {
186 : RewriteState state;
187 : MemoryContext rw_cxt;
188 : MemoryContext old_cxt;
189 : HASHCTL hash_ctl;
190 :
191 : /*
192 : * To ease cleanup, make a separate context that will contain the
193 : * RewriteState struct itself plus all subsidiary data.
194 : */
195 6 : rw_cxt = AllocSetContextCreate(CurrentMemoryContext,
196 : "Table rewrite",
197 : ALLOCSET_DEFAULT_MINSIZE,
198 : ALLOCSET_DEFAULT_INITSIZE,
199 : ALLOCSET_DEFAULT_MAXSIZE);
200 6 : old_cxt = MemoryContextSwitchTo(rw_cxt);
201 :
202 : /* Create and fill in the state struct */
203 6 : state = palloc0(sizeof(RewriteStateData));
204 :
205 6 : state->rs_new_rel = new_heap;
206 6 : state->rs_buffer = (Page) palloc(BLCKSZ);
207 : /* new_heap needn't be empty, just locked */
208 6 : state->rs_blockno = RelationGetNumberOfBlocks(new_heap);
209 6 : state->rs_buffer_valid = false;
210 6 : state->rs_use_wal = use_wal;
211 6 : state->rs_oldest_xmin = oldest_xmin;
212 6 : state->rs_freeze_xid = freeze_xid;
213 6 : state->rs_cxt = rw_cxt;
214 :
215 : /* Initialize hash tables used to track update chains */
216 6 : memset(&hash_ctl, 0, sizeof(hash_ctl));
217 6 : hash_ctl.keysize = sizeof(TidHashKey);
218 6 : hash_ctl.entrysize = sizeof(UnresolvedTupData);
219 6 : hash_ctl.hcxt = state->rs_cxt;
220 6 : hash_ctl.hash = tag_hash;
221 :
222 6 : state->rs_unresolved_tups =
223 : hash_create("Rewrite / Unresolved ctids",
224 : 128, /* arbitrary initial size */
225 : &hash_ctl,
226 : HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
227 :
228 6 : hash_ctl.entrysize = sizeof(OldToNewMappingData);
229 :
230 6 : state->rs_old_new_tid_map =
231 : hash_create("Rewrite / Old to new tid map",
232 : 128, /* arbitrary initial size */
233 : &hash_ctl,
234 : HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
235 :
236 6 : MemoryContextSwitchTo(old_cxt);
237 :
238 6 : return state;
239 : }
240 :
241 : /*
242 : * End a rewrite.
243 : *
244 : * state and any other resources are freed.
245 : */
246 : void
247 : end_heap_rewrite(RewriteState state)
248 6 : {
249 : HASH_SEQ_STATUS seq_status;
250 : UnresolvedTup unresolved;
251 :
252 : /*
253 : * Write any remaining tuples in the UnresolvedTups table. If we have any
254 : * left, they should in fact be dead, but let's err on the safe side.
255 : *
256 : * XXX this really is a waste of code no?
257 : */
258 6 : hash_seq_init(&seq_status, state->rs_unresolved_tups);
259 :
260 12 : while ((unresolved = hash_seq_search(&seq_status)) != NULL)
261 : {
262 0 : ItemPointerSetInvalid(&unresolved->tuple->t_data->t_ctid);
263 0 : raw_heap_insert(state, unresolved->tuple);
264 : }
265 :
266 : /* Write the last page, if any */
267 6 : if (state->rs_buffer_valid)
268 : {
269 6 : if (state->rs_use_wal)
270 0 : log_newpage(&state->rs_new_rel->rd_node,
271 : state->rs_blockno,
272 : state->rs_buffer);
273 6 : RelationOpenSmgr(state->rs_new_rel);
274 6 : smgrextend(state->rs_new_rel->rd_smgr, state->rs_blockno,
275 : (char *) state->rs_buffer, true);
276 : }
277 :
278 : /*
279 : * If the rel isn't temp, must fsync before commit. We use heap_sync to
280 : * ensure that the toast table gets fsync'd too.
281 : *
282 : * It's obvious that we must do this when not WAL-logging. It's less
283 : * obvious that we have to do it even if we did WAL-log the pages. The
284 : * reason is the same as in tablecmds.c's copy_relation_data(): we're
285 : * writing data that's not in shared buffers, and so a CHECKPOINT
286 : * occurring during the rewriteheap operation won't have fsync'd data we
287 : * wrote before the checkpoint.
288 : */
289 6 : if (!state->rs_new_rel->rd_istemp)
290 6 : heap_sync(state->rs_new_rel);
291 :
292 : /* Deleting the context frees everything */
293 6 : MemoryContextDelete(state->rs_cxt);
294 6 : }
295 :
296 : /*
297 : * Add a tuple to the new heap.
298 : *
299 : * Visibility information is copied from the original tuple, except that
300 : * we "freeze" very-old tuples. Note that since we scribble on new_tuple,
301 : * it had better be temp storage not a pointer to the original tuple.
302 : *
303 : * state opaque state as returned by begin_heap_rewrite
304 : * old_tuple original tuple in the old heap
305 : * new_tuple new, rewritten tuple to be inserted to new heap
306 : */
307 : void
308 : rewrite_heap_tuple(RewriteState state,
309 : HeapTuple old_tuple, HeapTuple new_tuple)
310 56 : {
311 : MemoryContext old_cxt;
312 : ItemPointerData old_tid;
313 : TidHashKey hashkey;
314 : bool found;
315 : bool free_new;
316 :
317 56 : old_cxt = MemoryContextSwitchTo(state->rs_cxt);
318 :
319 : /*
320 : * Copy the original tuple's visibility information into new_tuple.
321 : *
322 : * XXX we might later need to copy some t_infomask2 bits, too? Right now,
323 : * we intentionally clear the HOT status bits.
324 : */
325 56 : memcpy(&new_tuple->t_data->t_choice.t_heap,
326 : &old_tuple->t_data->t_choice.t_heap,
327 : sizeof(HeapTupleFields));
328 :
329 56 : new_tuple->t_data->t_infomask &= ~HEAP_XACT_MASK;
330 56 : new_tuple->t_data->t_infomask2 &= ~HEAP2_XACT_MASK;
331 56 : new_tuple->t_data->t_infomask |=
332 : old_tuple->t_data->t_infomask & HEAP_XACT_MASK;
333 :
334 : /*
335 : * While we have our hands on the tuple, we may as well freeze any
336 : * very-old xmin or xmax, so that future VACUUM effort can be saved.
337 : *
338 : * Note we abuse heap_freeze_tuple() a bit here, since it's expecting to
339 : * be given a pointer to a tuple in a disk buffer. It happens though that
340 : * we can get the right things to happen by passing InvalidBuffer for the
341 : * buffer.
342 : */
343 56 : heap_freeze_tuple(new_tuple->t_data, state->rs_freeze_xid, InvalidBuffer);
344 :
345 : /*
346 : * Invalid ctid means that ctid should point to the tuple itself. We'll
347 : * override it later if the tuple is part of an update chain.
348 : */
349 56 : ItemPointerSetInvalid(&new_tuple->t_data->t_ctid);
350 :
351 : /*
352 : * If the tuple has been updated, check the old-to-new mapping hash table.
353 : */
354 56 : if (!(old_tuple->t_data->t_infomask & (HEAP_XMAX_INVALID |
355 : HEAP_IS_LOCKED)) &&
356 : !(ItemPointerEquals(&(old_tuple->t_self),
357 : &(old_tuple->t_data->t_ctid))))
358 : {
359 : OldToNewMapping mapping;
360 :
361 5 : memset(&hashkey, 0, sizeof(hashkey));
362 5 : hashkey.xmin = HeapTupleHeaderGetXmax(old_tuple->t_data);
363 5 : hashkey.tid = old_tuple->t_data->t_ctid;
364 :
365 5 : mapping = (OldToNewMapping)
366 : hash_search(state->rs_old_new_tid_map, &hashkey,
367 : HASH_FIND, NULL);
368 :
369 5 : if (mapping != NULL)
370 : {
371 : /*
372 : * We've already copied the tuple that t_ctid points to, so we can
373 : * set the ctid of this tuple to point to the new location, and
374 : * insert it right away.
375 : */
376 1 : new_tuple->t_data->t_ctid = mapping->new_tid;
377 :
378 : /* We don't need the mapping entry anymore */
379 1 : hash_search(state->rs_old_new_tid_map, &hashkey,
380 : HASH_REMOVE, &found);
381 : Assert(found);
382 : }
383 : else
384 : {
385 : /*
386 : * We haven't seen the tuple t_ctid points to yet. Stash this
387 : * tuple into unresolved_tups to be written later.
388 : */
389 : UnresolvedTup unresolved;
390 :
391 4 : unresolved = hash_search(state->rs_unresolved_tups, &hashkey,
392 : HASH_ENTER, &found);
393 : Assert(!found);
394 :
395 4 : unresolved->old_tid = old_tuple->t_self;
396 4 : unresolved->tuple = heap_copytuple(new_tuple);
397 :
398 : /*
399 : * We can't do anything more now, since we don't know where the
400 : * tuple will be written.
401 : */
402 4 : MemoryContextSwitchTo(old_cxt);
403 4 : return;
404 : }
405 : }
406 :
407 : /*
408 : * Now we will write the tuple, and then check to see if it is the B tuple
409 : * in any new or known pair. When we resolve a known pair, we will be
410 : * able to write that pair's A tuple, and then we have to check if it
411 : * resolves some other pair. Hence, we need a loop here.
412 : */
413 52 : old_tid = old_tuple->t_self;
414 52 : free_new = false;
415 :
416 : for (;;)
417 : {
418 : ItemPointerData new_tid;
419 :
420 : /* Insert the tuple and find out where it's put in new_heap */
421 56 : raw_heap_insert(state, new_tuple);
422 56 : new_tid = new_tuple->t_self;
423 :
424 : /*
425 : * If the tuple is the updated version of a row, and the prior version
426 : * wouldn't be DEAD yet, then we need to either resolve the prior
427 : * version (if it's waiting in rs_unresolved_tups), or make an entry
428 : * in rs_old_new_tid_map (so we can resolve it when we do see it). The
429 : * previous tuple's xmax would equal this one's xmin, so it's
430 : * RECENTLY_DEAD if and only if the xmin is not before OldestXmin.
431 : */
432 56 : if ((new_tuple->t_data->t_infomask & HEAP_UPDATED) &&
433 : !TransactionIdPrecedes(HeapTupleHeaderGetXmin(new_tuple->t_data),
434 : state->rs_oldest_xmin))
435 : {
436 : /*
437 : * Okay, this is B in an update pair. See if we've seen A.
438 : */
439 : UnresolvedTup unresolved;
440 :
441 5 : memset(&hashkey, 0, sizeof(hashkey));
442 5 : hashkey.xmin = HeapTupleHeaderGetXmin(new_tuple->t_data);
443 5 : hashkey.tid = old_tid;
444 :
445 5 : unresolved = hash_search(state->rs_unresolved_tups, &hashkey,
446 : HASH_FIND, NULL);
447 :
448 5 : if (unresolved != NULL)
449 : {
450 : /*
451 : * We have seen and memorized the previous tuple already. Now
452 : * that we know where we inserted the tuple its t_ctid points
453 : * to, fix its t_ctid and insert it to the new heap.
454 : */
455 4 : if (free_new)
456 2 : heap_freetuple(new_tuple);
457 4 : new_tuple = unresolved->tuple;
458 4 : free_new = true;
459 4 : old_tid = unresolved->old_tid;
460 4 : new_tuple->t_data->t_ctid = new_tid;
461 :
462 : /*
463 : * We don't need the hash entry anymore, but don't free its
464 : * tuple just yet.
465 : */
466 4 : hash_search(state->rs_unresolved_tups, &hashkey,
467 : HASH_REMOVE, &found);
468 : Assert(found);
469 :
470 : /* loop back to insert the previous tuple in the chain */
471 4 : continue;
472 : }
473 : else
474 : {
475 : /*
476 : * Remember the new tid of this tuple. We'll use it to set the
477 : * ctid when we find the previous tuple in the chain.
478 : */
479 : OldToNewMapping mapping;
480 :
481 1 : mapping = hash_search(state->rs_old_new_tid_map, &hashkey,
482 : HASH_ENTER, &found);
483 : Assert(!found);
484 :
485 1 : mapping->new_tid = new_tid;
486 : }
487 : }
488 :
489 : /* Done with this (chain of) tuples, for now */
490 52 : if (free_new)
491 2 : heap_freetuple(new_tuple);
492 : break;
493 : }
494 :
495 52 : MemoryContextSwitchTo(old_cxt);
496 : }
497 :
498 : /*
499 : * Register a dead tuple with an ongoing rewrite. Dead tuples are not
500 : * copied to the new table, but we still make note of them so that we
501 : * can release some resources earlier.
502 : */
503 : void
504 : rewrite_heap_dead_tuple(RewriteState state, HeapTuple old_tuple)
505 0 : {
506 : /*
507 : * If we have already seen an earlier tuple in the update chain that
508 : * points to this tuple, let's forget about that earlier tuple. It's in
509 : * fact dead as well, our simple xmax < OldestXmin test in
510 : * HeapTupleSatisfiesVacuum just wasn't enough to detect it. It happens
511 : * when xmin of a tuple is greater than xmax, which sounds
512 : * counter-intuitive but is perfectly valid.
513 : *
514 : * We don't bother to try to detect the situation the other way round,
515 : * when we encounter the dead tuple first and then the recently dead one
516 : * that points to it. If that happens, we'll have some unmatched entries
517 : * in the UnresolvedTups hash table at the end. That can happen anyway,
518 : * because a vacuum might have removed the dead tuple in the chain before
519 : * us.
520 : */
521 : UnresolvedTup unresolved;
522 : TidHashKey hashkey;
523 : bool found;
524 :
525 0 : memset(&hashkey, 0, sizeof(hashkey));
526 0 : hashkey.xmin = HeapTupleHeaderGetXmin(old_tuple->t_data);
527 0 : hashkey.tid = old_tuple->t_self;
528 :
529 0 : unresolved = hash_search(state->rs_unresolved_tups, &hashkey,
530 : HASH_FIND, NULL);
531 :
532 0 : if (unresolved != NULL)
533 : {
534 : /* Need to free the contained tuple as well as the hashtable entry */
535 0 : heap_freetuple(unresolved->tuple);
536 0 : hash_search(state->rs_unresolved_tups, &hashkey,
537 : HASH_REMOVE, &found);
538 : Assert(found);
539 : }
540 0 : }
541 :
542 : /*
543 : * Insert a tuple to the new relation. This has to track heap_insert
544 : * and its subsidiary functions!
545 : *
546 : * t_self of the tuple is set to the new TID of the tuple. If t_ctid of the
547 : * tuple is invalid on entry, it's replaced with the new TID as well (in
548 : * the inserted data only, not in the caller's copy).
549 : */
550 : static void
551 : raw_heap_insert(RewriteState state, HeapTuple tup)
552 56 : {
553 56 : Page page = state->rs_buffer;
554 : Size pageFreeSpace,
555 : saveFreeSpace;
556 : Size len;
557 : OffsetNumber newoff;
558 : HeapTuple heaptup;
559 :
560 : /*
561 : * If the new tuple is too big for storage or contains already toasted
562 : * out-of-line attributes from some other relation, invoke the toaster.
563 : *
564 : * Note: below this point, heaptup is the data we actually intend to store
565 : * into the relation; tup is the caller's original untoasted data.
566 : */
567 56 : if (state->rs_new_rel->rd_rel->relkind == RELKIND_TOASTVALUE)
568 : {
569 : /* toast table entries should never be recursively toasted */
570 : Assert(!HeapTupleHasExternal(tup));
571 0 : heaptup = tup;
572 : }
573 57 : else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD)
574 1 : heaptup = toast_insert_or_update(state->rs_new_rel, tup, NULL,
575 : state->rs_use_wal, false);
576 : else
577 55 : heaptup = tup;
578 :
579 56 : len = MAXALIGN(heaptup->t_len); /* be conservative */
580 :
581 : /*
582 : * If we're gonna fail for oversize tuple, do it right away
583 : */
584 56 : if (len > MaxHeapTupleSize)
585 0 : ereport(ERROR,
586 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
587 : errmsg("row is too big: size %lu, maximum size %lu",
588 : (unsigned long) len,
589 : (unsigned long) MaxHeapTupleSize)));
590 :
591 : /* Compute desired extra freespace due to fillfactor option */
592 56 : saveFreeSpace = RelationGetTargetPageFreeSpace(state->rs_new_rel,
593 : HEAP_DEFAULT_FILLFACTOR);
594 :
595 : /* Now we can check to see if there's enough free space already. */
596 56 : if (state->rs_buffer_valid)
597 : {
598 50 : pageFreeSpace = PageGetHeapFreeSpace(page);
599 :
600 50 : if (len + saveFreeSpace > pageFreeSpace)
601 : {
602 : /* Doesn't fit, so write out the existing page */
603 :
604 : /* XLOG stuff */
605 0 : if (state->rs_use_wal)
606 0 : log_newpage(&state->rs_new_rel->rd_node,
607 : state->rs_blockno,
608 : page);
609 :
610 : /*
611 : * Now write the page. We say isTemp = true even if it's not a
612 : * temp table, because there's no need for smgr to schedule an
613 : * fsync for this write; we'll do it ourselves in
614 : * end_heap_rewrite.
615 : */
616 0 : RelationOpenSmgr(state->rs_new_rel);
617 0 : smgrextend(state->rs_new_rel->rd_smgr, state->rs_blockno,
618 : (char *) page, true);
619 :
620 0 : state->rs_blockno++;
621 0 : state->rs_buffer_valid = false;
622 : }
623 : }
624 :
625 56 : if (!state->rs_buffer_valid)
626 : {
627 : /* Initialize a new empty page */
628 6 : PageInit(page, BLCKSZ, 0);
629 6 : state->rs_buffer_valid = true;
630 : }
631 :
632 : /* And now we can insert the tuple into the page */
633 56 : newoff = PageAddItem(page, (Item) heaptup->t_data, len,
634 : InvalidOffsetNumber, false, true);
635 56 : if (newoff == InvalidOffsetNumber)
636 0 : elog(ERROR, "failed to add tuple");
637 :
638 : /* Update caller's t_self to the actual position where it was stored */
639 56 : ItemPointerSet(&(tup->t_self), state->rs_blockno, newoff);
640 :
641 : /*
642 : * Insert the correct position into CTID of the stored tuple, too, if the
643 : * caller didn't supply a valid CTID.
644 : */
645 56 : if (!ItemPointerIsValid(&tup->t_data->t_ctid))
646 : {
647 : ItemId newitemid;
648 : HeapTupleHeader onpage_tup;
649 :
650 51 : newitemid = PageGetItemId(page, newoff);
651 51 : onpage_tup = (HeapTupleHeader) PageGetItem(page, newitemid);
652 :
653 51 : onpage_tup->t_ctid = tup->t_self;
654 : }
655 :
656 : /* If heaptup is a private copy, release it. */
657 56 : if (heaptup != tup)
658 1 : heap_freetuple(heaptup);
659 56 : }
|