]> granicus.if.org Git - postgresql/blob - src/backend/access/heap/hio.c
8a95749c152a57379542cdd9ac12e278baf785d4
[postgresql] / src / backend / access / heap / hio.c
1 /*-------------------------------------------------------------------------
2  *
3  * hio.c--
4  *    POSTGRES heap access method input/output code.
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *    $Id: hio.c,v 1.2 1996/10/18 07:43:43 vadim Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include <string.h>
15
16 #include "c.h"
17
18 #include "access/heapam.h"
19 #include "access/hio.h"
20 #include "access/htup.h"
21
22 #include "storage/block.h"
23 #include "storage/buf.h"
24 #include "storage/bufmgr.h"
25 #include "storage/bufpage.h"
26 #include "storage/itemid.h"
27 #include "storage/itemptr.h"
28 #include "storage/off.h"
29
30 #include "utils/memutils.h"
31 #include "utils/elog.h"
32 #include "utils/rel.h"
33
34 /*
35  * amputunique  - place tuple at tid
36  *   Currently on errors, calls elog.  Perhaps should return -1?
37  *   Possible errors include the addition of a tuple to the page
38  *   between the time the linep is chosen and the page is L_UP'd.
39  *
40  *   This should be coordinated with the B-tree code.
41  *   Probably needs to have an amdelunique to allow for
42  *   internal index records to be deleted and reordered as needed.
43  *   For the heap AM, this should never be needed.
44  */
45 void
46 RelationPutHeapTuple(Relation relation,
47                      BlockNumber blockIndex,
48                      HeapTuple tuple)
49 {
50     Buffer              buffer;
51     Page                pageHeader;
52     BlockNumber         numberOfBlocks;
53     OffsetNumber        offnum;
54     unsigned int        len;
55     ItemId              itemId;
56     Item                item;
57     
58     /* ----------------
59      *  increment access statistics
60      * ----------------
61      */
62     IncrHeapAccessStat(local_RelationPutHeapTuple);
63     IncrHeapAccessStat(global_RelationPutHeapTuple);
64     
65     Assert(RelationIsValid(relation));
66     Assert(HeapTupleIsValid(tuple));
67     
68     numberOfBlocks = RelationGetNumberOfBlocks(relation);
69     Assert(blockIndex < numberOfBlocks);
70     
71     buffer = ReadBuffer(relation, blockIndex);
72 #ifndef NO_BUFFERISVALID
73     if (!BufferIsValid(buffer)) {
74         elog(WARN, "RelationPutHeapTuple: no buffer for %ld in %s",
75              blockIndex, &relation->rd_rel->relname);
76     }
77 #endif
78     
79     pageHeader = (Page)BufferGetPage(buffer);
80     len = (unsigned)DOUBLEALIGN(tuple->t_len);  /* be conservative */
81     Assert((int)len <= PageGetFreeSpace(pageHeader));
82     
83     offnum = PageAddItem((Page)pageHeader, (Item)tuple,
84                          tuple->t_len, InvalidOffsetNumber, LP_USED);
85     
86     itemId = PageGetItemId((Page)pageHeader, offnum);
87     item = PageGetItem((Page)pageHeader, itemId);
88     
89     ItemPointerSet(&((HeapTuple)item)->t_ctid, blockIndex, offnum);
90     
91     WriteBuffer(buffer);
92     /* return an accurate tuple */
93     ItemPointerSet(&tuple->t_ctid, blockIndex, offnum);
94 }
95
96 /*
97  * This routine is another in the series of attempts to reduce the number
98  * of I/O's and system calls executed in the various benchmarks.  In
99  * particular, this routine is used to append data to the end of a relation
100  * file without excessive lseeks.  This code should do no more than 2 semops
101  * in the ideal case.
102  *
103  * Eventually, we should cache the number of blocks in a relation somewhere.
104  * Until that time, this code will have to do an lseek to determine the number
105  * of blocks in a relation.
106  * 
107  * This code should ideally do at most 4 semops, 1 lseek, and possibly 1 write
108  * to do an append; it's possible to eliminate 2 of the semops if we do direct
109  * buffer stuff (!); the lseek and the write can go if we get
110  * RelationGetNumberOfBlocks to be useful.
111  *
112  * NOTE: This code presumes that we have a write lock on the relation.
113  *
114  * Also note that this routine probably shouldn't have to exist, and does
115  * screw up the call graph rather badly, but we are wasting so much time and
116  * system resources being massively general that we are losing badly in our
117  * performance benchmarks.
118  */
119 void
120 RelationPutHeapTupleAtEnd(Relation relation, HeapTuple tuple)
121 {
122     Buffer              buffer;
123     Page                pageHeader;
124     BlockNumber         lastblock;
125     OffsetNumber        offnum;
126     unsigned int        len;
127     ItemId              itemId;
128     Item                item;
129     
130     Assert(RelationIsValid(relation));
131     Assert(HeapTupleIsValid(tuple));
132     
133     /*
134      * XXX This does an lseek - VERY expensive - but at the moment it
135      * is the only way to accurately determine how many blocks are in
136      * a relation.  A good optimization would be to get this to actually
137      * work properly.
138      */
139     
140     lastblock = RelationGetNumberOfBlocks(relation);
141     
142     if (lastblock == 0)
143         {
144             buffer = ReadBuffer(relation, lastblock);
145             pageHeader = (Page)BufferGetPage(buffer);
146             if (PageIsNew((PageHeader) pageHeader))
147                 {
148                     buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
149                     pageHeader = (Page)BufferGetPage(buffer);
150                     PageInit(pageHeader, BufferGetPageSize(buffer), 0);
151                 }
152         }
153     else
154         buffer = ReadBuffer(relation, lastblock - 1);
155     
156     pageHeader = (Page)BufferGetPage(buffer);
157     len = (unsigned)DOUBLEALIGN(tuple->t_len);  /* be conservative */
158     
159     /*
160      * Note that this is true if the above returned a bogus page, which
161      * it will do for a completely empty relation.
162      */
163     
164     if (len > PageGetFreeSpace(pageHeader))
165         {
166             buffer = ReleaseAndReadBuffer(buffer, relation, P_NEW);
167             pageHeader = (Page)BufferGetPage(buffer);
168             PageInit(pageHeader, BufferGetPageSize(buffer), 0);
169             
170             if (len > PageGetFreeSpace(pageHeader))
171                 elog(WARN, "Tuple is too big: size %d", len);
172         }
173     
174     offnum = PageAddItem((Page)pageHeader, (Item)tuple,
175                          tuple->t_len, InvalidOffsetNumber, LP_USED);
176     
177     itemId = PageGetItemId((Page)pageHeader, offnum);
178     item = PageGetItem((Page)pageHeader, itemId);
179     
180     lastblock = BufferGetBlockNumber(buffer);
181     
182     ItemPointerSet(&((HeapTuple)item)->t_ctid, lastblock, offnum);
183     
184     /* return an accurate tuple */
185     ItemPointerSet(&tuple->t_ctid, lastblock, offnum);
186     
187     WriteBuffer(buffer);
188 }