1 /*-------------------------------------------------------------------------
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
12 *-------------------------------------------------------------------------
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"
26 initGinState(GinState
*state
, Relation index
)
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
,
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;
72 state
->canPartialMatch
[i
] = false;
78 * Extract attribute (column) number of stored entry from GIN tuple
81 gintuple_get_attrnum(GinState
*ginstate
, IndexTuple tuple
)
83 OffsetNumber colN
= FirstOffsetNumber
;
85 if (!ginstate
->oneCol
)
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],
98 colN
= DatumGetUInt16(res
);
99 Assert(colN
>= FirstOffsetNumber
&& colN
<= ginstate
->origTupdesc
->natts
);
106 * Extract stored datum from GIN tuple
109 gin_index_getattr(GinState
*ginstate
, IndexTuple tuple
)
114 if (ginstate
->oneCol
)
117 * Single column index doesn't store attribute numbers in tuples
119 res
= index_getattr(tuple
, FirstOffsetNumber
, ginstate
->origTupdesc
,
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],
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
147 GinNewBuffer(Relation index
)
152 /* First, try to get a page from FSM */
155 BlockNumber blkno
= GetFreeIndexPage(index
);
157 if (blkno
== InvalidBlockNumber
)
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
);
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
);
186 LockRelationForExtension(index
, ExclusiveLock
);
188 buffer
= ReadBuffer(index
, P_NEW
);
189 LockBuffer(buffer
, GIN_EXCLUSIVE
);
192 UnlockRelationForExtension(index
, ExclusiveLock
);
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
));
207 opaque
->rightlink
= InvalidBlockNumber
;
211 GinInitBuffer(Buffer b
, uint32 f
)
213 GinInitPage(BufferGetPage(b
), f
, BufferGetPageSize(b
));
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(
237 &ginstate
->compareFn
[attnum
- 1],
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;
255 FmgrInfo
*cmpDatumFunc
;
260 cmpEntries(const Datum
*a
, const Datum
*b
, cmpEntriesData
*arg
)
262 int res
= DatumGetInt32(FunctionCall2(arg
->cmpDatumFunc
,
266 *(arg
->needUnique
) = TRUE
;
272 extractEntriesS(GinState
*ginstate
, OffsetNumber attnum
, Datum value
, int32
*nentries
,
277 entries
= (Datum
*) DatumGetPointer(FunctionCall2(
278 &ginstate
->extractValueFn
[attnum
- 1],
280 PointerGetDatum(nentries
)
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
);
302 extractEntriesSU(GinState
*ginstate
, OffsetNumber attnum
, Datum value
, int32
*nentries
)
305 Datum
*entries
= extractEntriesS(ginstate
, attnum
, value
, nentries
,
315 while (ptr
- entries
< *nentries
)
317 if (compareEntries(ginstate
, attnum
, *ptr
, *res
) != 0)
323 *nentries
= res
+ 1 - entries
;
330 ginoptions(PG_FUNCTION_ARGS
)
332 Datum reloptions
= PG_GETARG_DATUM(0);
333 bool validate
= PG_GETARG_BOOL(1);
334 relopt_value
*options
;
337 static const relopt_parse_elt tab
[] = {
338 {"fastupdate", RELOPT_TYPE_BOOL
, offsetof(GinOptions
, useFastUpdate
)}
341 options
= parseRelOptions(reloptions
, validate
, RELOPT_KIND_GIN
,
344 /* if none set, we're done */
348 rdopts
= allocateReloptStruct(sizeof(GinOptions
), options
, numoptions
);
350 fillRelOptions((void *) rdopts
, sizeof(GinOptions
), options
, numoptions
,
351 validate
, tab
, lengthof(tab
));
355 PG_RETURN_BYTEA_P(rdopts
);