1 : /*-------------------------------------------------------------------------
2 : *
3 : * ginbulk.c
4 : * routines for fast build of inverted index
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : * IDENTIFICATION
11 : * $PostgreSQL: pgsql/src/backend/access/gin/ginbulk.c,v 1.10 2007/11/16 21:55:59 tgl Exp $
12 : *-------------------------------------------------------------------------
13 : */
14 :
15 : #include "postgres.h"
16 :
17 : #include "access/gin.h"
18 : #include "utils/datum.h"
19 : #include "utils/memutils.h"
20 :
21 :
22 : #define DEF_NENTRY 2048
23 : #define DEF_NPTR 4
24 :
25 : void
26 : ginInitBA(BuildAccumulator *accum)
27 3 : {
28 3 : accum->maxdepth = 1;
29 3 : accum->stackpos = 0;
30 3 : accum->entries = NULL;
31 3 : accum->stack = NULL;
32 3 : accum->allocatedMemory = 0;
33 3 : accum->entryallocator = NULL;
34 3 : }
35 :
36 : static EntryAccumulator *
37 : EAAllocate(BuildAccumulator *accum)
38 1430 : {
39 1430 : if (accum->entryallocator == NULL || accum->length >= DEF_NENTRY)
40 : {
41 3 : accum->entryallocator = palloc(sizeof(EntryAccumulator) * DEF_NENTRY);
42 3 : accum->allocatedMemory += GetMemoryChunkSpace(accum->entryallocator);
43 3 : accum->length = 0;
44 : }
45 :
46 1430 : accum->length++;
47 1430 : return accum->entryallocator + accum->length - 1;
48 : }
49 :
50 : /*
51 : * Stores heap item pointer. For robust, it checks that
52 : * item pointer are ordered
53 : */
54 : static void
55 : ginInsertData(BuildAccumulator *accum, EntryAccumulator *entry, ItemPointer heapptr)
56 28468 : {
57 28468 : if (entry->number >= entry->length)
58 : {
59 3068 : accum->allocatedMemory -= GetMemoryChunkSpace(entry->list);
60 3068 : entry->length *= 2;
61 3068 : entry->list = (ItemPointerData *) repalloc(entry->list,
62 : sizeof(ItemPointerData) * entry->length);
63 3068 : accum->allocatedMemory += GetMemoryChunkSpace(entry->list);
64 : }
65 :
66 28468 : if (entry->shouldSort == FALSE)
67 : {
68 28468 : int res = compareItemPointers(entry->list + entry->number - 1, heapptr);
69 :
70 : Assert(res != 0);
71 :
72 28468 : if (res > 0)
73 0 : entry->shouldSort = TRUE;
74 : }
75 :
76 28468 : entry->list[entry->number] = *heapptr;
77 28468 : entry->number++;
78 28468 : }
79 :
80 : /*
81 : * This is basically the same as datumCopy(), but we duplicate some code
82 : * to avoid computing the datum size twice.
83 : */
84 : static Datum
85 : getDatumCopy(BuildAccumulator *accum, Datum value)
86 1430 : {
87 1430 : Form_pg_attribute *att = accum->ginstate->tupdesc->attrs;
88 : Datum res;
89 :
90 1430 : if (att[0]->attbyval)
91 99 : res = value;
92 : else
93 : {
94 : Size realSize;
95 : char *s;
96 :
97 1331 : realSize = datumGetSize(value, false, att[0]->attlen);
98 :
99 1331 : s = (char *) palloc(realSize);
100 1331 : accum->allocatedMemory += GetMemoryChunkSpace(s);
101 :
102 1331 : memcpy(s, DatumGetPointer(value), realSize);
103 1331 : res = PointerGetDatum(s);
104 : }
105 1430 : return res;
106 : }
107 :
108 : /*
109 : * Find/store one entry from indexed value.
110 : */
111 : static void
112 : ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry)
113 29898 : {
114 29898 : EntryAccumulator *ea = accum->entries,
115 29898 : *pea = NULL;
116 29898 : int res = 0;
117 29898 : uint32 depth = 1;
118 :
119 329069 : while (ea)
120 : {
121 297741 : res = compareEntries(accum->ginstate, entry, ea->value);
122 297741 : if (res == 0)
123 28468 : break; /* found */
124 : else
125 : {
126 269273 : pea = ea;
127 269273 : if (res < 0)
128 141576 : ea = ea->left;
129 : else
130 127697 : ea = ea->right;
131 : }
132 269273 : depth++;
133 : }
134 :
135 29898 : if (depth > accum->maxdepth)
136 49 : accum->maxdepth = depth;
137 :
138 29898 : if (ea == NULL)
139 : {
140 1430 : ea = EAAllocate(accum);
141 :
142 1430 : ea->left = ea->right = NULL;
143 1430 : ea->value = getDatumCopy(accum, entry);
144 1430 : ea->length = DEF_NPTR;
145 1430 : ea->number = 1;
146 1430 : ea->shouldSort = FALSE;
147 1430 : ea->list = (ItemPointerData *) palloc(sizeof(ItemPointerData) * DEF_NPTR);
148 1430 : accum->allocatedMemory += GetMemoryChunkSpace(ea->list);
149 1430 : ea->list[0] = *heapptr;
150 :
151 1430 : if (pea == NULL)
152 3 : accum->entries = ea;
153 : else
154 : {
155 : Assert(res != 0);
156 1427 : if (res < 0)
157 716 : pea->left = ea;
158 : else
159 711 : pea->right = ea;
160 : }
161 : }
162 : else
163 28468 : ginInsertData(accum, ea, heapptr);
164 29898 : }
165 :
166 : /*
167 : * insert middle of left part the middle of right one,
168 : * then calls itself for each parts
169 : */
170 : static void
171 : ginChooseElem(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, uint32 nentry,
172 : uint32 low, uint32 high, uint32 offset)
173 42796 : {
174 : uint32 pos;
175 42796 : uint32 middle = (low + high) >> 1;
176 :
177 42796 : pos = (low + middle) >> 1;
178 42796 : if (low != middle && pos >= offset && pos - offset < nentry)
179 14742 : ginInsertEntry(accum, heapptr, entries[pos - offset]);
180 42796 : pos = (high + middle + 1) >> 1;
181 42796 : if (middle + 1 != high && pos >= offset && pos - offset < nentry)
182 14456 : ginInsertEntry(accum, heapptr, entries[pos - offset]);
183 :
184 42796 : if (low != middle)
185 21390 : ginChooseElem(accum, heapptr, entries, nentry, low, middle, offset);
186 42796 : if (high != middle + 1)
187 20706 : ginChooseElem(accum, heapptr, entries, nentry, middle + 1, high, offset);
188 42796 : }
189 :
190 : /*
191 : * Insert one heap pointer. Suppose entries is sorted.
192 : * Insertion order tries to get binary tree balanced: first insert middle value,
193 : * next middle on left part and middle of right part.
194 : */
195 : void
196 : ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, int32 nentry)
197 700 : {
198 : uint32 i,
199 700 : nbit = 0,
200 : offset;
201 :
202 700 : if (nentry <= 0)
203 0 : return;
204 :
205 700 : i = nentry - 1;
206 4257 : for (; i > 0; i >>= 1)
207 3557 : nbit++;
208 :
209 700 : nbit = 1 << nbit;
210 700 : offset = (nbit - nentry) / 2;
211 :
212 700 : ginInsertEntry(accum, heapptr, entries[(nbit >> 1) - offset]);
213 700 : ginChooseElem(accum, heapptr, entries, nentry, 0, nbit, offset);
214 : }
215 :
216 : static int
217 : qsortCompareItemPointers(const void *a, const void *b)
218 0 : {
219 0 : int res = compareItemPointers((ItemPointer) a, (ItemPointer) b);
220 :
221 : Assert(res != 0);
222 0 : return res;
223 : }
224 :
225 : /*
226 : * walk on binary tree and returns ordered nodes
227 : */
228 : static EntryAccumulator *
229 : walkTree(BuildAccumulator *accum)
230 2857 : {
231 2857 : EntryAccumulator *entry = accum->stack[accum->stackpos];
232 :
233 2857 : if (entry->list != NULL)
234 : {
235 : /* return entry itself: we already was at left sublink */
236 716 : return entry;
237 : }
238 2141 : else if (entry->right && entry->right != accum->stack[accum->stackpos + 1])
239 : {
240 : /* go on right sublink */
241 711 : accum->stackpos++;
242 711 : entry = entry->right;
243 :
244 : /* find most-left value */
245 : for (;;)
246 : {
247 1405 : accum->stack[accum->stackpos] = entry;
248 1405 : if (entry->left)
249 : {
250 694 : accum->stackpos++;
251 694 : entry = entry->left;
252 : }
253 : else
254 711 : break;
255 694 : }
256 : }
257 : else
258 : {
259 : /* we already return all left subtree, itself and right subtree */
260 1430 : if (accum->stackpos == 0)
261 3 : return 0;
262 1427 : accum->stackpos--;
263 1427 : return walkTree(accum);
264 : }
265 :
266 711 : return entry;
267 : }
268 :
269 : ItemPointerData *
270 : ginGetEntry(BuildAccumulator *accum, Datum *value, uint32 *n)
271 1433 : {
272 : EntryAccumulator *entry;
273 : ItemPointerData *list;
274 :
275 1433 : if (accum->stack == NULL)
276 : {
277 : /* first call */
278 3 : accum->stack = palloc0(sizeof(EntryAccumulator *) * (accum->maxdepth + 1));
279 3 : accum->allocatedMemory += GetMemoryChunkSpace(accum->stack);
280 3 : entry = accum->entries;
281 :
282 3 : if (entry == NULL)
283 0 : return NULL;
284 :
285 : /* find most-left value */
286 : for (;;)
287 : {
288 25 : accum->stack[accum->stackpos] = entry;
289 25 : if (entry->left)
290 : {
291 22 : accum->stackpos++;
292 22 : entry = entry->left;
293 : }
294 : else
295 3 : break;
296 22 : }
297 : }
298 : else
299 : {
300 1430 : accum->allocatedMemory -= GetMemoryChunkSpace(accum->stack[accum->stackpos]->list);
301 1430 : pfree(accum->stack[accum->stackpos]->list);
302 1430 : accum->stack[accum->stackpos]->list = NULL;
303 1430 : entry = walkTree(accum);
304 : }
305 :
306 1433 : if (entry == NULL)
307 3 : return NULL;
308 :
309 1430 : *n = entry->number;
310 1430 : *value = entry->value;
311 1430 : list = entry->list;
312 :
313 : Assert(list != NULL);
314 :
315 1430 : if (entry->shouldSort && entry->number > 1)
316 0 : qsort(list, *n, sizeof(ItemPointerData), qsortCompareItemPointers);
317 :
318 1430 : return list;
319 : }
|