1 : /*-------------------------------------------------------------------------
2 : *
3 : * ginutil.c
4 : * utilities routines for the postgres inverted index access method.
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/ginutil.c,v 1.12 2007/11/15 22:25:14 momjian Exp $
12 : *-------------------------------------------------------------------------
13 : */
14 :
15 : #include "postgres.h"
16 : #include "access/genam.h"
17 : #include "access/gin.h"
18 : #include "access/heapam.h"
19 : #include "access/reloptions.h"
20 : #include "storage/freespace.h"
21 :
22 : void
23 : initGinState(GinState *state, Relation index)
24 33 : {
25 33 : if (index->rd_att->natts != 1)
26 0 : elog(ERROR, "numberOfAttributes %d != 1",
27 : index->rd_att->natts);
28 :
29 33 : state->tupdesc = index->rd_att;
30 :
31 33 : fmgr_info_copy(&(state->compareFn),
32 : index_getprocinfo(index, 1, GIN_COMPARE_PROC),
33 : CurrentMemoryContext);
34 33 : fmgr_info_copy(&(state->extractValueFn),
35 : index_getprocinfo(index, 1, GIN_EXTRACTVALUE_PROC),
36 : CurrentMemoryContext);
37 33 : fmgr_info_copy(&(state->extractQueryFn),
38 : index_getprocinfo(index, 1, GIN_EXTRACTQUERY_PROC),
39 : CurrentMemoryContext);
40 33 : fmgr_info_copy(&(state->consistentFn),
41 : index_getprocinfo(index, 1, GIN_CONSISTENT_PROC),
42 : CurrentMemoryContext);
43 33 : }
44 :
45 : /*
46 : * Allocate a new page (either by recycling, or by extending the index file)
47 : * The returned buffer is already pinned and exclusive-locked
48 : * Caller is responsible for initializing the page by calling GinInitBuffer
49 : */
50 :
51 : Buffer
52 : GinNewBuffer(Relation index)
53 66 : {
54 : Buffer buffer;
55 : bool needLock;
56 :
57 : /* First, try to get a page from FSM */
58 : for (;;)
59 : {
60 66 : BlockNumber blkno = GetFreeIndexPage(&index->rd_node);
61 :
62 66 : if (blkno == InvalidBlockNumber)
63 66 : break;
64 :
65 0 : buffer = ReadBuffer(index, blkno);
66 :
67 : /*
68 : * We have to guard against the possibility that someone else already
69 : * recycled this page; the buffer may be locked if so.
70 : */
71 0 : if (ConditionalLockBuffer(buffer))
72 : {
73 0 : Page page = BufferGetPage(buffer);
74 :
75 0 : if (PageIsNew(page))
76 0 : return buffer; /* OK to use, if never initialized */
77 :
78 0 : if (GinPageIsDeleted(page))
79 0 : return buffer; /* OK to use */
80 :
81 0 : LockBuffer(buffer, GIN_UNLOCK);
82 : }
83 :
84 : /* Can't use it, so release buffer and try again */
85 0 : ReleaseBuffer(buffer);
86 0 : }
87 :
88 : /* Must extend the file */
89 66 : needLock = !RELATION_IS_LOCAL(index);
90 66 : if (needLock)
91 0 : LockRelationForExtension(index, ExclusiveLock);
92 :
93 66 : buffer = ReadBuffer(index, P_NEW);
94 66 : LockBuffer(buffer, GIN_EXCLUSIVE);
95 :
96 66 : if (needLock)
97 0 : UnlockRelationForExtension(index, ExclusiveLock);
98 :
99 66 : return buffer;
100 : }
101 :
102 : void
103 : GinInitPage(Page page, uint32 f, Size pageSize)
104 108 : {
105 : GinPageOpaque opaque;
106 :
107 108 : PageInit(page, pageSize, sizeof(GinPageOpaqueData));
108 :
109 108 : opaque = GinPageGetOpaque(page);
110 108 : memset(opaque, 0, sizeof(GinPageOpaqueData));
111 108 : opaque->flags = f;
112 108 : opaque->rightlink = InvalidBlockNumber;
113 108 : }
114 :
115 : void
116 : GinInitBuffer(Buffer b, uint32 f)
117 24 : {
118 24 : GinInitPage(BufferGetPage(b), f, BufferGetPageSize(b));
119 24 : }
120 :
121 : int
122 : compareEntries(GinState *ginstate, Datum a, Datum b)
123 308432 : {
124 308432 : return DatumGetInt32(
125 : FunctionCall2(
126 : &ginstate->compareFn,
127 : a, b
128 : )
129 : );
130 : }
131 :
132 : typedef struct
133 : {
134 : FmgrInfo *cmpDatumFunc;
135 : bool *needUnique;
136 : } cmpEntriesData;
137 :
138 : static int
139 : cmpEntries(const Datum *a, const Datum *b, cmpEntriesData *arg)
140 30676 : {
141 30676 : int res = DatumGetInt32(FunctionCall2(arg->cmpDatumFunc,
142 : *a, *b));
143 :
144 30676 : if (res == 0)
145 21 : *(arg->needUnique) = TRUE;
146 :
147 30676 : return res;
148 : }
149 :
150 : Datum *
151 : extractEntriesS(GinState *ginstate, Datum value, int32 *nentries,
152 : bool *needUnique)
153 712 : {
154 : Datum *entries;
155 :
156 712 : entries = (Datum *) DatumGetPointer(FunctionCall2(
157 : &ginstate->extractValueFn,
158 : value,
159 : PointerGetDatum(nentries)
160 : ));
161 :
162 712 : if (entries == NULL)
163 9 : *nentries = 0;
164 :
165 712 : *needUnique = FALSE;
166 712 : if (*nentries > 1)
167 : {
168 : cmpEntriesData arg;
169 :
170 687 : arg.cmpDatumFunc = &ginstate->compareFn;
171 687 : arg.needUnique = needUnique;
172 687 : qsort_arg(entries, *nentries, sizeof(Datum),
173 : (qsort_arg_comparator) cmpEntries, (void *) &arg);
174 : }
175 :
176 712 : return entries;
177 : }
178 :
179 :
180 : Datum *
181 : extractEntriesSU(GinState *ginstate, Datum value, int32 *nentries)
182 712 : {
183 : bool needUnique;
184 : Datum *entries = extractEntriesS(ginstate, value, nentries,
185 712 : &needUnique);
186 :
187 712 : if (needUnique)
188 : {
189 : Datum *ptr,
190 : *res;
191 :
192 19 : ptr = res = entries;
193 :
194 189 : while (ptr - entries < *nentries)
195 : {
196 151 : if (compareEntries(ginstate, *ptr, *res) != 0)
197 113 : *(++res) = *ptr++;
198 : else
199 38 : ptr++;
200 : }
201 :
202 19 : *nentries = res + 1 - entries;
203 : }
204 :
205 712 : return entries;
206 : }
207 :
208 : /*
209 : * It's analog of PageGetTempPage(), but copies whole page
210 : */
211 : Page
212 : GinPageGetCopyPage(Page page)
213 42 : {
214 42 : Size pageSize = PageGetPageSize(page);
215 : Page tmppage;
216 :
217 42 : tmppage = (Page) palloc(pageSize);
218 42 : memcpy(tmppage, page, pageSize);
219 :
220 42 : return tmppage;
221 : }
222 :
223 : Datum
224 : ginoptions(PG_FUNCTION_ARGS)
225 0 : {
226 0 : Datum reloptions = PG_GETARG_DATUM(0);
227 0 : bool validate = PG_GETARG_BOOL(1);
228 : bytea *result;
229 :
230 : /*
231 : * It's not clear that fillfactor is useful for GIN, but for the moment
232 : * we'll accept it anyway. (It won't do anything...)
233 : */
234 : #define GIN_MIN_FILLFACTOR 10
235 : #define GIN_DEFAULT_FILLFACTOR 100
236 :
237 0 : result = default_reloptions(reloptions, validate,
238 : GIN_MIN_FILLFACTOR,
239 : GIN_DEFAULT_FILLFACTOR);
240 0 : if (result)
241 0 : PG_RETURN_BYTEA_P(result);
242 0 : PG_RETURN_NULL();
243 : }
|