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