Fix xslt_process() to ensure that it inserts a NULL terminator after the
[PostgreSQL.git] / src / backend / access / gin / ginutil.c
blobe5cae785e00a666c891dac03575c3bb5bb894d11
1 /*-------------------------------------------------------------------------
3 * ginutil.c
4 * utilities routines for the postgres inverted index access method.
7 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * $PostgreSQL$
12 *-------------------------------------------------------------------------
15 #include "postgres.h"
16 #include "access/genam.h"
17 #include "access/gin.h"
18 #include "access/reloptions.h"
19 #include "catalog/pg_type.h"
20 #include "storage/bufmgr.h"
21 #include "storage/freespace.h"
22 #include "storage/indexfsm.h"
23 #include "storage/lmgr.h"
25 void
26 initGinState(GinState *state, Relation index)
28 int i;
30 state->origTupdesc = index->rd_att;
32 state->oneCol = (index->rd_att->natts == 1) ? true : false;
34 for (i = 0; i < index->rd_att->natts; i++)
36 state->tupdesc[i] = CreateTemplateTupleDesc(2, false);
38 TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL,
39 INT2OID, -1, 0);
40 TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 2, NULL,
41 index->rd_att->attrs[i]->atttypid,
42 index->rd_att->attrs[i]->atttypmod,
43 index->rd_att->attrs[i]->attndims
46 fmgr_info_copy(&(state->compareFn[i]),
47 index_getprocinfo(index, i + 1, GIN_COMPARE_PROC),
48 CurrentMemoryContext);
49 fmgr_info_copy(&(state->extractValueFn[i]),
50 index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC),
51 CurrentMemoryContext);
52 fmgr_info_copy(&(state->extractQueryFn[i]),
53 index_getprocinfo(index, i + 1, GIN_EXTRACTQUERY_PROC),
54 CurrentMemoryContext);
55 fmgr_info_copy(&(state->consistentFn[i]),
56 index_getprocinfo(index, i + 1, GIN_CONSISTENT_PROC),
57 CurrentMemoryContext);
60 * Check opclass capability to do partial match.
62 if (index_getprocid(index, i + 1, GIN_COMPARE_PARTIAL_PROC) != InvalidOid)
64 fmgr_info_copy(&(state->comparePartialFn[i]),
65 index_getprocinfo(index, i + 1, GIN_COMPARE_PARTIAL_PROC),
66 CurrentMemoryContext);
68 state->canPartialMatch[i] = true;
70 else
72 state->canPartialMatch[i] = false;
78 * Extract attribute (column) number of stored entry from GIN tuple
80 OffsetNumber
81 gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
83 OffsetNumber colN = FirstOffsetNumber;
85 if (!ginstate->oneCol)
87 Datum res;
88 bool isnull;
91 * First attribute is always int16, so we can safely use any tuple
92 * descriptor to obtain first attribute of tuple
94 res = index_getattr(tuple, FirstOffsetNumber, ginstate->tupdesc[0],
95 &isnull);
96 Assert(!isnull);
98 colN = DatumGetUInt16(res);
99 Assert(colN >= FirstOffsetNumber && colN <= ginstate->origTupdesc->natts);
102 return colN;
106 * Extract stored datum from GIN tuple
108 Datum
109 gin_index_getattr(GinState *ginstate, IndexTuple tuple)
111 bool isnull;
112 Datum res;
114 if (ginstate->oneCol)
117 * Single column index doesn't store attribute numbers in tuples
119 res = index_getattr(tuple, FirstOffsetNumber, ginstate->origTupdesc,
120 &isnull);
122 else
125 * Since the datum type depends on which index column it's from, we
126 * must be careful to use the right tuple descriptor here.
128 OffsetNumber colN = gintuple_get_attrnum(ginstate, tuple);
130 res = index_getattr(tuple, OffsetNumberNext(FirstOffsetNumber),
131 ginstate->tupdesc[colN - 1],
132 &isnull);
135 Assert(!isnull);
137 return res;
141 * Allocate a new page (either by recycling, or by extending the index file)
142 * The returned buffer is already pinned and exclusive-locked
143 * Caller is responsible for initializing the page by calling GinInitBuffer
146 Buffer
147 GinNewBuffer(Relation index)
149 Buffer buffer;
150 bool needLock;
152 /* First, try to get a page from FSM */
153 for (;;)
155 BlockNumber blkno = GetFreeIndexPage(index);
157 if (blkno == InvalidBlockNumber)
158 break;
160 buffer = ReadBuffer(index, blkno);
163 * We have to guard against the possibility that someone else already
164 * recycled this page; the buffer may be locked if so.
166 if (ConditionalLockBuffer(buffer))
168 Page page = BufferGetPage(buffer);
170 if (PageIsNew(page))
171 return buffer; /* OK to use, if never initialized */
173 if (GinPageIsDeleted(page))
174 return buffer; /* OK to use */
176 LockBuffer(buffer, GIN_UNLOCK);
179 /* Can't use it, so release buffer and try again */
180 ReleaseBuffer(buffer);
183 /* Must extend the file */
184 needLock = !RELATION_IS_LOCAL(index);
185 if (needLock)
186 LockRelationForExtension(index, ExclusiveLock);
188 buffer = ReadBuffer(index, P_NEW);
189 LockBuffer(buffer, GIN_EXCLUSIVE);
191 if (needLock)
192 UnlockRelationForExtension(index, ExclusiveLock);
194 return buffer;
197 void
198 GinInitPage(Page page, uint32 f, Size pageSize)
200 GinPageOpaque opaque;
202 PageInit(page, pageSize, sizeof(GinPageOpaqueData));
204 opaque = GinPageGetOpaque(page);
205 memset(opaque, 0, sizeof(GinPageOpaqueData));
206 opaque->flags = f;
207 opaque->rightlink = InvalidBlockNumber;
210 void
211 GinInitBuffer(Buffer b, uint32 f)
213 GinInitPage(BufferGetPage(b), f, BufferGetPageSize(b));
216 void
217 GinInitMetabuffer(Buffer b)
219 GinMetaPageData *metadata;
220 Page page = BufferGetPage(b);
222 GinInitPage(page, GIN_META, BufferGetPageSize(b));
224 metadata = GinPageGetMeta(page);
226 metadata->head = metadata->tail = InvalidBlockNumber;
227 metadata->tailFreeSize = 0;
228 metadata->nPendingPages = 0;
229 metadata->nPendingHeapTuples = 0;
233 compareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, Datum b)
235 return DatumGetInt32(
236 FunctionCall2(
237 &ginstate->compareFn[attnum - 1],
238 a, b
244 compareAttEntries(GinState *ginstate, OffsetNumber attnum_a, Datum a,
245 OffsetNumber attnum_b, Datum b)
247 if (attnum_a == attnum_b)
248 return compareEntries(ginstate, attnum_a, a, b);
250 return (attnum_a < attnum_b) ? -1 : 1;
253 typedef struct
255 FmgrInfo *cmpDatumFunc;
256 bool *needUnique;
257 } cmpEntriesData;
259 static int
260 cmpEntries(const Datum *a, const Datum *b, cmpEntriesData *arg)
262 int res = DatumGetInt32(FunctionCall2(arg->cmpDatumFunc,
263 *a, *b));
265 if (res == 0)
266 *(arg->needUnique) = TRUE;
268 return res;
271 Datum *
272 extractEntriesS(GinState *ginstate, OffsetNumber attnum, Datum value, int32 *nentries,
273 bool *needUnique)
275 Datum *entries;
277 entries = (Datum *) DatumGetPointer(FunctionCall2(
278 &ginstate->extractValueFn[attnum - 1],
279 value,
280 PointerGetDatum(nentries)
283 if (entries == NULL)
284 *nentries = 0;
286 *needUnique = FALSE;
287 if (*nentries > 1)
289 cmpEntriesData arg;
291 arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
292 arg.needUnique = needUnique;
293 qsort_arg(entries, *nentries, sizeof(Datum),
294 (qsort_arg_comparator) cmpEntries, (void *) &arg);
297 return entries;
301 Datum *
302 extractEntriesSU(GinState *ginstate, OffsetNumber attnum, Datum value, int32 *nentries)
304 bool needUnique;
305 Datum *entries = extractEntriesS(ginstate, attnum, value, nentries,
306 &needUnique);
308 if (needUnique)
310 Datum *ptr,
311 *res;
313 ptr = res = entries;
315 while (ptr - entries < *nentries)
317 if (compareEntries(ginstate, attnum, *ptr, *res) != 0)
318 *(++res) = *ptr++;
319 else
320 ptr++;
323 *nentries = res + 1 - entries;
326 return entries;
329 Datum
330 ginoptions(PG_FUNCTION_ARGS)
332 Datum reloptions = PG_GETARG_DATUM(0);
333 bool validate = PG_GETARG_BOOL(1);
334 relopt_value *options;
335 GinOptions *rdopts;
336 int numoptions;
337 static const relopt_parse_elt tab[] = {
338 {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)}
341 options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
342 &numoptions);
344 /* if none set, we're done */
345 if (numoptions == 0)
346 PG_RETURN_NULL();
348 rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
350 fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numoptions,
351 validate, tab, lengthof(tab));
353 pfree(options);
355 PG_RETURN_BYTEA_P(rdopts);