1 /*-------------------------------------------------------------------------
4 * POSTGRES heap access method input/output code.
6 * Copyright (c) 1994, Regents of the University of California
10 * $Id: hio.c,v 1.20 1999/05/25 16:07:07 momjian Exp $
12 *-------------------------------------------------------------------------
17 #include <storage/bufpage.h>
18 #include <access/hio.h>
19 #include <access/heapam.h>
20 #include <storage/bufmgr.h>
21 #include <utils/memutils.h>
24 * amputunique - place tuple at tid
25 * Currently on errors, calls elog. Perhaps should return -1?
26 * Possible errors include the addition of a tuple to the page
27 * between the time the linep is chosen and the page is L_UP'd.
29 * This should be coordinated with the B-tree code.
30 * Probably needs to have an amdelunique to allow for
31 * internal index records to be deleted and reordered as needed.
32 * For the heap AM, this should never be needed.
34 * Note - we assume that caller hold BUFFER_LOCK_EXCLUSIVE on the buffer.
38 RelationPutHeapTuple(Relation relation,
49 * increment access statistics
52 IncrHeapAccessStat(local_RelationPutHeapTuple);
53 IncrHeapAccessStat(global_RelationPutHeapTuple);
55 pageHeader = (Page) BufferGetPage(buffer);
56 len = (unsigned) DOUBLEALIGN(tuple->t_len); /* be conservative */
57 Assert((int) len <= PageGetFreeSpace(pageHeader));
59 offnum = PageAddItem((Page) pageHeader, (Item) tuple->t_data,
60 tuple->t_len, InvalidOffsetNumber, LP_USED);
62 itemId = PageGetItemId((Page) pageHeader, offnum);
63 item = PageGetItem((Page) pageHeader, itemId);
65 ItemPointerSet(&((HeapTupleHeader) item)->t_ctid,
66 BufferGetBlockNumber(buffer), offnum);
69 * Let the caller do this!
71 * WriteBuffer(buffer);
74 /* return an accurate tuple */
75 ItemPointerSet(&tuple->t_self, BufferGetBlockNumber(buffer), offnum);
79 * This routine is another in the series of attempts to reduce the number
80 * of I/O's and system calls executed in the various benchmarks. In
81 * particular, this routine is used to append data to the end of a relation
82 * file without excessive lseeks. This code should do no more than 2 semops
85 * Eventually, we should cache the number of blocks in a relation somewhere.
86 * Until that time, this code will have to do an lseek to determine the number
87 * of blocks in a relation.
89 * This code should ideally do at most 4 semops, 1 lseek, and possibly 1 write
90 * to do an append; it's possible to eliminate 2 of the semops if we do direct
91 * buffer stuff (!); the lseek and the write can go if we get
92 * RelationGetNumberOfBlocks to be useful.
94 * NOTE: This code presumes that we have a write lock on the relation.
95 * Not now - we use extend locking...
97 * Also note that this routine probably shouldn't have to exist, and does
98 * screw up the call graph rather badly, but we are wasting so much time and
99 * system resources being massively general that we are losing badly in our
100 * performance benchmarks.
103 RelationPutHeapTupleAtEnd(Relation relation, HeapTuple tuple)
107 BlockNumber lastblock;
114 * Lock relation for extention. We can use LockPage here as long as in
115 * all other places we use page-level locking for indices only.
116 * Alternatevely, we could define pseudo-table as we do for
117 * transactions with XactLockTable.
119 if (!relation->rd_myxactonly)
120 LockPage(relation, 0, ExclusiveLock);
123 * XXX This does an lseek - VERY expensive - but at the moment it is
124 * the only way to accurately determine how many blocks are in a
125 * relation. A good optimization would be to get this to actually
129 lastblock = RelationGetNumberOfBlocks(relation);
133 buffer = ReadBuffer(relation, lastblock);
134 pageHeader = (Page) BufferGetPage(buffer);
137 * There was IF instead of ASSERT here ?!
139 Assert(PageIsNew((PageHeader) pageHeader));
140 buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
141 pageHeader = (Page) BufferGetPage(buffer);
142 PageInit(pageHeader, BufferGetPageSize(buffer), 0);
145 buffer = ReadBuffer(relation, lastblock - 1);
147 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
148 pageHeader = (Page) BufferGetPage(buffer);
149 len = (unsigned) DOUBLEALIGN(tuple->t_len); /* be conservative */
152 * Note that this is true if the above returned a bogus page, which it
153 * will do for a completely empty relation.
156 if (len > PageGetFreeSpace(pageHeader))
158 LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
159 buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
160 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
161 pageHeader = (Page) BufferGetPage(buffer);
162 PageInit(pageHeader, BufferGetPageSize(buffer), 0);
164 if (len > PageGetFreeSpace(pageHeader))
165 elog(ERROR, "Tuple is too big: size %d", len);
168 if (!relation->rd_myxactonly)
169 UnlockPage(relation, 0, ExclusiveLock);
171 offnum = PageAddItem((Page) pageHeader, (Item) tuple->t_data,
172 tuple->t_len, InvalidOffsetNumber, LP_USED);
174 itemId = PageGetItemId((Page) pageHeader, offnum);
175 item = PageGetItem((Page) pageHeader, itemId);
177 lastblock = BufferGetBlockNumber(buffer);
179 ItemPointerSet(&((HeapTupleHeader) item)->t_ctid, lastblock, offnum);
181 /* return an accurate tuple */
182 ItemPointerSet(&tuple->t_self, lastblock, offnum);
184 LockBuffer(buffer, BUFFER_LOCK_UNLOCK);