8
8
*
9
9
*
10
10
* IDENTIFICATION
11
- * $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.73 2008/09/30 10:52:10 heikki Exp $
11
+ * $PostgreSQL: pgsql/src/backend/access/heap/hio.c,v 1.74 2008/11/06 20:51:14 tgl Exp $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
15
15
16
16
#include "postgres.h"
17
17
18
+ #include "access/heapam.h"
18
19
#include "access/hio.h"
19
20
#include "storage/bufmgr.h"
20
21
#include "storage/freespace.h"
@@ -56,6 +57,43 @@ RelationPutHeapTuple(Relation relation,
56
57
((HeapTupleHeader ) item )-> t_ctid = tuple -> t_self ;
57
58
}
58
59
60
+ /*
61
+ * Read in a buffer, using bulk-insert strategy if bistate isn't NULL.
62
+ */
63
+ static Buffer
64
+ ReadBufferBI (Relation relation , BlockNumber targetBlock ,
65
+ BulkInsertState bistate )
66
+ {
67
+ Buffer buffer ;
68
+
69
+ /* If not bulk-insert, exactly like ReadBuffer */
70
+ if (!bistate )
71
+ return ReadBuffer (relation , targetBlock );
72
+
73
+ /* If we have the desired block already pinned, re-pin and return it */
74
+ if (bistate -> current_buf != InvalidBuffer )
75
+ {
76
+ if (BufferGetBlockNumber (bistate -> current_buf ) == targetBlock )
77
+ {
78
+ IncrBufferRefCount (bistate -> current_buf );
79
+ return bistate -> current_buf ;
80
+ }
81
+ /* ... else drop the old buffer */
82
+ ReleaseBuffer (bistate -> current_buf );
83
+ bistate -> current_buf = InvalidBuffer ;
84
+ }
85
+
86
+ /* Perform a read using the buffer strategy */
87
+ buffer = ReadBufferExtended (relation , MAIN_FORKNUM , targetBlock ,
88
+ RBM_NORMAL , bistate -> strategy );
89
+
90
+ /* Save the selected block as target for future inserts */
91
+ IncrBufferRefCount (buffer );
92
+ bistate -> current_buf = buffer ;
93
+
94
+ return buffer ;
95
+ }
96
+
59
97
/*
60
98
* RelationGetBufferForTuple
61
99
*
@@ -80,20 +118,26 @@ RelationPutHeapTuple(Relation relation,
80
118
* happen if space is freed in that page after heap_update finds there's not
81
119
* enough there). In that case, the page will be pinned and locked only once.
82
120
*
83
- * If use_fsm is true (the normal case), we use FSM to help us find free
84
- * space. If use_fsm is false , we always append a new empty page to the
85
- * end of the relation if the tuple won't fit on the current target page.
121
+ * We normally use FSM to help us find free space. However,
122
+ * if HEAP_INSERT_SKIP_FSM is specified , we just append a new empty page to
123
+ * the end of the relation if the tuple won't fit on the current target page.
86
124
* This can save some cycles when we know the relation is new and doesn't
87
125
* contain useful amounts of free space.
88
126
*
89
- * The use_fsm = false case is also useful for non-WAL-logged additions to a
127
+ * HEAP_INSERT_SKIP_FSM is also useful for non-WAL-logged additions to a
90
128
* relation, if the caller holds exclusive lock and is careful to invalidate
91
129
* relation->rd_targblock before the first insertion --- that ensures that
92
130
* all insertions will occur into newly added pages and not be intermixed
93
131
* with tuples from other transactions. That way, a crash can't risk losing
94
132
* any committed data of other transactions. (See heap_insert's comments
95
133
* for additional constraints needed for safe usage of this behavior.)
96
134
*
135
+ * The caller can also provide a BulkInsertState object to optimize many
136
+ * insertions into the same relation. This keeps a pin on the current
137
+ * insertion target page (to save pin/unpin cycles) and also passes a
138
+ * BULKWRITE buffer selection strategy object to the buffer manager.
139
+ * Passing NULL for bistate selects the default behavior.
140
+ *
97
141
* We always try to avoid filling existing pages further than the fillfactor.
98
142
* This is OK since this routine is not consulted when updating a tuple and
99
143
* keeping it on the same page, which is the scenario fillfactor is meant
@@ -104,8 +148,10 @@ RelationPutHeapTuple(Relation relation,
104
148
*/
105
149
Buffer
106
150
RelationGetBufferForTuple (Relation relation , Size len ,
107
- Buffer otherBuffer , bool use_fsm )
151
+ Buffer otherBuffer , int options ,
152
+ struct BulkInsertStateData * bistate )
108
153
{
154
+ bool use_fsm = !(options & HEAP_INSERT_SKIP_FSM );
109
155
Buffer buffer = InvalidBuffer ;
110
156
Page page ;
111
157
Size pageFreeSpace ,
@@ -116,6 +162,9 @@ RelationGetBufferForTuple(Relation relation, Size len,
116
162
117
163
len = MAXALIGN (len ); /* be conservative */
118
164
165
+ /* Bulk insert is not supported for updates, only inserts. */
166
+ Assert (otherBuffer == InvalidBuffer || !bistate );
167
+
119
168
/*
120
169
* If we're gonna fail for oversize tuple, do it right away
121
170
*/
@@ -137,25 +186,27 @@ RelationGetBufferForTuple(Relation relation, Size len,
137
186
138
187
/*
139
188
* We first try to put the tuple on the same page we last inserted a tuple
140
- * on, as cached in the relcache entry. If that doesn't work, we ask the
141
- * shared Free Space Map to locate a suitable page. Since the FSM's info
142
- * might be out of date, we have to be prepared to loop around and retry
143
- * multiple times. (To insure this isn't an infinite loop, we must update
144
- * the FSM with the correct amount of free space on each page that proves
145
- * not to be suitable.) If the FSM has no record of a page with enough
146
- * free space, we give up and extend the relation.
189
+ * on, as cached in the BulkInsertState or relcache entry. If that
190
+ * doesn't work, we ask the Free Space Map to locate a suitable page.
191
+ * Since the FSM's info might be out of date, we have to be prepared to
192
+ * loop around and retry multiple times. (To insure this isn't an infinite
193
+ * loop, we must update the FSM with the correct amount of free space on
194
+ * each page that proves not to be suitable.) If the FSM has no record of
195
+ * a page with enough free space, we give up and extend the relation.
147
196
*
148
197
* When use_fsm is false, we either put the tuple onto the existing target
149
198
* page or extend the relation.
150
199
*/
151
- if (len + saveFreeSpace <= MaxHeapTupleSize )
152
- targetBlock = relation -> rd_targblock ;
153
- else
200
+ if (len + saveFreeSpace > MaxHeapTupleSize )
154
201
{
155
- /* can't fit, don't screw up FSM request tracking by trying */
202
+ /* can't fit, don't bother asking FSM */
156
203
targetBlock = InvalidBlockNumber ;
157
204
use_fsm = false;
158
205
}
206
+ else if (bistate && bistate -> current_buf != InvalidBuffer )
207
+ targetBlock = BufferGetBlockNumber (bistate -> current_buf );
208
+ else
209
+ targetBlock = relation -> rd_targblock ;
159
210
160
211
if (targetBlock == InvalidBlockNumber && use_fsm )
161
212
{
@@ -189,7 +240,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
189
240
if (otherBuffer == InvalidBuffer )
190
241
{
191
242
/* easy case */
192
- buffer = ReadBuffer (relation , targetBlock );
243
+ buffer = ReadBufferBI (relation , targetBlock , bistate );
193
244
LockBuffer (buffer , BUFFER_LOCK_EXCLUSIVE );
194
245
}
195
246
else if (otherBlock == targetBlock )
@@ -274,7 +325,7 @@ RelationGetBufferForTuple(Relation relation, Size len,
274
325
* it worth keeping an accurate file length in shared memory someplace,
275
326
* rather than relying on the kernel to do it for us?
276
327
*/
277
- buffer = ReadBuffer (relation , P_NEW );
328
+ buffer = ReadBufferBI (relation , P_NEW , bistate );
278
329
279
330
/*
280
331
* We can be certain that locking the otherBuffer first is OK, since it
0 commit comments