1 : /*-------------------------------------------------------------------------
2 : *
3 : * hashinsert.c
4 : * Item insertion in hash tables for Postgres.
5 : *
6 : * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * $PostgreSQL: pgsql/src/backend/access/hash/hashinsert.c,v 1.47 2007/09/20 17:56:30 tgl Exp $
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 :
16 : #include "postgres.h"
17 :
18 : #include "access/hash.h"
19 :
20 :
21 : static OffsetNumber _hash_pgaddtup(Relation rel, Buffer buf,
22 : Size itemsize, IndexTuple itup);
23 :
24 :
25 : /*
26 : * _hash_doinsert() -- Handle insertion of a single index tuple.
27 : *
28 : * This routine is called by the public interface routines, hashbuild
29 : * and hashinsert. By here, itup is completely filled in.
30 : */
31 : void
32 : _hash_doinsert(Relation rel, IndexTuple itup)
33 40018 : {
34 : Buffer buf;
35 : Buffer metabuf;
36 : HashMetaPage metap;
37 : BlockNumber blkno;
38 : Page page;
39 : HashPageOpaque pageopaque;
40 : Size itemsz;
41 : bool do_expand;
42 : uint32 hashkey;
43 : Bucket bucket;
44 : Datum datum;
45 : bool isnull;
46 :
47 : /*
48 : * Compute the hash key for the item. We do this first so as not to need
49 : * to hold any locks while running the hash function.
50 : */
51 40018 : if (rel->rd_rel->relnatts != 1)
52 0 : elog(ERROR, "hash indexes support only one index key");
53 40018 : datum = index_getattr(itup, 1, RelationGetDescr(rel), &isnull);
54 : Assert(!isnull);
55 40018 : hashkey = _hash_datum2hashkey(rel, datum);
56 :
57 : /* compute item size too */
58 40018 : itemsz = IndexTupleDSize(*itup);
59 40018 : itemsz = MAXALIGN(itemsz); /* be safe, PageAddItem will do this but we
60 : * need to be consistent */
61 :
62 : /*
63 : * Acquire shared split lock so we can compute the target bucket safely
64 : * (see README).
65 : */
66 40018 : _hash_getlock(rel, 0, HASH_SHARE);
67 :
68 : /* Read the metapage */
69 40018 : metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
70 40018 : metap = (HashMetaPage) BufferGetPage(metabuf);
71 :
72 : /*
73 : * Check whether the item can fit on a hash page at all. (Eventually, we
74 : * ought to try to apply TOAST methods if not.) Note that at this point,
75 : * itemsz doesn't include the ItemId.
76 : */
77 40018 : if (itemsz > HashMaxItemSize((Page) metap))
78 0 : ereport(ERROR,
79 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
80 : errmsg("index row size %lu exceeds hash maximum %lu",
81 : (unsigned long) itemsz,
82 : (unsigned long) HashMaxItemSize((Page) metap)),
83 : errhint("Values larger than a buffer page cannot be indexed.")));
84 :
85 : /*
86 : * Compute the target bucket number, and convert to block number.
87 : */
88 40018 : bucket = _hash_hashkey2bucket(hashkey,
89 : metap->hashm_maxbucket,
90 : metap->hashm_highmask,
91 : metap->hashm_lowmask);
92 :
93 40018 : blkno = BUCKET_TO_BLKNO(metap, bucket);
94 :
95 : /* release lock on metapage, but keep pin since we'll need it again */
96 40018 : _hash_chgbufaccess(rel, metabuf, HASH_READ, HASH_NOLOCK);
97 :
98 : /*
99 : * Acquire share lock on target bucket; then we can release split lock.
100 : */
101 40018 : _hash_getlock(rel, blkno, HASH_SHARE);
102 :
103 40018 : _hash_droplock(rel, 0, HASH_SHARE);
104 :
105 : /* Fetch the primary bucket page for the bucket */
106 40018 : buf = _hash_getbuf(rel, blkno, HASH_WRITE, LH_BUCKET_PAGE);
107 40018 : page = BufferGetPage(buf);
108 40018 : pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
109 : Assert(pageopaque->hasho_bucket == bucket);
110 :
111 : /* Do the insertion */
112 85816 : while (PageGetFreeSpace(page) < itemsz)
113 : {
114 : /*
115 : * no space on this page; check for an overflow page
116 : */
117 5780 : BlockNumber nextblkno = pageopaque->hasho_nextblkno;
118 :
119 5780 : if (BlockNumberIsValid(nextblkno))
120 : {
121 : /*
122 : * ovfl page exists; go get it. if it doesn't have room, we'll
123 : * find out next pass through the loop test above.
124 : */
125 5663 : _hash_relbuf(rel, buf);
126 5663 : buf = _hash_getbuf(rel, nextblkno, HASH_WRITE, LH_OVERFLOW_PAGE);
127 5663 : page = BufferGetPage(buf);
128 : }
129 : else
130 : {
131 : /*
132 : * we're at the end of the bucket chain and we haven't found a
133 : * page with enough room. allocate a new overflow page.
134 : */
135 :
136 : /* release our write lock without modifying buffer */
137 117 : _hash_chgbufaccess(rel, buf, HASH_READ, HASH_NOLOCK);
138 :
139 : /* chain to a new overflow page */
140 117 : buf = _hash_addovflpage(rel, metabuf, buf);
141 117 : page = BufferGetPage(buf);
142 :
143 : /* should fit now, given test above */
144 : Assert(PageGetFreeSpace(page) >= itemsz);
145 : }
146 5780 : pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
147 : Assert(pageopaque->hasho_flag == LH_OVERFLOW_PAGE);
148 : Assert(pageopaque->hasho_bucket == bucket);
149 : }
150 :
151 : /* found page with enough space, so add the item here */
152 40018 : (void) _hash_pgaddtup(rel, buf, itemsz, itup);
153 :
154 : /* write and release the modified page */
155 40018 : _hash_wrtbuf(rel, buf);
156 :
157 : /* We can drop the bucket lock now */
158 40018 : _hash_droplock(rel, blkno, HASH_SHARE);
159 :
160 : /*
161 : * Write-lock the metapage so we can increment the tuple count. After
162 : * incrementing it, check to see if it's time for a split.
163 : */
164 40018 : _hash_chgbufaccess(rel, metabuf, HASH_NOLOCK, HASH_WRITE);
165 :
166 40018 : metap->hashm_ntuples += 1;
167 :
168 : /* Make sure this stays in sync with _hash_expandtable() */
169 40018 : do_expand = metap->hashm_ntuples >
170 : (double) metap->hashm_ffactor * (metap->hashm_maxbucket + 1);
171 :
172 : /* Write out the metapage and drop lock, but keep pin */
173 40018 : _hash_chgbufaccess(rel, metabuf, HASH_WRITE, HASH_NOLOCK);
174 :
175 : /* Attempt to split if a split is needed */
176 40018 : if (do_expand)
177 250 : _hash_expandtable(rel, metabuf);
178 :
179 : /* Finally drop our pin on the metapage */
180 40018 : _hash_dropbuf(rel, metabuf);
181 40018 : }
182 :
183 : /*
184 : * _hash_pgaddtup() -- add a tuple to a particular page in the index.
185 : *
186 : * This routine adds the tuple to the page as requested; it does
187 : * not write out the page. It is an error to call pgaddtup() without
188 : * a write lock and pin.
189 : */
190 : static OffsetNumber
191 : _hash_pgaddtup(Relation rel,
192 : Buffer buf,
193 : Size itemsize,
194 : IndexTuple itup)
195 40018 : {
196 : OffsetNumber itup_off;
197 : Page page;
198 :
199 40018 : _hash_checkpage(rel, buf, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
200 40018 : page = BufferGetPage(buf);
201 :
202 40018 : itup_off = OffsetNumberNext(PageGetMaxOffsetNumber(page));
203 40018 : if (PageAddItem(page, (Item) itup, itemsize, itup_off, false, false)
204 : == InvalidOffsetNumber)
205 0 : elog(ERROR, "failed to add index item to \"%s\"",
206 : RelationGetRelationName(rel));
207 :
208 40018 : return itup_off;
209 : }
|