1 /*-------------------------------------------------------------------------
4 * routines for fast build of inverted index
7 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
12 *-------------------------------------------------------------------------
17 #include "access/gin.h"
18 #include "utils/datum.h"
19 #include "utils/memutils.h"
22 #define DEF_NENTRY 2048
26 ginInitBA(BuildAccumulator
*accum
)
30 accum
->entries
= NULL
;
32 accum
->allocatedMemory
= 0;
33 accum
->entryallocator
= NULL
;
36 static EntryAccumulator
*
37 EAAllocate(BuildAccumulator
*accum
)
39 if (accum
->entryallocator
== NULL
|| accum
->length
>= DEF_NENTRY
)
41 accum
->entryallocator
= palloc(sizeof(EntryAccumulator
) * DEF_NENTRY
);
42 accum
->allocatedMemory
+= GetMemoryChunkSpace(accum
->entryallocator
);
47 return accum
->entryallocator
+ accum
->length
- 1;
51 * Stores heap item pointer. For robust, it checks that
52 * item pointer are ordered
55 ginInsertData(BuildAccumulator
*accum
, EntryAccumulator
*entry
, ItemPointer heapptr
)
57 if (entry
->number
>= entry
->length
)
59 accum
->allocatedMemory
-= GetMemoryChunkSpace(entry
->list
);
61 entry
->list
= (ItemPointerData
*) repalloc(entry
->list
,
62 sizeof(ItemPointerData
) * entry
->length
);
63 accum
->allocatedMemory
+= GetMemoryChunkSpace(entry
->list
);
66 if (entry
->shouldSort
== FALSE
)
68 int res
= compareItemPointers(entry
->list
+ entry
->number
- 1, heapptr
);
73 entry
->shouldSort
= TRUE
;
76 entry
->list
[entry
->number
] = *heapptr
;
81 * This is basically the same as datumCopy(), but modified to count
82 * palloc'd space in accum.
85 getDatumCopy(BuildAccumulator
*accum
, OffsetNumber attnum
, Datum value
)
87 Form_pg_attribute att
= accum
->ginstate
->origTupdesc
->attrs
[ attnum
- 1 ];
94 res
= datumCopy(value
, false, att
->attlen
);
95 accum
->allocatedMemory
+= GetMemoryChunkSpace(DatumGetPointer(res
));
101 * Find/store one entry from indexed value.
104 ginInsertEntry(BuildAccumulator
*accum
, ItemPointer heapptr
, OffsetNumber attnum
, Datum entry
)
106 EntryAccumulator
*ea
= accum
->entries
,
113 res
= compareAttEntries(accum
->ginstate
, attnum
, entry
, ea
->attnum
, ea
->value
);
127 if (depth
> accum
->maxdepth
)
128 accum
->maxdepth
= depth
;
132 ea
= EAAllocate(accum
);
134 ea
->left
= ea
->right
= NULL
;
136 ea
->value
= getDatumCopy(accum
, attnum
, entry
);
137 ea
->length
= DEF_NPTR
;
139 ea
->shouldSort
= FALSE
;
140 ea
->list
= (ItemPointerData
*) palloc(sizeof(ItemPointerData
) * DEF_NPTR
);
141 accum
->allocatedMemory
+= GetMemoryChunkSpace(ea
->list
);
142 ea
->list
[0] = *heapptr
;
156 ginInsertData(accum
, ea
, heapptr
);
160 * insert middle of left part the middle of right one,
161 * then calls itself for each parts
164 ginChooseElem(BuildAccumulator
*accum
, ItemPointer heapptr
, OffsetNumber attnum
,
165 Datum
*entries
, uint32 nentry
,
166 uint32 low
, uint32 high
, uint32 offset
)
169 uint32 middle
= (low
+ high
) >> 1;
171 pos
= (low
+ middle
) >> 1;
172 if (low
!= middle
&& pos
>= offset
&& pos
- offset
< nentry
)
173 ginInsertEntry(accum
, heapptr
, attnum
, entries
[pos
- offset
]);
174 pos
= (high
+ middle
+ 1) >> 1;
175 if (middle
+ 1 != high
&& pos
>= offset
&& pos
- offset
< nentry
)
176 ginInsertEntry(accum
, heapptr
, attnum
, entries
[pos
- offset
]);
179 ginChooseElem(accum
, heapptr
, attnum
, entries
, nentry
, low
, middle
, offset
);
180 if (high
!= middle
+ 1)
181 ginChooseElem(accum
, heapptr
, attnum
, entries
, nentry
, middle
+ 1, high
, offset
);
185 * Insert one heap pointer. Suppose entries is sorted.
186 * Insertion order tries to get binary tree balanced: first insert middle value,
187 * next middle on left part and middle of right part.
190 ginInsertRecordBA(BuildAccumulator
*accum
, ItemPointer heapptr
, OffsetNumber attnum
,
191 Datum
*entries
, int32 nentry
)
201 for (; i
> 0; i
>>= 1)
205 offset
= (nbit
- nentry
) / 2;
207 ginInsertEntry(accum
, heapptr
, attnum
, entries
[(nbit
>> 1) - offset
]);
208 ginChooseElem(accum
, heapptr
, attnum
, entries
, nentry
, 0, nbit
, offset
);
212 qsortCompareItemPointers(const void *a
, const void *b
)
214 int res
= compareItemPointers((ItemPointer
) a
, (ItemPointer
) b
);
221 * walk on binary tree and returns ordered nodes
223 static EntryAccumulator
*
224 walkTree(BuildAccumulator
*accum
)
226 EntryAccumulator
*entry
= accum
->stack
[accum
->stackpos
];
228 if (entry
->list
!= NULL
)
230 /* return entry itself: we already was at left sublink */
233 else if (entry
->right
&& entry
->right
!= accum
->stack
[accum
->stackpos
+ 1])
235 /* go on right sublink */
237 entry
= entry
->right
;
239 /* find most-left value */
242 accum
->stack
[accum
->stackpos
] = entry
;
254 /* we already return all left subtree, itself and right subtree */
255 if (accum
->stackpos
== 0)
258 return walkTree(accum
);
265 ginGetEntry(BuildAccumulator
*accum
, OffsetNumber
*attnum
, Datum
*value
, uint32
*n
)
267 EntryAccumulator
*entry
;
268 ItemPointerData
*list
;
270 if (accum
->stack
== NULL
)
273 accum
->stack
= palloc0(sizeof(EntryAccumulator
*) * (accum
->maxdepth
+ 1));
274 accum
->allocatedMemory
+= GetMemoryChunkSpace(accum
->stack
);
275 entry
= accum
->entries
;
280 /* find most-left value */
283 accum
->stack
[accum
->stackpos
] = entry
;
295 accum
->allocatedMemory
-= GetMemoryChunkSpace(accum
->stack
[accum
->stackpos
]->list
);
296 pfree(accum
->stack
[accum
->stackpos
]->list
);
297 accum
->stack
[accum
->stackpos
]->list
= NULL
;
298 entry
= walkTree(accum
);
305 *attnum
= entry
->attnum
;
306 *value
= entry
->value
;
309 Assert(list
!= NULL
);
311 if (entry
->shouldSort
&& entry
->number
> 1)
312 qsort(list
, *n
, sizeof(ItemPointerData
), qsortCompareItemPointers
);