Fix GIN to support null keys, empty and null items, and full index scans.

Per my recent proposal(s).  Null key datums can now be returned by
extractValue and extractQuery functions, and will be stored in the index.
Also, placeholder entries are made for indexable items that are NULL or
contain no keys according to extractValue.  This means that the index is
now always complete, having at least one entry for every indexed heap TID,
and so we can get rid of the prohibition on full-index scans.  A full-index
scan is implemented much the same way as partial-match scans were already:
we build a bitmap representing all the TIDs found in the index, and then
drive the results off that.

Also, introduce a concept of a "search mode" that can be requested by
extractQuery when the operator requires matching to empty items (this is
just as cheap as matching to a single key) or requires a full index scan
(which is not so cheap, but it sure beats failing or giving wrong answers).
The behavior remains backward compatible for opclasses that don't return
any null keys or request a non-default search mode.

Using these features, we can now make the GIN index opclass for anyarray
behave in a way that matches the actual anyarray operators for &&, <@, @>,
and = ... which it failed to do before in assorted corner cases.

This commit fixes the core GIN code and ginarrayprocs.c, updates the
documentation, and adds some simple regression test cases for the new
behaviors using the array operators.  The tsearch and contrib GIN opclass
support functions still need to be looked over and probably fixed.

Another thing I intend to fix separately is that this is pretty inefficient
for cases where more than one scan condition needs a full-index search:
we'll run duplicate GinScanEntrys, each one of which builds a large bitmap.
There is some existing logic to merge duplicate GinScanEntrys but it needs
refactoring to make it work for entries belonging to different scan keys.

Note that most of gin.h has been split out into a new file gin_private.h,
so that gin.h doesn't export anything that's not supposed to be used by GIN
opclasses or the rest of the backend.  I did quite a bit of other code
beautification work as well, mostly fixing comments and choosing more
appropriate names for things.
This commit is contained in:
Tom Lane 2011-01-07 19:16:24 -05:00
parent 9b4271deb9
commit 73912e7fbd
23 changed files with 3505 additions and 2049 deletions

View File

@ -4,6 +4,7 @@
#include "postgres.h"
#include "access/gin.h"
#include "access/skey.h"
#include "catalog/pg_type.h"
#include "hstore.h"

View File

@ -12,17 +12,38 @@
<title>Introduction</title>
<para>
<acronym>GIN</acronym> stands for Generalized Inverted Index. It is
an index structure storing a set of (key, posting list) pairs, where
a <quote>posting list</> is a set of rows in which the key occurs. Each
indexed value can contain many keys, so the same row ID can appear in
multiple posting lists.
<acronym>GIN</acronym> stands for Generalized Inverted Index.
<acronym>GIN</acronym> is designed for handling cases where the items
to be indexed are composite values, and the queries to be handled by
the index need to search for element values that appear within
the composite items. For example, the items could be documents,
and the queries could be searches for documents containing specific words.
</para>
<para>
It is generalized in the sense that a <acronym>GIN</acronym> index
does not need to be aware of the operation that it accelerates.
Instead, it uses custom strategies defined for particular data types.
We use the word <firstterm>item</> to refer to a composite value that
is to be indexed, and the word <firstterm>key</> to refer to an element
value. <acronym>GIN</acronym> always stores and searches for keys,
not item values per se.
</para>
<para>
A <acronym>GIN</acronym> index stores a set of (key, posting list) pairs,
where a <firstterm>posting list</> is a set of row IDs in which the key
occurs. The same row ID can appear in multiple posting lists, since
an item can contain more than one key. Each key value is stored only
once, so a <acronym>GIN</acronym> index is very compact for cases
where the same key appears many times.
</para>
<para>
<acronym>GIN</acronym> is generalized in the sense that the
<acronym>GIN</acronym> access method code does not need to know the
specific operations that it accelerates.
Instead, it uses custom strategies defined for particular data types.
The strategy defines how keys are extracted from indexed items and
query conditions, and how to determine whether a row that contains
some of the key values in a query actually satisfies the query.
</para>
<para>
@ -54,7 +75,7 @@
<para>
All it takes to get a <acronym>GIN</acronym> access method working is to
implement four (or five) user-defined methods, which define the behavior of
keys in the tree and the relationships between keys, indexed values,
keys in the tree and the relationships between keys, indexed items,
and indexable queries. In short, <acronym>GIN</acronym> combines
extensibility with generality, code reuse, and a clean interface.
</para>
@ -68,53 +89,92 @@
<term><function>int compare(Datum a, Datum b)</></term>
<listitem>
<para>
Compares keys (not indexed values!) and returns an integer less than
Compares two keys (not indexed items!) and returns an integer less than
zero, zero, or greater than zero, indicating whether the first key is
less than, equal to, or greater than the second.
less than, equal to, or greater than the second. Null keys are never
passed to this function.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>Datum *extractValue(Datum inputValue, int32 *nkeys)</></term>
<term><function>Datum *extractValue(Datum itemValue, int32 *nkeys,
bool **nullFlags)</></term>
<listitem>
<para>
Returns an array of keys given a value to be indexed. The
Returns a palloc'd array of keys given an item to be indexed. The
number of returned keys must be stored into <literal>*nkeys</>.
If any of the keys can be null, also palloc an array of
<literal>*nkeys</> booleans, store its address at
<literal>*nullFlags</>, and set these null flags as needed.
<literal>*nullFlags</> can be left NULL (its initial value)
if all keys are non-null.
The return value can be NULL if the item contains no keys.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><function>Datum *extractQuery(Datum query, int32 *nkeys,
StrategyNumber n, bool **pmatch, Pointer **extra_data)</></term>
StrategyNumber n, bool **pmatch, Pointer **extra_data,
bool **nullFlags, int32 *searchMode)</></term>
<listitem>
<para>
Returns an array of keys given a value to be queried; that is,
Returns a palloc'd array of keys given a value to be queried; that is,
<literal>query</> is the value on the right-hand side of an
indexable operator whose left-hand side is the indexed column.
<literal>n</> is the strategy number of the operator within the
operator class (see <xref linkend="xindex-strategies">).
Often, <function>extractQuery</> will need
to consult <literal>n</> to determine the data type of
<literal>query</> and the key values that need to be extracted.
<literal>query</> and the method it should use to extract key values.
The number of returned keys must be stored into <literal>*nkeys</>.
If the query contains no keys then <function>extractQuery</>
should store 0 or -1 into <literal>*nkeys</>, depending on the
semantics of the operator. 0 means that every
value matches the <literal>query</> and a full-index scan should be
performed (but see <xref linkend="gin-limit">).
-1 means that nothing can match the <literal>query</>, and
so the index scan can be skipped entirely.
If any of the keys can be null, also palloc an array of
<literal>*nkeys</> booleans, store its address at
<literal>*nullFlags</>, and set these null flags as needed.
<literal>*nullFlags</> can be left NULL (its initial value)
if all keys are non-null.
The return value can be NULL if the <literal>query</> contains no keys.
</para>
<para>
<literal>searchMode</> is an output argument that allows
<function>extractQuery</> to specify details about how the search
will be done.
If <literal>*searchMode</> is set to
<literal>GIN_SEARCH_MODE_DEFAULT</> (which is the value it is
initialized to before call), only items that match at least one of
the returned keys are considered candidate matches.
If <literal>*searchMode</> is set to
<literal>GIN_SEARCH_MODE_INCLUDE_EMPTY</>, then in addition to items
containing at least one matching key, items that contain no keys at
all are considered candidate matches. (This mode is useful for
implementing is-subset-of operators, for example.)
If <literal>*searchMode</> is set to <literal>GIN_SEARCH_MODE_ALL</>,
then all non-null items in the index are considered candidate
matches, whether they match any of the returned keys or not. (This
mode is much slower than the other two choices, since it requires
scanning essentially the entire index, but it may be necessary to
implement corner cases correctly. An operator that needs this mode
in most cases is probably not a good candidate for a GIN operator
class.)
The symbols to use for setting this mode are defined in
<filename>access/gin.h</>.
</para>
<para>
<literal>pmatch</> is an output argument for use when partial match
is supported. To use it, <function>extractQuery</> must allocate
an array of <literal>*nkeys</> Booleans and store its address at
an array of <literal>*nkeys</> booleans and store its address at
<literal>*pmatch</>. Each element of the array should be set to TRUE
if the corresponding key requires partial match, FALSE if not.
If <literal>*pmatch</> is set to NULL then GIN assumes partial match
is not required. The variable is initialized to NULL before call,
so this argument can simply be ignored by operator classes that do
not support partial match.
</para>
<para>
<literal>extra_data</> is an output argument that allows
<function>extractQuery</> to pass additional data to the
<function>consistent</> and <function>comparePartial</> methods.
@ -133,25 +193,51 @@
<varlistentry>
<term><function>bool consistent(bool check[], StrategyNumber n, Datum query,
int32 nkeys, Pointer extra_data[], bool *recheck)</></term>
int32 nkeys, Pointer extra_data[], bool *recheck,
Datum queryKeys[], bool nullFlags[])</></term>
<listitem>
<para>
Returns TRUE if the indexed value satisfies the query operator with
strategy number <literal>n</> (or might satisfy, if the recheck
indication is returned). The <literal>check</> array has length
Returns TRUE if an indexed item satisfies the query operator with
strategy number <literal>n</> (or might satisfy it, if the recheck
indication is returned). This function does not have direct access
to the indexed item's value, since <acronym>GIN</acronym> does not
store items explicitly. Rather, what is available is knowledge
about which key values extracted from the query appear in a given
indexed item. The <literal>check</> array has length
<literal>nkeys</>, which is the same as the number of keys previously
returned by <function>extractQuery</> for this <literal>query</> datum.
Each element of the
<literal>check</> array is TRUE if the indexed value contains the
<literal>check</> array is TRUE if the indexed item contains the
corresponding query key, ie, if (check[i] == TRUE) the i-th key of the
<function>extractQuery</> result array is present in the indexed value.
The original <literal>query</> datum (not the extracted key array!) is
passed in case the <function>consistent</> method needs to consult it.
<function>extractQuery</> result array is present in the indexed item.
The original <literal>query</> datum is
passed in case the <function>consistent</> method needs to consult it,
and so are the <literal>queryKeys[]</> and <literal>nullFlags[]</>
arrays previously returned by <function>extractQuery</>.
<literal>extra_data</> is the extra-data array returned by
<function>extractQuery</>, or NULL if none.
</para>
<para>
When <function>extractQuery</> returns a null key in
<literal>queryKeys[]</>, the corresponding <literal>check[]</> element
is TRUE if the indexed item contains a null key; that is, the
semantics of <literal>check[]</> are like <literal>IS NOT DISTINCT
FROM</>. The <function>consistent</> function can examine the
corresponding <literal>nullFlags[]</> element if it needs to tell
the difference between a regular value match and a null match.
</para>
<para>
On success, <literal>*recheck</> should be set to TRUE if the heap
tuple needs to be rechecked against the query operator, or FALSE if
the index test is exact.
the index test is exact. That is, a FALSE return value guarantees
that the heap tuple does not match the query; a TRUE return value with
<literal>*recheck</> set to FALSE guarantees that the heap tuple does
match the query; and a TRUE return value with
<literal>*recheck</> set to TRUE means that the heap tuple might match
the query, so it needs to be fetched and rechecked by evaluating the
query operator directly against the originally indexed item.
</para>
</listitem>
</varlistentry>
@ -166,7 +252,7 @@
Pointer extra_data)</></term>
<listitem>
<para>
Compare a partial-match query to an index key. Returns an integer
Compare a partial-match query key to an index key. Returns an integer
whose sign indicates the result: less than zero means the index key
does not match the query, but the index scan should continue; zero
means that the index key does match the query; greater than zero
@ -176,6 +262,7 @@
semantics are needed to determine when to end the scan. Also,
<literal>extra_data</> is the corresponding element of the extra-data
array made by <function>extractQuery</>, or NULL if none.
Null keys are never passed to this function.
</para>
</listitem>
</varlistentry>
@ -190,6 +277,18 @@
<xref linkend="gin-partial-match"> for details.
</para>
<para>
The actual data types of the various <literal>Datum</> values mentioned
above vary depending on the operator class. The item values passed to
<function>extractValue</> are always of the operator class's input type, and
all key values must be of the class's <literal>STORAGE</> type. The type of
the <literal>query</> argument passed to <function>extractQuery</> and
<function>consistent</> is whatever is specified as the right-hand input
type of the class member operator identified by the strategy number.
This need not be the same as the item type, so long as key values of the
correct type can be extracted from it.
</para>
</sect1>
<sect1 id="gin-implementation">
@ -197,10 +296,26 @@
<para>
Internally, a <acronym>GIN</acronym> index contains a B-tree index
constructed over keys, where each key is an element of the indexed value
(a member of an array, for example) and where each tuple in a leaf page is
either a pointer to a B-tree over heap pointers (PT, posting tree), or a
list of heap pointers (PL, posting list) if the list is small enough.
constructed over keys, where each key is an element of one or more indexed
items (a member of an array, for example) and where each tuple in a leaf
page contains either a pointer to a B-tree of heap pointers (a
<quote>posting tree</>), or a simple list of heap pointers (a <quote>posting
list</>) when the list is small enough to fit into a single index tuple along
with the key value.
</para>
<para>
As of <productname>PostgreSQL</productname> 9.1, NULL key values can be
included in the index. Also, placeholder NULLs are included in the index
for indexed items that are NULL or contain no keys according to
<function>extractValue</>. This allows searches that should find empty
items to do so.
</para>
<para>
Multi-column <acronym>GIN</acronym> indexes are implemented by building
a single B-tree over composite values (column number, key value). The
key values for different columns can be of different types.
</para>
<sect2 id="gin-fast-update">
@ -210,7 +325,7 @@
Updating a <acronym>GIN</acronym> index tends to be slow because of the
intrinsic nature of inverted indexes: inserting or updating one heap row
can cause many inserts into the index (one for each key extracted
from the indexed value). As of <productname>PostgreSQL</productname> 8.4,
from the indexed item). As of <productname>PostgreSQL</productname> 8.4,
<acronym>GIN</> is capable of postponing much of this work by inserting
new tuples into a temporary, unsorted list of pending entries.
When the table is vacuumed, or if the pending list becomes too large
@ -218,7 +333,7 @@
main <acronym>GIN</acronym> data structure using the same bulk insert
techniques used during initial index creation. This greatly improves
<acronym>GIN</acronym> index update speed, even counting the additional
vacuum overhead. Moreover the overhead can be done by a background
vacuum overhead. Moreover the overhead work can be done by a background
process instead of in foreground query processing.
</para>
@ -252,9 +367,9 @@
The <function>extractQuery</> method, instead of returning a key value
to be matched exactly, returns a key value that is the lower bound of
the range to be searched, and sets the <literal>pmatch</> flag true.
The key range is then searched using the <function>comparePartial</>
method. <function>comparePartial</> must return zero for an actual
match, less than zero for a non-match that is still within the range
The key range is then scanned using the <function>comparePartial</>
method. <function>comparePartial</> must return zero for a matching
index key, less than zero for a non-match that is still within the range
to be searched, or greater than zero if the index key is past the range
that could match.
</para>
@ -271,7 +386,7 @@
<listitem>
<para>
Insertion into a <acronym>GIN</acronym> index can be slow
due to the likelihood of many keys being inserted for each value.
due to the likelihood of many keys being inserted for each item.
So, for bulk insertions into a table it is advisable to drop the GIN
index and recreate it after finishing bulk insertion.
</para>
@ -302,7 +417,7 @@
<para>
During a series of insertions into an existing <acronym>GIN</acronym>
index that has <literal>FASTUPDATE</> enabled, the system will clean up
the pending-entry list whenever it grows larger than
the pending-entry list whenever the list grows larger than
<varname>work_mem</>. To avoid fluctuations in observed response time,
it's desirable to have pending-list cleanup occur in the background
(i.e., via autovacuum). Foreground cleanup operations can be avoided by
@ -318,7 +433,7 @@
<listitem>
<para>
The primary goal of developing <acronym>GIN</acronym> indexes was
to create support for highly scalable, full-text search in
to create support for highly scalable full-text search in
<productname>PostgreSQL</productname>, and there are often situations when
a full-text search returns a very large set of results. Moreover, this
often happens when the query contains very frequent words, so that the
@ -328,9 +443,9 @@
fast.)
</para>
<para>
To facilitate controlled execution of such queries
To facilitate controlled execution of such queries,
<acronym>GIN</acronym> has a configurable soft upper limit on the
number of rows returned, the
number of rows returned: the
<varname>gin_fuzzy_search_limit</varname> configuration parameter.
It is set to 0 (meaning no limit) by default.
If a non-zero limit is set, then the returned set is a subset of
@ -338,9 +453,13 @@
</para>
<para>
<quote>Soft</quote> means that the actual number of returned results
could differ slightly from the specified limit, depending on the query
could differ somewhat from the specified limit, depending on the query
and the quality of the system's random number generator.
</para>
<para>
From experience, values in the thousands (e.g., 5000 &mdash; 20000)
work well.
</para>
</listitem>
</varlistentry>
</variablelist>
@ -351,44 +470,13 @@
<title>Limitations</title>
<para>
<acronym>GIN</acronym> doesn't support full index scans. The reason for
this is that <function>extractValue</> is allowed to return zero keys,
as for example might happen with an empty string or empty array. In such
a case the indexed value will be unrepresented in the index. It is
therefore impossible for <acronym>GIN</acronym> to guarantee that a
scan of the index can find every row in the table.
</para>
<para>
Because of this limitation, when <function>extractQuery</function> returns
<literal>nkeys = 0</> to indicate that all values match the query,
<acronym>GIN</acronym> will emit an error. (If there are multiple ANDed
indexable operators in the query, this happens only if they all return zero
for <literal>nkeys</>.)
</para>
<para>
It is possible for an operator class to circumvent the restriction against
full index scan. To do that, <function>extractValue</> must return at least
one (possibly dummy) key for every indexed value, and
<function>extractQuery</function> must convert an unrestricted search into
a partial-match query that will scan the whole index. This is inefficient
but might be necessary to avoid corner-case failures with operators such
as <literal>LIKE</> or subset inclusion.
</para>
<para>
<acronym>GIN</acronym> assumes that indexable operators are strict.
This means that <function>extractValue</> will not be called at all on
a NULL value (so the value will go unindexed), and
<function>extractQuery</function> will not be called on a NULL comparison
value either (instead, the query is presumed to be unmatchable).
</para>
<para>
A possibly more serious limitation is that <acronym>GIN</acronym> cannot
handle NULL keys &mdash; for example, an array containing a NULL cannot
be handled except by ignoring the NULL.
<acronym>GIN</acronym> assumes that indexable operators are strict. This
means that <function>extractValue</> will not be called at all on a NULL
item value (instead, a placeholder index entry is created automatically),
and <function>extractQuery</function> will not be called on a NULL query
value either (instead, the query is presumed to be unsatisfiable). Note
however that NULL key values contained within a non-null composite item
or query value are supported.
</para>
</sect1>

View File

@ -15,23 +15,23 @@ is similar to GiST and differs from btree indices, which have predefined,
comparison-based operations.
An inverted index is an index structure storing a set of (key, posting list)
pairs, where 'posting list' is a set of documents in which the key occurs.
pairs, where 'posting list' is a set of heap rows in which the key occurs.
(A text document would usually contain many keys.) The primary goal of
Gin indices is support for highly scalable, full-text search in PostgreSQL.
Gin consists of a B-tree index constructed over entries (ET, entries tree),
where each entry is an element of the indexed value (element of array, lexeme
for tsvector) and where each tuple in a leaf page is either a pointer to a
B-tree over item pointers (PT, posting tree), or a list of item pointers
(PL, posting list) if the tuple is small enough.
A Gin index consists of a B-tree index constructed over key values,
where each key is an element of some indexed items (element of array, lexeme
for tsvector) and where each tuple in a leaf page contains either a pointer to
a B-tree over item pointers (posting tree), or a simple list of item pointers
(posting list) if the list is small enough.
Note: There is no delete operation for ET. The reason for this is that in
our experience, the set of distinct words in a large corpus changes very
rarely. This greatly simplifies the code and concurrency algorithms.
Note: There is no delete operation in the key (entry) tree. The reason for
this is that in our experience, the set of distinct words in a large corpus
changes very slowly. This greatly simplifies the code and concurrency
algorithms.
Gin comes with built-in support for one-dimensional arrays (eg. integer[],
text[]), but no support for NULL elements. The following operations are
available:
Core PostgreSQL includes built-in Gin support for one-dimensional arrays
(eg. integer[], text[]). The following operations are available:
* contains: value_array @> query_array
* overlaps: value_array && query_array
@ -71,7 +71,7 @@ limit).
If a non-zero search limit is set, then the returned set is a subset of the
whole result set, chosen at random.
"Soft" means that the actual number of returned results could slightly differ
"Soft" means that the actual number of returned results could differ
from the specified limit, depending on the query and the quality of the
system's random number generator.
@ -80,40 +80,156 @@ From experience, a value of 'gin_fuzzy_search_limit' in the thousands
have no effect for queries returning a result set with less tuples than this
number.
Index structure
---------------
The "items" that a GIN index indexes are composite values that contain
zero or more "keys". For example, an item might be an integer array, and
then the keys would be the individual integer values. The index actually
stores and searches for the key values, not the items per se. In the
pg_opclass entry for a GIN opclass, the opcintype is the data type of the
items, and the opckeytype is the data type of the keys. GIN is optimized
for cases where items contain many keys and the same key values appear
in many different items.
A GIN index contains a metapage, a btree of key entries, and possibly
"posting tree" pages, which hold the overflow when a key entry acquires
too many heap tuple pointers to fit in a btree page. Additionally, if the
fast-update feature is enabled, there can be "list pages" holding "pending"
key entries that haven't yet been merged into the main btree. The list
pages have to be scanned linearly when doing a search, so the pending
entries should be merged into the main btree before there get to be too
many of them. The advantage of the pending list is that bulk insertion of
a few thousand entries can be much faster than retail insertion. (The win
comes mainly from not having to do multiple searches/insertions when the
same key appears in multiple new heap tuples.)
Key entries are nominally of the same IndexEntry format as used in other
index types, but since a leaf key entry typically refers to multiple heap
tuples, there are significant differences. (See GinFormTuple, which works
by building a "normal" index tuple and then modifying it.) The points to
know are:
* In a single-column index, a key tuple just contains the key datum, but
in a multi-column index, a key tuple contains the pair (column number,
key datum) where the column number is stored as an int2. This is needed
to support different key data types in different columns. This much of
the tuple is built by index_form_tuple according to the usual rules.
The column number (if present) can never be null, but the key datum can
be, in which case a null bitmap is present as usual. (As usual for index
tuples, the size of the null bitmap is fixed at INDEX_MAX_KEYS.)
* If the key datum is null (ie, IndexTupleHasNulls() is true), then
just after the nominal index data (ie, at offset IndexInfoFindDataOffset
or IndexInfoFindDataOffset + sizeof(int2)) there is a byte indicating
the "category" of the null entry. These are the possible categories:
1 = ordinary null key value extracted from an indexable item
2 = placeholder for zero-key indexable item
3 = placeholder for null indexable item
Placeholder null entries are inserted into the index because otherwise
there would be no index entry at all for an empty or null indexable item,
which would mean that full index scans couldn't be done and various corner
cases would give wrong answers. The different categories of null entries
are treated as distinct keys by the btree, but heap itempointers for the
same category of null entry are merged into one index entry just as happens
with ordinary key entries.
* In a key entry at the btree leaf level, at the next SHORTALIGN boundary,
there is an array of zero or more ItemPointers, which store the heap tuple
TIDs for which the indexable items contain this key. This is called the
"posting list". The TIDs in a posting list must appear in sorted order.
If the list would be too big for the index tuple to fit on an index page,
the ItemPointers are pushed out to a separate posting page or pages, and
none appear in the key entry itself. The separate pages are called a
"posting tree"; they are organized as a btree of ItemPointer values.
Note that in either case, the ItemPointers associated with a key can
easily be read out in sorted order; this is relied on by the scan
algorithms.
* The index tuple header fields of a leaf key entry are abused as follows:
1) Posting list case:
* ItemPointerGetBlockNumber(&itup->t_tid) contains the offset from index
tuple start to the posting list.
Access macros: GinGetPostingOffset(itup) / GinSetPostingOffset(itup,n)
* ItemPointerGetOffsetNumber(&itup->t_tid) contains the number of elements
in the posting list (number of heap itempointers).
Access macros: GinGetNPosting(itup) / GinSetNPosting(itup,n)
* If IndexTupleHasNulls(itup) is true, the null category byte can be
accessed/set with GinGetNullCategory(itup,gs) / GinSetNullCategory(itup,gs,c)
* The posting list can be accessed with GinGetPosting(itup)
2) Posting tree case:
* ItemPointerGetBlockNumber(&itup->t_tid) contains the index block number
of the root of the posting tree.
Access macros: GinGetPostingTree(itup) / GinSetPostingTree(itup, blkno)
* ItemPointerGetOffsetNumber(&itup->t_tid) contains the magic number
GIN_TREE_POSTING, which distinguishes this from the posting-list case
(it's large enough that that many heap itempointers couldn't possibly
fit on an index page). This value is inserted automatically by the
GinSetPostingTree macro.
* If IndexTupleHasNulls(itup) is true, the null category byte can be
accessed/set with GinGetNullCategory(itup) / GinSetNullCategory(itup,c)
* The posting list is not present and must not be accessed.
Use the macro GinIsPostingTree(itup) to determine which case applies.
In both cases, itup->t_info & INDEX_SIZE_MASK contains actual total size of
tuple, and the INDEX_VAR_MASK and INDEX_NULL_MASK bits have their normal
meanings as set by index_form_tuple.
Index tuples in non-leaf levels of the btree contain the optional column
number, key datum, and null category byte as above. They do not contain
a posting list. ItemPointerGetBlockNumber(&itup->t_tid) is the downlink
to the next lower btree level, and ItemPointerGetOffsetNumber(&itup->t_tid)
is InvalidOffsetNumber. Use the access macros GinGetDownlink/GinSetDownlink
to get/set the downlink.
Index entries that appear in "pending list" pages work a tad differently as
well. The optional column number, key datum, and null category byte are as
for other GIN index entries. However, there is always exactly one heap
itempointer associated with a pending entry, and it is stored in the t_tid
header field just as in non-GIN indexes. There is no posting list.
Furthermore, the code that searches the pending list assumes that all
entries for a given heap tuple appear consecutively in the pending list and
are sorted by the column-number-plus-key-datum. The GIN_LIST_FULLROW page
flag bit tells whether entries for a given heap tuple are spread across
multiple pending-list pages. If GIN_LIST_FULLROW is set, the page contains
all the entries for one or more heap tuples. If GIN_LIST_FULLROW is clear,
the page contains entries for only one heap tuple, *and* they are not all
the entries for that tuple. (Thus, a heap tuple whose entries do not all
fit on one pending-list page must have those pages to itself, even if this
results in wasting much of the space on the preceding page and the last
page for the tuple.)
Limitations
-----------
* No support for multicolumn indices
* Gin doesn't uses scan->kill_prior_tuple & scan->ignore_killed_tuples
* Gin searches entries only by equality matching. This may be improved in
future.
* Gin doesn't support full scans of indices.
* Gin doesn't index NULL values.
Open Items
----------
We appreciate any comments, help and suggestions.
* Teach optimizer/executor that GIN is intrinsically clustered. i.e., it
always returns ItemPointer in ascending order.
* Tweak gincostestimate.
* Gin doesn't use scan->kill_prior_tuple & scan->ignore_killed_tuples
* Gin searches entries only by equality matching, or simple range
matching using the "partial match" feature.
TODO
----
Nearest future:
* Opclasses for all types (no programming, just many catalog changes).
* Opclasses for more types (no programming, just many catalog changes)
Distant future:
* Replace B-tree of entries to something like GiST
* Add multicolumn support
* Optimize insert operations (background index insertion)
Authors
-------
All work was done by Teodor Sigaev (teodor@sigaev.ru) and Oleg Bartunov
Original work was done by Teodor Sigaev (teodor@sigaev.ru) and Oleg Bartunov
(oleg@sai.msu.su).

View File

@ -14,7 +14,9 @@
#include "postgres.h"
#include "access/gin.h"
#include "access/skey.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@ -23,34 +25,23 @@
#define GinContainedStrategy 3
#define GinEqualStrategy 4
#define ARRAYCHECK(x) do { \
if ( ARR_HASNULL(x) ) \
ereport(ERROR, \
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), \
errmsg("array must not contain null values"))); \
} while(0)
/*
* Function used as extractValue and extractQuery both
* extractValue support function
*/
Datum
ginarrayextract(PG_FUNCTION_ARGS)
{
ArrayType *array;
int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
Datum *entries = NULL;
/* Make copy of array input to ensure it doesn't disappear while in use */
ArrayType *array = PG_GETARG_ARRAYTYPE_P_COPY(0);
int32 *nkeys = (int32 *) PG_GETARG_POINTER(1);
bool **nullFlags = (bool **) PG_GETARG_POINTER(2);
int16 elmlen;
bool elmbyval;
char elmalign;
/*
* we should guarantee that array will not be destroyed during all
* operation
*/
array = PG_GETARG_ARRAYTYPE_P_COPY(0);
ARRAYCHECK(array);
Datum *elems;
bool *nulls;
int nelems;
get_typlenbyvalalign(ARR_ELEMTYPE(array),
&elmlen, &elmbyval, &elmalign);
@ -58,89 +49,140 @@ ginarrayextract(PG_FUNCTION_ARGS)
deconstruct_array(array,
ARR_ELEMTYPE(array),
elmlen, elmbyval, elmalign,
&entries, NULL, (int *) nentries);
&elems, &nulls, &nelems);
if (*nentries == 0 && PG_NARGS() == 3)
{
switch (PG_GETARG_UINT16(2)) /* StrategyNumber */
{
case GinOverlapStrategy:
*nentries = -1; /* nobody can be found */
break;
case GinContainsStrategy:
case GinContainedStrategy:
case GinEqualStrategy:
default: /* require fullscan: GIN can't find void
* arrays */
break;
}
}
*nkeys = nelems;
*nullFlags = nulls;
/* we should not free array, entries[i] points into it */
PG_RETURN_POINTER(entries);
/* we should not free array, elems[i] points into it */
PG_RETURN_POINTER(elems);
}
/*
* extractQuery support function
*/
Datum
ginqueryarrayextract(PG_FUNCTION_ARGS)
{
PG_RETURN_DATUM(DirectFunctionCall3(ginarrayextract,
PG_GETARG_DATUM(0),
PG_GETARG_DATUM(1),
PG_GETARG_DATUM(2)));
/* Make copy of array input to ensure it doesn't disappear while in use */
ArrayType *array = PG_GETARG_ARRAYTYPE_P_COPY(0);
int32 *nkeys = (int32 *) PG_GETARG_POINTER(1);
StrategyNumber strategy = PG_GETARG_UINT16(2);
/* bool **pmatch = (bool **) PG_GETARG_POINTER(3); */
/* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
bool **nullFlags = (bool **) PG_GETARG_POINTER(5);
int32 *searchMode = (int32 *) PG_GETARG_POINTER(6);
int16 elmlen;
bool elmbyval;
char elmalign;
Datum *elems;
bool *nulls;
int nelems;
get_typlenbyvalalign(ARR_ELEMTYPE(array),
&elmlen, &elmbyval, &elmalign);
deconstruct_array(array,
ARR_ELEMTYPE(array),
elmlen, elmbyval, elmalign,
&elems, &nulls, &nelems);
*nkeys = nelems;
*nullFlags = nulls;
switch (strategy)
{
case GinOverlapStrategy:
*searchMode = GIN_SEARCH_MODE_DEFAULT;
break;
case GinContainsStrategy:
if (nelems > 0)
*searchMode = GIN_SEARCH_MODE_DEFAULT;
else /* everything contains the empty set */
*searchMode = GIN_SEARCH_MODE_ALL;
break;
case GinContainedStrategy:
/* empty set is contained in everything */
*searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
break;
case GinEqualStrategy:
if (nelems > 0)
*searchMode = GIN_SEARCH_MODE_DEFAULT;
else
*searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
break;
default:
elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
strategy);
}
/* we should not free array, elems[i] points into it */
PG_RETURN_POINTER(elems);
}
/*
* consistent support function
*/
Datum
ginarrayconsistent(PG_FUNCTION_ARGS)
{
bool *check = (bool *) PG_GETARG_POINTER(0);
StrategyNumber strategy = PG_GETARG_UINT16(1);
ArrayType *query = PG_GETARG_ARRAYTYPE_P(2);
/* int32 nkeys = PG_GETARG_INT32(3); */
/* ArrayType *query = PG_GETARG_ARRAYTYPE_P(2); */
int32 nkeys = PG_GETARG_INT32(3);
/* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
bool *recheck = (bool *) PG_GETARG_POINTER(5);
/* Datum *queryKeys = (Datum *) PG_GETARG_POINTER(6); */
bool *nullFlags = (bool *) PG_GETARG_POINTER(7);
bool res;
int i,
nentries;
/* ARRAYCHECK was already done by previous ginarrayextract call */
int32 i;
switch (strategy)
{
case GinOverlapStrategy:
/* result is not lossy */
*recheck = false;
/* at least one element in check[] is true, so result = true */
res = true;
break;
case GinContainedStrategy:
/* we will need recheck */
*recheck = true;
/* at least one element in check[] is true, so result = true */
res = true;
/* must have a match for at least one non-null element */
res = false;
for (i = 0; i < nkeys; i++)
{
if (check[i] && !nullFlags[i])
{
res = true;
break;
}
}
break;
case GinContainsStrategy:
/* result is not lossy */
*recheck = false;
/* must have all elements in check[] true */
nentries = ArrayGetNItems(ARR_NDIM(query), ARR_DIMS(query));
/* must have all elements in check[] true, and no nulls */
res = true;
for (i = 0; i < nentries; i++)
for (i = 0; i < nkeys; i++)
{
if (!check[i])
if (!check[i] || nullFlags[i])
{
res = false;
break;
}
}
break;
case GinContainedStrategy:
/* we will need recheck */
*recheck = true;
/* can't do anything else useful here */
res = true;
break;
case GinEqualStrategy:
/* we will need recheck */
*recheck = true;
/* must have all elements in check[] true */
nentries = ArrayGetNItems(ARR_NDIM(query), ARR_DIMS(query));
/*
* Must have all elements in check[] true; no discrimination
* against nulls here. This is because array_contain_compare
* and array_eq handle nulls differently ...
*/
res = true;
for (i = 0; i < nentries; i++)
for (i = 0; i < nkeys; i++)
{
if (!check[i])
{

View File

@ -14,7 +14,7 @@
#include "postgres.h"
#include "access/gin.h"
#include "access/gin_private.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "utils/rel.h"
@ -104,7 +104,8 @@ ginFindLeafPage(GinBtree btree, GinBtreeStack *stack)
* ok, page is correctly locked, we should check to move right ..,
* root never has a right link, so small optimization
*/
while (btree->fullScan == FALSE && stack->blkno != rootBlkno && btree->isMoveRight(btree, page))
while (btree->fullScan == FALSE && stack->blkno != rootBlkno &&
btree->isMoveRight(btree, page))
{
BlockNumber rightlink = GinPageGetOpaque(page)->rightlink;
@ -226,7 +227,6 @@ ginFindParents(GinBtree btree, GinBtreeStack *stack,
LockBuffer(root->buffer, GIN_UNLOCK);
Assert(blkno != InvalidBlockNumber);
for (;;)
{
buffer = ReadBuffer(btree->index, blkno);

View File

@ -14,12 +14,12 @@
#include "postgres.h"
#include "access/gin.h"
#include "access/gin_private.h"
#include "utils/datum.h"
#include "utils/memutils.h"
#define DEF_NENTRY 2048 /* EntryAccumulator allocation quantum */
#define DEF_NENTRY 2048 /* GinEntryAccumulator allocation quantum */
#define DEF_NPTR 5 /* ItemPointer initial allocation quantum */
@ -27,48 +27,49 @@
static void
ginCombineData(RBNode *existing, const RBNode *newdata, void *arg)
{
EntryAccumulator *eo = (EntryAccumulator *) existing;
const EntryAccumulator *en = (const EntryAccumulator *) newdata;
GinEntryAccumulator *eo = (GinEntryAccumulator *) existing;
const GinEntryAccumulator *en = (const GinEntryAccumulator *) newdata;
BuildAccumulator *accum = (BuildAccumulator *) arg;
/*
* Note this code assumes that newdata contains only one itempointer.
*/
if (eo->number >= eo->length)
if (eo->count >= eo->maxcount)
{
accum->allocatedMemory -= GetMemoryChunkSpace(eo->list);
eo->length *= 2;
eo->list = (ItemPointerData *) repalloc(eo->list,
sizeof(ItemPointerData) * eo->length);
eo->maxcount *= 2;
eo->list = (ItemPointerData *)
repalloc(eo->list, sizeof(ItemPointerData) * eo->maxcount);
accum->allocatedMemory += GetMemoryChunkSpace(eo->list);
}
/* If item pointers are not ordered, they will need to be sorted. */
/* If item pointers are not ordered, they will need to be sorted later */
if (eo->shouldSort == FALSE)
{
int res;
res = ginCompareItemPointers(eo->list + eo->number - 1, en->list);
res = ginCompareItemPointers(eo->list + eo->count - 1, en->list);
Assert(res != 0);
if (res > 0)
eo->shouldSort = TRUE;
}
eo->list[eo->number] = en->list[0];
eo->number++;
eo->list[eo->count] = en->list[0];
eo->count++;
}
/* Comparator function for rbtree.c */
static int
cmpEntryAccumulator(const RBNode *a, const RBNode *b, void *arg)
{
const EntryAccumulator *ea = (const EntryAccumulator *) a;
const EntryAccumulator *eb = (const EntryAccumulator *) b;
const GinEntryAccumulator *ea = (const GinEntryAccumulator *) a;
const GinEntryAccumulator *eb = (const GinEntryAccumulator *) b;
BuildAccumulator *accum = (BuildAccumulator *) arg;
return ginCompareAttEntries(accum->ginstate, ea->attnum, ea->value,
eb->attnum, eb->value);
return ginCompareAttEntries(accum->ginstate,
ea->attnum, ea->key, ea->category,
eb->attnum, eb->key, eb->category);
}
/* Allocator function for rbtree.c */
@ -76,22 +77,22 @@ static RBNode *
ginAllocEntryAccumulator(void *arg)
{
BuildAccumulator *accum = (BuildAccumulator *) arg;
EntryAccumulator *ea;
GinEntryAccumulator *ea;
/*
* Allocate memory by rather big chunks to decrease overhead. We have
* no need to reclaim RBNodes individually, so this costs nothing.
*/
if (accum->entryallocator == NULL || accum->length >= DEF_NENTRY)
if (accum->entryallocator == NULL || accum->eas_used >= DEF_NENTRY)
{
accum->entryallocator = palloc(sizeof(EntryAccumulator) * DEF_NENTRY);
accum->entryallocator = palloc(sizeof(GinEntryAccumulator) * DEF_NENTRY);
accum->allocatedMemory += GetMemoryChunkSpace(accum->entryallocator);
accum->length = 0;
accum->eas_used = 0;
}
/* Allocate new RBNode from current chunk */
ea = accum->entryallocator + accum->length;
accum->length++;
ea = accum->entryallocator + accum->eas_used;
accum->eas_used++;
return (RBNode *) ea;
}
@ -99,10 +100,11 @@ ginAllocEntryAccumulator(void *arg)
void
ginInitBA(BuildAccumulator *accum)
{
/* accum->ginstate is intentionally not set here */
accum->allocatedMemory = 0;
accum->length = 0;
accum->entryallocator = NULL;
accum->tree = rb_create(sizeof(EntryAccumulator),
accum->eas_used = 0;
accum->tree = rb_create(sizeof(GinEntryAccumulator),
cmpEntryAccumulator,
ginCombineData,
ginAllocEntryAccumulator,
@ -111,8 +113,8 @@ ginInitBA(BuildAccumulator *accum)
}
/*
* This is basically the same as datumCopy(), but modified to count
* palloc'd space in accum.
* This is basically the same as datumCopy(), but extended to count
* palloc'd space in accum->allocatedMemory.
*/
static Datum
getDatumCopy(BuildAccumulator *accum, OffsetNumber attnum, Datum value)
@ -134,32 +136,37 @@ getDatumCopy(BuildAccumulator *accum, OffsetNumber attnum, Datum value)
* Find/store one entry from indexed value.
*/
static void
ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum, Datum entry)
ginInsertBAEntry(BuildAccumulator *accum,
ItemPointer heapptr, OffsetNumber attnum,
Datum key, GinNullCategory category)
{
EntryAccumulator key;
EntryAccumulator *ea;
GinEntryAccumulator eatmp;
GinEntryAccumulator *ea;
bool isNew;
/*
* For the moment, fill only the fields of key that will be looked at
* For the moment, fill only the fields of eatmp that will be looked at
* by cmpEntryAccumulator or ginCombineData.
*/
key.attnum = attnum;
key.value = entry;
eatmp.attnum = attnum;
eatmp.key = key;
eatmp.category = category;
/* temporarily set up single-entry itempointer list */
key.list = heapptr;
eatmp.list = heapptr;
ea = (EntryAccumulator *) rb_insert(accum->tree, (RBNode *) &key, &isNew);
ea = (GinEntryAccumulator *) rb_insert(accum->tree, (RBNode *) &eatmp,
&isNew);
if (isNew)
{
/*
* Finish initializing new tree entry, including making permanent
* copies of the datum and itempointer.
* copies of the datum (if it's not null) and itempointer.
*/
ea->value = getDatumCopy(accum, attnum, entry);
ea->length = DEF_NPTR;
ea->number = 1;
if (category == GIN_CAT_NORM_KEY)
ea->key = getDatumCopy(accum, attnum, key);
ea->maxcount = DEF_NPTR;
ea->count = 1;
ea->shouldSort = FALSE;
ea->list =
(ItemPointerData *) palloc(sizeof(ItemPointerData) * DEF_NPTR);
@ -175,7 +182,7 @@ ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum
}
/*
* Insert one heap pointer.
* Insert the entries for one heap pointer.
*
* Since the entries are being inserted into a balanced binary tree, you
* might think that the order of insertion wouldn't be critical, but it turns
@ -187,22 +194,24 @@ ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum
* We do this as follows. First, we imagine that we have an array whose size
* is the smallest power of two greater than or equal to the actual array
* size. Second, we insert the middle entry of our virtual array into the
* tree; then, we insert the middles of each half of out virtual array, then
* tree; then, we insert the middles of each half of our virtual array, then
* middles of quarters, etc.
*/
void
ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum,
Datum *entries, int32 nentry)
ginInsertBAEntries(BuildAccumulator *accum,
ItemPointer heapptr, OffsetNumber attnum,
Datum *entries, GinNullCategory *categories,
int32 nentries)
{
uint32 step = nentry;
uint32 step = nentries;
if (nentry <= 0)
if (nentries <= 0)
return;
Assert(ItemPointerIsValid(heapptr) && attnum >= FirstOffsetNumber);
/*
* step will contain largest power of 2 and <= nentry
* step will contain largest power of 2 and <= nentries
*/
step |= (step >> 1);
step |= (step >> 2);
@ -216,8 +225,9 @@ ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber att
{
int i;
for (i = step - 1; i < nentry && i >= 0; i += step << 1 /* *2 */ )
ginInsertEntry(accum, heapptr, attnum, entries[i]);
for (i = step - 1; i < nentries && i >= 0; i += step << 1 /* *2 */ )
ginInsertBAEntry(accum, heapptr, attnum,
entries[i], categories[i]);
step >>= 1; /* /2 */
}
@ -228,37 +238,47 @@ qsortCompareItemPointers(const void *a, const void *b)
{
int res = ginCompareItemPointers((ItemPointer) a, (ItemPointer) b);
/* Assert that there are no equal item pointers being sorted */
Assert(res != 0);
return res;
}
/* Prepare to read out the rbtree contents using ginGetEntry */
/* Prepare to read out the rbtree contents using ginGetBAEntry */
void
ginBeginBAScan(BuildAccumulator *accum)
{
rb_begin_iterate(accum->tree, LeftRightWalk);
}
/*
* Get the next entry in sequence from the BuildAccumulator's rbtree.
* This consists of a single key datum and a list (array) of one or more
* heap TIDs in which that key is found. The list is guaranteed sorted.
*/
ItemPointerData *
ginGetEntry(BuildAccumulator *accum, OffsetNumber *attnum, Datum *value, uint32 *n)
ginGetBAEntry(BuildAccumulator *accum,
OffsetNumber *attnum, Datum *key, GinNullCategory *category,
uint32 *n)
{
EntryAccumulator *entry;
GinEntryAccumulator *entry;
ItemPointerData *list;
entry = (EntryAccumulator *) rb_iterate(accum->tree);
entry = (GinEntryAccumulator *) rb_iterate(accum->tree);
if (entry == NULL)
return NULL;
return NULL; /* no more entries */
*n = entry->number;
*attnum = entry->attnum;
*value = entry->value;
*key = entry->key;
*category = entry->category;
list = entry->list;
*n = entry->count;
Assert(list != NULL);
Assert(list != NULL && entry->count > 0);
if (entry->shouldSort && entry->number > 1)
qsort(list, *n, sizeof(ItemPointerData), qsortCompareItemPointers);
if (entry->shouldSort && entry->count > 1)
qsort(list, entry->count, sizeof(ItemPointerData),
qsortCompareItemPointers);
return list;
}

View File

@ -14,21 +14,27 @@
#include "postgres.h"
#include "access/gin.h"
#include "access/gin_private.h"
#include "storage/bufmgr.h"
#include "utils/rel.h"
int
ginCompareItemPointers(ItemPointer a, ItemPointer b)
{
if (GinItemPointerGetBlockNumber(a) == GinItemPointerGetBlockNumber(b))
BlockNumber ba = GinItemPointerGetBlockNumber(a);
BlockNumber bb = GinItemPointerGetBlockNumber(b);
if (ba == bb)
{
if (GinItemPointerGetOffsetNumber(a) == GinItemPointerGetOffsetNumber(b))
OffsetNumber oa = GinItemPointerGetOffsetNumber(a);
OffsetNumber ob = GinItemPointerGetOffsetNumber(b);
if (oa == ob)
return 0;
return (GinItemPointerGetOffsetNumber(a) > GinItemPointerGetOffsetNumber(b)) ? 1 : -1;
return (oa > ob) ? 1 : -1;
}
return (GinItemPointerGetBlockNumber(a) > GinItemPointerGetBlockNumber(b)) ? 1 : -1;
return (ba > bb) ? 1 : -1;
}
/*
@ -122,12 +128,13 @@ dataLocateItem(GinBtree btree, GinBtreeStack *stack)
pitem = (PostingItem *) GinDataPageGetItem(page, mid);
if (mid == maxoff)
{
/*
* Right infinity, page already correctly chosen with a help of
* dataIsMoveRight
*/
result = -1;
}
else
{
pitem = (PostingItem *) GinDataPageGetItem(page, mid);
@ -220,7 +227,7 @@ dataFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber stor
Assert(!GinPageIsLeaf(page));
Assert(GinPageIsData(page));
/* if page isn't changed, we returns storedOff */
/* if page isn't changed, we return storedOff */
if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
{
pitem = (PostingItem *) GinDataPageGetItem(page, storedOff);
@ -286,9 +293,11 @@ GinDataPageAddItem(Page page, void *data, OffsetNumber offset)
{
ptr = GinDataPageGetItem(page, offset);
if (maxoff + 1 - offset != 0)
memmove(ptr + GinSizeOfItem(page), ptr, (maxoff - offset + 1) * GinSizeOfItem(page));
memmove(ptr + GinSizeOfDataPageItem(page),
ptr,
(maxoff - offset + 1) * GinSizeOfDataPageItem(page));
}
memcpy(ptr, data, GinSizeOfItem(page));
memcpy(ptr, data, GinSizeOfDataPageItem(page));
GinPageGetOpaque(page)->maxoff++;
}
@ -372,10 +381,11 @@ static void
dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prdata)
{
Page page = BufferGetPage(buf);
static XLogRecData rdata[3];
int sizeofitem = GinSizeOfItem(page);
static ginxlogInsert data;
int sizeofitem = GinSizeOfDataPageItem(page);
int cnt = 0;
/* these must be static so they can be returned to caller */
static XLogRecData rdata[3];
static ginxlogInsert data;
*prdata = rdata;
Assert(GinPageIsData(page));
@ -453,20 +463,21 @@ dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prda
static Page
dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRecData **prdata)
{
static ginxlogSplit data;
static XLogRecData rdata[4];
static char vector[2 * BLCKSZ];
char *ptr;
OffsetNumber separator;
ItemPointer bound;
Page lpage = PageGetTempPageCopy(BufferGetPage(lbuf));
ItemPointerData oldbound = *GinDataPageGetRightBound(lpage);
int sizeofitem = GinSizeOfItem(lpage);
int sizeofitem = GinSizeOfDataPageItem(lpage);
OffsetNumber maxoff = GinPageGetOpaque(lpage)->maxoff;
Page rpage = BufferGetPage(rbuf);
Size pageSize = PageGetPageSize(lpage);
Size freeSpace;
uint32 nCopied = 1;
/* these must be static so they can be returned to caller */
static ginxlogSplit data;
static XLogRecData rdata[4];
static char vector[2 * BLCKSZ];
GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
freeSpace = GinDataPageGetFreeSpace(rpage);
@ -482,9 +493,11 @@ dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRe
if (GinPageIsLeaf(lpage) && GinPageRightMost(lpage) && off > GinPageGetOpaque(lpage)->maxoff)
{
nCopied = 0;
while (btree->curitem < btree->nitem && maxoff * sizeof(ItemPointerData) < 2 * (freeSpace - sizeof(ItemPointerData)))
while (btree->curitem < btree->nitem &&
maxoff * sizeof(ItemPointerData) < 2 * (freeSpace - sizeof(ItemPointerData)))
{
memcpy(vector + maxoff * sizeof(ItemPointerData), btree->items + btree->curitem,
memcpy(vector + maxoff * sizeof(ItemPointerData),
btree->items + btree->curitem,
sizeof(ItemPointerData));
maxoff++;
nCopied++;
@ -631,9 +644,9 @@ ginPrepareScanPostingTree(Relation index, BlockNumber rootBlkno, bool searchMode
* Inserts array of item pointers, may execute several tree scan (very rare)
*/
void
ginInsertItemPointer(GinPostingTreeScan *gdi,
ItemPointerData *items, uint32 nitem,
GinStatsData *buildStats)
ginInsertItemPointers(GinPostingTreeScan *gdi,
ItemPointerData *items, uint32 nitem,
GinStatsData *buildStats)
{
BlockNumber rootBlkno = gdi->stack->blkno;

View File

@ -14,7 +14,7 @@
#include "postgres.h"
#include "access/gin.h"
#include "access/gin_private.h"
#include "storage/bufmgr.h"
#include "utils/rel.h"
@ -24,107 +24,116 @@
* If the tuple would be too big to be stored, function throws a suitable
* error if errorTooBig is TRUE, or returns NULL if errorTooBig is FALSE.
*
* On leaf pages, Index tuple has non-traditional layout. Tuple may contain
* posting list or root blocknumber of posting tree.
* Macros: GinIsPostingTree(itup) / GinSetPostingTree(itup, blkno)
* 1) Posting list
* - itup->t_info & INDEX_SIZE_MASK contains total size of tuple as usual
* - ItemPointerGetBlockNumber(&itup->t_tid) contains original
* size of tuple (without posting list).
* Macros: GinGetOrigSizePosting(itup) / GinSetOrigSizePosting(itup,n)
* - ItemPointerGetOffsetNumber(&itup->t_tid) contains number
* of elements in posting list (number of heap itempointers)
* Macros: GinGetNPosting(itup) / GinSetNPosting(itup,n)
* - After standard part of tuple there is a posting list, ie, array
* of heap itempointers
* Macros: GinGetPosting(itup)
* 2) Posting tree
* - itup->t_info & INDEX_SIZE_MASK contains size of tuple as usual
* - ItemPointerGetBlockNumber(&itup->t_tid) contains block number of
* root of posting tree
* - ItemPointerGetOffsetNumber(&itup->t_tid) contains magic number
* GIN_TREE_POSTING, which distinguishes this from posting-list case
*
* Attributes of an index tuple are different for single and multicolumn index.
* For single-column case, index tuple stores only value to be indexed.
* For multicolumn case, it stores two attributes: column number of value
* and value.
* See src/backend/access/gin/README for a description of the index tuple
* format that is being built here. We build on the assumption that we
* are making a leaf-level key entry containing a posting list of nipd items.
* If the caller is actually trying to make a posting-tree entry, non-leaf
* entry, or pending-list entry, it should pass nipd = 0 and then overwrite
* the t_tid fields as necessary. In any case, ipd can be NULL to skip
* copying any itempointers into the posting list; the caller is responsible
* for filling the posting list afterwards, if ipd = NULL and nipd > 0.
*/
IndexTuple
GinFormTuple(Relation index, GinState *ginstate,
OffsetNumber attnum, Datum key,
ItemPointerData *ipd, uint32 nipd, bool errorTooBig)
GinFormTuple(GinState *ginstate,
OffsetNumber attnum, Datum key, GinNullCategory category,
ItemPointerData *ipd, uint32 nipd,
bool errorTooBig)
{
bool isnull[2] = {FALSE, FALSE};
Datum datums[2];
bool isnull[2];
IndexTuple itup;
uint32 newsize;
/* Build the basic tuple: optional column number, plus key datum */
if (ginstate->oneCol)
itup = index_form_tuple(ginstate->origTupdesc, &key, isnull);
{
datums[0] = key;
isnull[0] = (category != GIN_CAT_NORM_KEY);
}
else
{
Datum datums[2];
datums[0] = UInt16GetDatum(attnum);
isnull[0] = false;
datums[1] = key;
itup = index_form_tuple(ginstate->tupdesc[attnum - 1], datums, isnull);
isnull[1] = (category != GIN_CAT_NORM_KEY);
}
GinSetOrigSizePosting(itup, IndexTupleSize(itup));
itup = index_form_tuple(ginstate->tupdesc[attnum - 1], datums, isnull);
if (nipd > 0)
/*
* Determine and store offset to the posting list, making sure there is
* room for the category byte if needed.
*
* Note: because index_form_tuple MAXALIGNs the tuple size, there may well
* be some wasted pad space. Is it worth recomputing the data length to
* prevent that? That would also allow us to Assert that the real data
* doesn't overlap the GinNullCategory byte, which this code currently
* takes on faith.
*/
newsize = IndexTupleSize(itup);
if (IndexTupleHasNulls(itup))
{
newsize = MAXALIGN(SHORTALIGN(IndexTupleSize(itup)) + sizeof(ItemPointerData) * nipd);
if (newsize > Min(INDEX_SIZE_MASK, GinMaxItemSize))
{
if (errorTooBig)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
(unsigned long) newsize,
(unsigned long) Min(INDEX_SIZE_MASK,
GinMaxItemSize),
RelationGetRelationName(index))));
return NULL;
}
uint32 minsize;
Assert(category != GIN_CAT_NORM_KEY);
minsize = GinCategoryOffset(itup, ginstate) + sizeof(GinNullCategory);
newsize = Max(newsize, minsize);
}
newsize = SHORTALIGN(newsize);
GinSetPostingOffset(itup, newsize);
GinSetNPosting(itup, nipd);
/*
* Add space needed for posting list, if any. Then check that the tuple
* won't be too big to store.
*/
newsize += sizeof(ItemPointerData) * nipd;
newsize = MAXALIGN(newsize);
if (newsize > Min(INDEX_SIZE_MASK, GinMaxItemSize))
{
if (errorTooBig)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
(unsigned long) newsize,
(unsigned long) Min(INDEX_SIZE_MASK,
GinMaxItemSize),
RelationGetRelationName(ginstate->index))));
pfree(itup);
return NULL;
}
/*
* Resize tuple if needed
*/
if (newsize != IndexTupleSize(itup))
{
itup = repalloc(itup, newsize);
/* set new size */
/* set new size in tuple header */
itup->t_info &= ~INDEX_SIZE_MASK;
itup->t_info |= newsize;
if (ipd)
memcpy(GinGetPosting(itup), ipd, sizeof(ItemPointerData) * nipd);
GinSetNPosting(itup, nipd);
}
else
/*
* Insert category byte, if needed
*/
if (category != GIN_CAT_NORM_KEY)
{
/*
* Gin tuple without any ItemPointers should be large enough to keep
* one ItemPointer, to prevent inconsistency between
* ginHeapTupleFastCollect and ginEntryInsert called by
* ginHeapTupleInsert. ginHeapTupleFastCollect forms tuple without
* extra pointer to heap, but ginEntryInsert (called for pending list
* cleanup during vacuum) will form the same tuple with one
* ItemPointer.
*/
newsize = MAXALIGN(SHORTALIGN(IndexTupleSize(itup)) + sizeof(ItemPointerData));
if (newsize > Min(INDEX_SIZE_MASK, GinMaxItemSize))
{
if (errorTooBig)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
(unsigned long) newsize,
(unsigned long) Min(INDEX_SIZE_MASK,
GinMaxItemSize),
RelationGetRelationName(index))));
return NULL;
}
GinSetNPosting(itup, 0);
Assert(IndexTupleHasNulls(itup));
GinSetNullCategory(itup, ginstate, category);
}
/*
* Copy in the posting list, if provided
*/
if (ipd)
memcpy(GinGetPosting(itup), ipd, sizeof(ItemPointerData) * nipd);
return itup;
}
@ -140,7 +149,8 @@ GinShortenTuple(IndexTuple itup, uint32 nipd)
Assert(nipd <= GinGetNPosting(itup));
newsize = MAXALIGN(SHORTALIGN(GinGetOrigSizePosting(itup)) + sizeof(ItemPointerData) * nipd);
newsize = GinGetPostingOffset(itup) + sizeof(ItemPointerData) * nipd;
newsize = MAXALIGN(newsize);
Assert(newsize <= (itup->t_info & INDEX_SIZE_MASK));
@ -150,9 +160,46 @@ GinShortenTuple(IndexTuple itup, uint32 nipd)
GinSetNPosting(itup, nipd);
}
/*
* Form a non-leaf entry tuple by copying the key data from the given tuple,
* which can be either a leaf or non-leaf entry tuple.
*
* Any posting list in the source tuple is not copied. The specified child
* block number is inserted into t_tid.
*/
static IndexTuple
GinFormInteriorTuple(IndexTuple itup, Page page, BlockNumber childblk)
{
IndexTuple nitup;
if (GinPageIsLeaf(page) && !GinIsPostingTree(itup))
{
/* Tuple contains a posting list, just copy stuff before that */
uint32 origsize = GinGetPostingOffset(itup);
origsize = MAXALIGN(origsize);
nitup = (IndexTuple) palloc(origsize);
memcpy(nitup, itup, origsize);
/* ... be sure to fix the size header field ... */
nitup->t_info &= ~INDEX_SIZE_MASK;
nitup->t_info |= origsize;
}
else
{
/* Copy the tuple as-is */
nitup = (IndexTuple) palloc(IndexTupleSize(itup));
memcpy(nitup, itup, IndexTupleSize(itup));
}
/* Now insert the correct downlink */
GinSetDownlink(nitup, childblk);
return nitup;
}
/*
* Entry tree is a "static", ie tuple never deletes from it,
* so we don't use right bound, we use rightest key instead.
* so we don't use right bound, we use rightmost key instead.
*/
static IndexTuple
getRightMostTuple(Page page)
@ -166,16 +213,20 @@ static bool
entryIsMoveRight(GinBtree btree, Page page)
{
IndexTuple itup;
OffsetNumber attnum;
Datum key;
GinNullCategory category;
if (GinPageRightMost(page))
return FALSE;
itup = getRightMostTuple(page);
attnum = gintuple_get_attrnum(btree->ginstate, itup);
key = gintuple_get_key(btree->ginstate, itup, &category);
if (ginCompareAttEntries(btree->ginstate,
btree->entryAttnum, btree->entryValue,
gintuple_get_attrnum(btree->ginstate, itup),
gin_index_getattr(btree->ginstate, itup)) > 0)
btree->entryAttnum, btree->entryKey, btree->entryCategory,
attnum, key, category) > 0)
return TRUE;
return FALSE;
@ -183,7 +234,7 @@ entryIsMoveRight(GinBtree btree, Page page)
/*
* Find correct tuple in non-leaf page. It supposed that
* page correctly choosen and searching value SHOULD be on page
* page correctly chosen and searching value SHOULD be on page
*/
static BlockNumber
entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
@ -216,23 +267,31 @@ entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
OffsetNumber mid = low + ((high - low) / 2);
if (mid == maxoff && GinPageRightMost(page))
{
/* Right infinity */
result = -1;
}
else
{
OffsetNumber attnum;
Datum key;
GinNullCategory category;
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
attnum = gintuple_get_attrnum(btree->ginstate, itup);
key = gintuple_get_key(btree->ginstate, itup, &category);
result = ginCompareAttEntries(btree->ginstate,
btree->entryAttnum,
btree->entryValue,
gintuple_get_attrnum(btree->ginstate, itup),
gin_index_getattr(btree->ginstate, itup));
btree->entryKey,
btree->entryCategory,
attnum, key, category);
}
if (result == 0)
{
stack->off = mid;
Assert(GinItemPointerGetBlockNumber(&(itup)->t_tid) != GIN_ROOT_BLKNO);
return GinItemPointerGetBlockNumber(&(itup)->t_tid);
Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
return GinGetDownlink(itup);
}
else if (result > 0)
low = mid + 1;
@ -244,13 +303,13 @@ entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
stack->off = high;
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, high));
Assert(GinItemPointerGetBlockNumber(&(itup)->t_tid) != GIN_ROOT_BLKNO);
return GinItemPointerGetBlockNumber(&(itup)->t_tid);
Assert(GinGetDownlink(itup) != GIN_ROOT_BLKNO);
return GinGetDownlink(itup);
}
/*
* Searches correct position for value on leaf page.
* Page should be corrrectly choosen.
* Page should be correctly chosen.
* Returns true if value found on page.
*/
static bool
@ -259,7 +318,6 @@ entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack)
Page page = BufferGetPage(stack->buffer);
OffsetNumber low,
high;
IndexTuple itup;
Assert(GinPageIsLeaf(page));
Assert(!GinPageIsData(page));
@ -284,14 +342,20 @@ entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack)
while (high > low)
{
OffsetNumber mid = low + ((high - low) / 2);
IndexTuple itup;
OffsetNumber attnum;
Datum key;
GinNullCategory category;
int result;
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, mid));
attnum = gintuple_get_attrnum(btree->ginstate, itup);
key = gintuple_get_key(btree->ginstate, itup, &category);
result = ginCompareAttEntries(btree->ginstate,
btree->entryAttnum,
btree->entryValue,
gintuple_get_attrnum(btree->ginstate, itup),
gin_index_getattr(btree->ginstate, itup));
btree->entryKey,
btree->entryCategory,
attnum, key, category);
if (result == 0)
{
stack->off = mid;
@ -321,7 +385,7 @@ entryFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber sto
if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
{
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, storedOff));
if (GinItemPointerGetBlockNumber(&(itup)->t_tid) == blkno)
if (GinGetDownlink(itup) == blkno)
return storedOff;
/*
@ -331,7 +395,7 @@ entryFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber sto
for (i = storedOff + 1; i <= maxoff; i++)
{
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
if (GinItemPointerGetBlockNumber(&(itup)->t_tid) == blkno)
if (GinGetDownlink(itup) == blkno)
return i;
}
maxoff = storedOff - 1;
@ -341,7 +405,7 @@ entryFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber sto
for (i = FirstOffsetNumber; i <= maxoff; i++)
{
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
if (GinItemPointerGetBlockNumber(&(itup)->t_tid) == blkno)
if (GinGetDownlink(itup) == blkno)
return i;
}
@ -358,7 +422,7 @@ entryGetLeftMostPage(GinBtree btree, Page page)
Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
return GinItemPointerGetBlockNumber(&(itup)->t_tid);
return GinGetDownlink(itup);
}
static bool
@ -406,7 +470,7 @@ entryPreparePage(GinBtree btree, Page page, OffsetNumber off)
{
IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
ItemPointerSet(&itup->t_tid, btree->rightblkno, InvalidOffsetNumber);
GinSetDownlink(itup, btree->rightblkno);
ret = btree->rightblkno;
}
@ -422,10 +486,11 @@ static void
entryPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prdata)
{
Page page = BufferGetPage(buf);
static XLogRecData rdata[3];
OffsetNumber placed;
static ginxlogInsert data;
int cnt = 0;
/* these must be static so they can be returned to caller */
static XLogRecData rdata[3];
static ginxlogInsert data;
*prdata = rdata;
data.updateBlkno = entryPreparePage(btree, page, off);
@ -474,31 +539,6 @@ entryPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prd
btree->entry = NULL;
}
/*
* Returns new tuple with copied value from source tuple.
* New tuple will not store posting list
*/
static IndexTuple
copyIndexTuple(IndexTuple itup, Page page)
{
IndexTuple nitup;
if (GinPageIsLeaf(page) && !GinIsPostingTree(itup))
{
nitup = (IndexTuple) palloc(MAXALIGN(GinGetOrigSizePosting(itup)));
memcpy(nitup, itup, GinGetOrigSizePosting(itup));
nitup->t_info &= ~INDEX_SIZE_MASK;
nitup->t_info |= GinGetOrigSizePosting(itup);
}
else
{
nitup = (IndexTuple) palloc(MAXALIGN(IndexTupleSize(itup)));
memcpy(nitup, itup, IndexTupleSize(itup));
}
return nitup;
}
/*
* Place tuple and split page, original buffer(lbuf) leaves untouched,
* returns shadow page of lbuf filled new data.
@ -508,26 +548,27 @@ copyIndexTuple(IndexTuple itup, Page page)
static Page
entrySplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRecData **prdata)
{
static XLogRecData rdata[2];
OffsetNumber i,
maxoff,
separator = InvalidOffsetNumber;
Size totalsize = 0;
Size lsize = 0,
size;
static char tupstore[2 * BLCKSZ];
char *ptr;
IndexTuple itup,
leftrightmost = NULL;
static ginxlogSplit data;
Page page;
Page lpage = PageGetTempPageCopy(BufferGetPage(lbuf));
Page rpage = BufferGetPage(rbuf);
Size pageSize = PageGetPageSize(lpage);
/* these must be static so they can be returned to caller */
static XLogRecData rdata[2];
static ginxlogSplit data;
static char tupstore[2 * BLCKSZ];
*prdata = rdata;
data.leftChildBlkno = (GinPageIsLeaf(lpage)) ?
InvalidOffsetNumber : GinItemPointerGetBlockNumber(&(btree->entry->t_tid));
InvalidOffsetNumber : GinGetDownlink(btree->entry);
data.updateBlkno = entryPreparePage(btree, lpage, off);
maxoff = PageGetMaxOffsetNumber(lpage);
@ -588,8 +629,8 @@ entrySplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogR
ptr += MAXALIGN(IndexTupleSize(itup));
}
btree->entry = copyIndexTuple(leftrightmost, lpage);
ItemPointerSet(&(btree->entry)->t_tid, BufferGetBlockNumber(lbuf), InvalidOffsetNumber);
btree->entry = GinFormInteriorTuple(leftrightmost, lpage,
BufferGetBlockNumber(lbuf));
btree->rightblkno = BufferGetBlockNumber(rbuf);
@ -627,8 +668,7 @@ ginPageGetLinkItup(Buffer buf)
Page page = BufferGetPage(buf);
itup = getRightMostTuple(page);
nitup = copyIndexTuple(itup, page);
ItemPointerSet(&nitup->t_tid, BufferGetBlockNumber(buf), InvalidOffsetNumber);
nitup = GinFormInteriorTuple(itup, page, BufferGetBlockNumber(buf));
return nitup;
}
@ -656,12 +696,20 @@ ginEntryFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf)
pfree(itup);
}
/*
* Set up GinBtree for entry page access
*
* Note: during WAL recovery, there may be no valid data in ginstate
* other than a faked-up Relation pointer; the key datum is bogus too.
*/
void
ginPrepareEntryScan(GinBtree btree, Relation index, OffsetNumber attnum, Datum value, GinState *ginstate)
ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum,
Datum key, GinNullCategory category,
GinState *ginstate)
{
memset(btree, 0, sizeof(GinBtreeData));
btree->index = index;
btree->index = ginstate->index;
btree->ginstate = ginstate;
btree->findChildPage = entryLocateEntry;
@ -680,6 +728,7 @@ ginPrepareEntryScan(GinBtree btree, Relation index, OffsetNumber attnum, Datum v
btree->isBuild = FALSE;
btree->entryAttnum = attnum;
btree->entryValue = value;
btree->entryKey = key;
btree->entryCategory = category;
btree->isDelete = FALSE;
}

View File

@ -18,8 +18,7 @@
#include "postgres.h"
#include "access/genam.h"
#include "access/gin.h"
#include "access/gin_private.h"
#include "catalog/index.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
@ -30,12 +29,13 @@
#define GIN_PAGE_FREESIZE \
( BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - MAXALIGN(sizeof(GinPageOpaqueData)) )
typedef struct DatumArray
typedef struct KeyArray
{
Datum *values; /* expansible array */
Datum *keys; /* expansible array */
GinNullCategory *categories; /* another expansible array */
int32 nvalues; /* current number of valid entries */
int32 maxvalues; /* allocated size of array */
} DatumArray;
int32 maxvalues; /* allocated size of arrays */
} KeyArray;
/*
@ -88,8 +88,9 @@ writeListPage(Relation index, Buffer buffer,
GinPageGetOpaque(page)->rightlink = rightlink;
/*
* tail page may contain only the whole row(s) or final part of row placed
* on previous pages
* tail page may contain only whole row(s) or final part of row placed
* on previous pages (a "row" here meaning all the index tuples generated
* for one heap tuple)
*/
if (rightlink == InvalidBlockNumber)
{
@ -210,13 +211,16 @@ makeSublist(Relation index, IndexTuple *tuples, int32 ntuples,
}
/*
* Inserts collected values during normal insertion. Function guarantees
* that all values of heap will be stored sequentially, preserving order
* Write the index tuples contained in *collector into the index's
* pending list.
*
* Function guarantees that all these tuples will be inserted consecutively,
* preserving order
*/
void
ginHeapTupleFastInsert(Relation index, GinState *ginstate,
GinTupleCollector *collector)
ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
{
Relation index = ginstate->index;
Buffer metabuffer;
Page metapage;
GinMetaPageData *metadata = NULL;
@ -291,7 +295,12 @@ ginHeapTupleFastInsert(Relation index, GinState *ginstate,
*/
START_CRIT_SECTION();
memcpy(metadata, &sublist, sizeof(GinMetaPageData));
metadata->head = sublist.head;
metadata->tail = sublist.tail;
metadata->tailFreeSize = sublist.tailFreeSize;
metadata->nPendingPages = sublist.nPendingPages;
metadata->nPendingHeapTuples = sublist.nPendingHeapTuples;
}
else
{
@ -421,34 +430,40 @@ ginHeapTupleFastInsert(Relation index, GinState *ginstate,
END_CRIT_SECTION();
if (needCleanup)
ginInsertCleanup(index, ginstate, false, NULL);
ginInsertCleanup(ginstate, false, NULL);
}
/*
* Collect values from one tuples to be indexed. All values for
* one tuples should be written at once - to guarantee consistent state
* Create temporary index tuples for a single indexable item (one index column
* for the heap tuple specified by ht_ctid), and append them to the array
* in *collector. They will subsequently be written out using
* ginHeapTupleFastInsert. Note that to guarantee consistent state, all
* temp tuples for a given heap tuple must be written in one call to
* ginHeapTupleFastInsert.
*/
uint32
ginHeapTupleFastCollect(Relation index, GinState *ginstate,
void
ginHeapTupleFastCollect(GinState *ginstate,
GinTupleCollector *collector,
OffsetNumber attnum, Datum value, ItemPointer item)
OffsetNumber attnum, Datum value, bool isNull,
ItemPointer ht_ctid)
{
Datum *entries;
GinNullCategory *categories;
int32 i,
nentries;
entries = ginExtractEntriesSU(ginstate, attnum, value, &nentries);
if (nentries == 0)
/* nothing to insert */
return 0;
/*
* Extract the key values that need to be inserted in the index
*/
entries = ginExtractEntries(ginstate, attnum, value, isNull,
&nentries, &categories);
/*
* Allocate/reallocate memory for storing collected tuples
*/
if (collector->tuples == NULL)
{
collector->lentuples = nentries * index->rd_att->natts;
collector->lentuples = nentries * ginstate->origTupdesc->natts;
collector->tuples = (IndexTuple *) palloc(sizeof(IndexTuple) * collector->lentuples);
}
@ -460,19 +475,19 @@ ginHeapTupleFastCollect(Relation index, GinState *ginstate,
}
/*
* Creates tuple's array
* Build an index tuple for each key value, and add to array. In
* pending tuples we just stick the heap TID into t_tid.
*/
for (i = 0; i < nentries; i++)
{
collector->tuples[collector->ntuples + i] =
GinFormTuple(index, ginstate, attnum, entries[i], NULL, 0, true);
collector->tuples[collector->ntuples + i]->t_tid = *item;
collector->sumsize += IndexTupleSize(collector->tuples[collector->ntuples + i]);
IndexTuple itup;
itup = GinFormTuple(ginstate, attnum, entries[i], categories[i],
NULL, 0, true);
itup->t_tid = *ht_ctid;
collector->tuples[collector->ntuples++] = itup;
collector->sumsize += IndexTupleSize(itup);
}
collector->ntuples += nentries;
return nentries;
}
/*
@ -591,38 +606,55 @@ shiftList(Relation index, Buffer metabuffer, BlockNumber newHead,
return false;
}
/* Add datum to DatumArray, resizing if needed */
/* Initialize empty KeyArray */
static void
addDatum(DatumArray *datums, Datum datum)
initKeyArray(KeyArray *keys, int32 maxvalues)
{
if (datums->nvalues >= datums->maxvalues)
keys->keys = (Datum *) palloc(sizeof(Datum) * maxvalues);
keys->categories = (GinNullCategory *)
palloc(sizeof(GinNullCategory) * maxvalues);
keys->nvalues = 0;
keys->maxvalues = maxvalues;
}
/* Add datum to KeyArray, resizing if needed */
static void
addDatum(KeyArray *keys, Datum datum, GinNullCategory category)
{
if (keys->nvalues >= keys->maxvalues)
{
datums->maxvalues *= 2;
datums->values = (Datum *) repalloc(datums->values,
sizeof(Datum) * datums->maxvalues);
keys->maxvalues *= 2;
keys->keys = (Datum *)
repalloc(keys->keys, sizeof(Datum) * keys->maxvalues);
keys->categories = (GinNullCategory *)
repalloc(keys->categories, sizeof(GinNullCategory) * keys->maxvalues);
}
datums->values[datums->nvalues++] = datum;
keys->keys[keys->nvalues] = datum;
keys->categories[keys->nvalues] = category;
keys->nvalues++;
}
/*
* Go through all tuples >= startoff on page and collect values in memory
* Collect data from a pending-list page in preparation for insertion into
* the main index.
*
* Note that da is just workspace --- it does not carry any state across
* Go through all tuples >= startoff on page and collect values in accum
*
* Note that ka is just workspace --- it does not carry any state across
* calls.
*/
static void
processPendingPage(BuildAccumulator *accum, DatumArray *da,
processPendingPage(BuildAccumulator *accum, KeyArray *ka,
Page page, OffsetNumber startoff)
{
ItemPointerData heapptr;
OffsetNumber i,
maxoff;
OffsetNumber attrnum,
curattnum;
OffsetNumber attrnum;
/* reset *da to empty */
da->nvalues = 0;
/* reset *ka to empty */
ka->nvalues = 0;
maxoff = PageGetMaxOffsetNumber(page);
Assert(maxoff >= FirstOffsetNumber);
@ -632,7 +664,11 @@ processPendingPage(BuildAccumulator *accum, DatumArray *da,
for (i = startoff; i <= maxoff; i = OffsetNumberNext(i))
{
IndexTuple itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
OffsetNumber curattnum;
Datum curkey;
GinNullCategory curcategory;
/* Check for change of heap TID or attnum */
curattnum = gintuple_get_attrnum(accum->ginstate, itup);
if (!ItemPointerIsValid(&heapptr))
@ -644,18 +680,25 @@ processPendingPage(BuildAccumulator *accum, DatumArray *da,
curattnum == attrnum))
{
/*
* We can insert several datums per call, but only for one heap
* tuple and one column.
* ginInsertBAEntries can insert several datums per call, but only
* for one heap tuple and one column. So call it at a boundary,
* and reset ka.
*/
ginInsertRecordBA(accum, &heapptr, attrnum, da->values, da->nvalues);
da->nvalues = 0;
ginInsertBAEntries(accum, &heapptr, attrnum,
ka->keys, ka->categories, ka->nvalues);
ka->nvalues = 0;
heapptr = itup->t_tid;
attrnum = curattnum;
}
addDatum(da, gin_index_getattr(accum->ginstate, itup));
/* Add key to KeyArray */
curkey = gintuple_get_key(accum->ginstate, itup, &curcategory);
addDatum(ka, curkey, curcategory);
}
ginInsertRecordBA(accum, &heapptr, attrnum, da->values, da->nvalues);
/* Dump out all remaining keys */
ginInsertBAEntries(accum, &heapptr, attrnum,
ka->keys, ka->categories, ka->nvalues);
}
/*
@ -679,9 +722,10 @@ processPendingPage(BuildAccumulator *accum, DatumArray *da,
* If stats isn't null, we count deleted pending pages into the counts.
*/
void
ginInsertCleanup(Relation index, GinState *ginstate,
ginInsertCleanup(GinState *ginstate,
bool vac_delay, IndexBulkDeleteResult *stats)
{
Relation index = ginstate->index;
Buffer metabuffer,
buffer;
Page metapage,
@ -690,7 +734,7 @@ ginInsertCleanup(Relation index, GinState *ginstate,
MemoryContext opCtx,
oldCtx;
BuildAccumulator accum;
DatumArray datums;
KeyArray datums;
BlockNumber blkno;
metabuffer = ReadBuffer(index, GIN_METAPAGE_BLKNO);
@ -726,10 +770,7 @@ ginInsertCleanup(Relation index, GinState *ginstate,
oldCtx = MemoryContextSwitchTo(opCtx);
datums.maxvalues = 128;
datums.nvalues = 0;
datums.values = (Datum *) palloc(sizeof(Datum) * datums.maxvalues);
initKeyArray(&datums, 128);
ginInitBA(&accum);
accum.ginstate = ginstate;
@ -748,7 +789,7 @@ ginInsertCleanup(Relation index, GinState *ginstate,
}
/*
* read page's datums into memory
* read page's datums into accum
*/
processPendingPage(&accum, &datums, page, FirstOffsetNumber);
@ -769,7 +810,8 @@ ginInsertCleanup(Relation index, GinState *ginstate,
{
ItemPointerData *list;
uint32 nlist;
Datum entry;
Datum key;
GinNullCategory category;
OffsetNumber maxoff,
attnum;
@ -787,9 +829,11 @@ ginInsertCleanup(Relation index, GinState *ginstate,
* list.
*/
ginBeginBAScan(&accum);
while ((list = ginGetEntry(&accum, &attnum, &entry, &nlist)) != NULL)
while ((list = ginGetBAEntry(&accum,
&attnum, &key, &category, &nlist)) != NULL)
{
ginEntryInsert(index, ginstate, attnum, entry, list, nlist, NULL);
ginEntryInsert(ginstate, attnum, key, category,
list, nlist, NULL);
if (vac_delay)
vacuum_delay_point();
}
@ -822,8 +866,10 @@ ginInsertCleanup(Relation index, GinState *ginstate,
processPendingPage(&accum, &datums, page, maxoff + 1);
ginBeginBAScan(&accum);
while ((list = ginGetEntry(&accum, &attnum, &entry, &nlist)) != NULL)
ginEntryInsert(index, ginstate, attnum, entry, list, nlist, NULL);
while ((list = ginGetBAEntry(&accum,
&attnum, &key, &category, &nlist)) != NULL)
ginEntryInsert(ginstate, attnum, key, category,
list, nlist, NULL);
}
/*
@ -857,9 +903,8 @@ ginInsertCleanup(Relation index, GinState *ginstate,
* release memory used so far and reinit state
*/
MemoryContextReset(opCtx);
initKeyArray(&datums, datums.maxvalues);
ginInitBA(&accum);
datums.nvalues = 0;
datums.values = (Datum *) palloc(sizeof(Datum) * datums.maxvalues);
}
else
{

File diff suppressed because it is too large Load Diff

View File

@ -14,8 +14,7 @@
#include "postgres.h"
#include "access/genam.h"
#include "access/gin.h"
#include "access/gin_private.h"
#include "catalog/index.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
@ -35,8 +34,10 @@ typedef struct
} GinBuildState;
/*
* Creates posting tree with one page. Function
* suppose that items[] fits to page
* Creates new posting tree with one page, containing the given TIDs.
* Returns the page number (which will be the root of this posting tree).
*
* items[] must be in sorted order with no duplicates.
*/
static BlockNumber
createPostingTree(Relation index, ItemPointerData *items, uint32 nitems)
@ -45,6 +46,9 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems)
Buffer buffer = GinNewBuffer(index);
Page page;
/* Assert that the items[] array will fit on one page */
Assert(nitems <= GinMaxLeafDataItems);
START_CRIT_SECTION();
GinInitBuffer(buffer, GIN_DATA | GIN_LEAF);
@ -76,12 +80,9 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems)
rdata[1].len = sizeof(ItemPointerData) * nitems;
rdata[1].next = NULL;
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_PTREE, rdata);
PageSetLSN(page, recptr);
PageSetTLI(page, ThisTimeLineID);
}
UnlockReleaseBuffer(buffer);
@ -93,28 +94,39 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems)
/*
* Adds array of item pointers to tuple's posting list or
* creates posting tree and tuple pointed to tree in a case
* Adds array of item pointers to tuple's posting list, or
* creates posting tree and tuple pointing to tree in case
* of not enough space. Max size of tuple is defined in
* GinFormTuple().
* GinFormTuple(). Returns a new, modified index tuple.
* items[] must be in sorted order with no duplicates.
*/
static IndexTuple
addItemPointersToTuple(Relation index, GinState *ginstate,
GinBtreeStack *stack, IndexTuple old,
ItemPointerData *items, uint32 nitem,
GinStatsData *buildStats)
addItemPointersToLeafTuple(GinState *ginstate,
IndexTuple old,
ItemPointerData *items, uint32 nitem,
GinStatsData *buildStats)
{
Datum key = gin_index_getattr(ginstate, old);
OffsetNumber attnum = gintuple_get_attrnum(ginstate, old);
IndexTuple res = GinFormTuple(index, ginstate, attnum, key,
NULL, nitem + GinGetNPosting(old),
false);
OffsetNumber attnum;
Datum key;
GinNullCategory category;
IndexTuple res;
Assert(!GinIsPostingTree(old));
attnum = gintuple_get_attrnum(ginstate, old);
key = gintuple_get_key(ginstate, old, &category);
/* try to build tuple with room for all the items */
res = GinFormTuple(ginstate, attnum, key, category,
NULL, nitem + GinGetNPosting(old),
false);
if (res)
{
/* good, small enough */
uint32 newnitem;
/* fill in the posting list with union of old and new TIDs */
newnitem = ginMergeItemPointers(GinGetPosting(res),
GinGetPosting(old),
GinGetNPosting(old),
@ -124,38 +136,115 @@ addItemPointersToTuple(Relation index, GinState *ginstate,
}
else
{
/* posting list would be too big, convert to posting tree */
BlockNumber postingRoot;
GinPostingTreeScan *gdi;
/* posting list becomes big, so we need to make posting's tree */
res = GinFormTuple(index, ginstate, attnum, key, NULL, 0, true);
postingRoot = createPostingTree(index, GinGetPosting(old), GinGetNPosting(old));
GinSetPostingTree(res, postingRoot);
gdi = ginPrepareScanPostingTree(index, postingRoot, FALSE);
gdi->btree.isBuild = (buildStats != NULL);
ginInsertItemPointer(gdi, items, nitem, buildStats);
pfree(gdi);
/*
* Initialize posting tree with the old tuple's posting list. It's
* surely small enough to fit on one posting-tree page, and should
* already be in order with no duplicates.
*/
postingRoot = createPostingTree(ginstate->index,
GinGetPosting(old),
GinGetNPosting(old));
/* During index build, count the newly-added data page */
if (buildStats)
buildStats->nDataPages++;
/* Now insert the TIDs-to-be-added into the posting tree */
gdi = ginPrepareScanPostingTree(ginstate->index, postingRoot, FALSE);
gdi->btree.isBuild = (buildStats != NULL);
ginInsertItemPointers(gdi, items, nitem, buildStats);
pfree(gdi);
/* And build a new posting-tree-only result tuple */
res = GinFormTuple(ginstate, attnum, key, category, NULL, 0, true);
GinSetPostingTree(res, postingRoot);
}
return res;
}
/*
* Inserts only one entry to the index, but it can add more than 1 ItemPointer.
* Build a fresh leaf tuple, either posting-list or posting-tree format
* depending on whether the given items list will fit.
* items[] must be in sorted order with no duplicates.
*
* This is basically the same logic as in addItemPointersToLeafTuple,
* but working from slightly different input.
*/
static IndexTuple
buildFreshLeafTuple(GinState *ginstate,
OffsetNumber attnum, Datum key, GinNullCategory category,
ItemPointerData *items, uint32 nitem,
GinStatsData *buildStats)
{
IndexTuple res;
/* try to build tuple with room for all the items */
res = GinFormTuple(ginstate, attnum, key, category,
items, nitem, false);
if (!res)
{
/* posting list would be too big, build posting tree */
BlockNumber postingRoot;
/*
* Build posting-tree-only result tuple. We do this first so as
* to fail quickly if the key is too big.
*/
res = GinFormTuple(ginstate, attnum, key, category, NULL, 0, true);
/*
* Initialize posting tree with as many TIDs as will fit on the
* first page.
*/
postingRoot = createPostingTree(ginstate->index,
items,
Min(nitem, GinMaxLeafDataItems));
/* During index build, count the newly-added data page */
if (buildStats)
buildStats->nDataPages++;
/* Add any remaining TIDs to the posting tree */
if (nitem > GinMaxLeafDataItems)
{
GinPostingTreeScan *gdi;
gdi = ginPrepareScanPostingTree(ginstate->index, postingRoot, FALSE);
gdi->btree.isBuild = (buildStats != NULL);
ginInsertItemPointers(gdi,
items + GinMaxLeafDataItems,
nitem - GinMaxLeafDataItems,
buildStats);
pfree(gdi);
}
/* And save the root link in the result tuple */
GinSetPostingTree(res, postingRoot);
}
return res;
}
/*
* Insert one or more heap TIDs associated with the given key value.
* This will either add a single key entry, or enlarge a pre-existing entry.
*
* During an index build, buildStats is non-null and the counters
* it contains should be incremented as needed.
*/
void
ginEntryInsert(Relation index, GinState *ginstate,
OffsetNumber attnum, Datum value,
ginEntryInsert(GinState *ginstate,
OffsetNumber attnum, Datum key, GinNullCategory category,
ItemPointerData *items, uint32 nitem,
GinStatsData *buildStats)
{
@ -168,85 +257,82 @@ ginEntryInsert(Relation index, GinState *ginstate,
if (buildStats)
buildStats->nEntries++;
ginPrepareEntryScan(&btree, index, attnum, value, ginstate);
ginPrepareEntryScan(&btree, attnum, key, category, ginstate);
stack = ginFindLeafPage(&btree, NULL);
page = BufferGetPage(stack->buffer);
if (btree.findItem(&btree, stack))
{
/* found entry */
/* found pre-existing entry */
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, stack->off));
if (GinIsPostingTree(itup))
{
/* lock root of posting tree */
GinPostingTreeScan *gdi;
/* add entries to existing posting tree */
BlockNumber rootPostingTree = GinGetPostingTree(itup);
GinPostingTreeScan *gdi;
/* release all stack */
LockBuffer(stack->buffer, GIN_UNLOCK);
freeGinBtreeStack(stack);
/* insert into posting tree */
gdi = ginPrepareScanPostingTree(index, rootPostingTree, FALSE);
gdi = ginPrepareScanPostingTree(ginstate->index, rootPostingTree, FALSE);
gdi->btree.isBuild = (buildStats != NULL);
ginInsertItemPointer(gdi, items, nitem, buildStats);
ginInsertItemPointers(gdi, items, nitem, buildStats);
pfree(gdi);
return;
}
itup = addItemPointersToTuple(index, ginstate, stack, itup,
items, nitem, buildStats);
/* modify an existing leaf entry */
itup = addItemPointersToLeafTuple(ginstate, itup,
items, nitem, buildStats);
btree.isDelete = TRUE;
}
else
{
/* We suppose that tuple can store at least one itempointer */
itup = GinFormTuple(index, ginstate, attnum, value, items, 1, true);
if (nitem > 1)
{
/* Add the rest, making a posting tree if necessary */
IndexTuple previtup = itup;
itup = addItemPointersToTuple(index, ginstate, stack, previtup,
items + 1, nitem - 1, buildStats);
pfree(previtup);
}
/* no match, so construct a new leaf entry */
itup = buildFreshLeafTuple(ginstate, attnum, key, category,
items, nitem, buildStats);
}
/* Insert the new or modified leaf tuple */
btree.entry = itup;
ginInsertValue(&btree, stack, buildStats);
pfree(itup);
}
/*
* Saves indexed value in memory accumulator during index creation
* Function isn't used during normal insert
* Extract index entries for a single indexable item, and add them to the
* BuildAccumulator's state.
*
* This function is used only during initial index creation.
*/
static uint32
ginHeapTupleBulkInsert(GinBuildState *buildstate, OffsetNumber attnum, Datum value, ItemPointer heapptr)
static void
ginHeapTupleBulkInsert(GinBuildState *buildstate, OffsetNumber attnum,
Datum value, bool isNull,
ItemPointer heapptr)
{
Datum *entries;
GinNullCategory *categories;
int32 nentries;
MemoryContext oldCtx;
oldCtx = MemoryContextSwitchTo(buildstate->funcCtx);
entries = ginExtractEntriesSU(buildstate->accum.ginstate, attnum, value, &nentries);
entries = ginExtractEntries(buildstate->accum.ginstate, attnum,
value, isNull,
&nentries, &categories);
MemoryContextSwitchTo(oldCtx);
if (nentries == 0)
/* nothing to insert */
return 0;
ginInsertBAEntries(&buildstate->accum, heapptr, attnum,
entries, categories, nentries);
ginInsertRecordBA(&buildstate->accum, heapptr, attnum, entries, nentries);
buildstate->indtuples += nentries;
MemoryContextReset(buildstate->funcCtx);
return nentries;
}
static void
@ -260,25 +346,26 @@ ginBuildCallback(Relation index, HeapTuple htup, Datum *values,
oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
for (i = 0; i < buildstate->ginstate.origTupdesc->natts; i++)
if (!isnull[i])
buildstate->indtuples += ginHeapTupleBulkInsert(buildstate,
(OffsetNumber) (i + 1), values[i],
&htup->t_self);
ginHeapTupleBulkInsert(buildstate, (OffsetNumber) (i + 1),
values[i], isnull[i],
&htup->t_self);
/* If we've maxed out our available memory, dump everything to the index */
if (buildstate->accum.allocatedMemory >= maintenance_work_mem * 1024L)
{
ItemPointerData *list;
Datum entry;
Datum key;
GinNullCategory category;
uint32 nlist;
OffsetNumber attnum;
ginBeginBAScan(&buildstate->accum);
while ((list = ginGetEntry(&buildstate->accum, &attnum, &entry, &nlist)) != NULL)
while ((list = ginGetBAEntry(&buildstate->accum,
&attnum, &key, &category, &nlist)) != NULL)
{
/* there could be many entries, so be willing to abort here */
CHECK_FOR_INTERRUPTS();
ginEntryInsert(index, &buildstate->ginstate, attnum, entry,
ginEntryInsert(&buildstate->ginstate, attnum, key, category,
list, nlist, &buildstate->buildStats);
}
@ -301,7 +388,8 @@ ginbuild(PG_FUNCTION_ARGS)
Buffer RootBuffer,
MetaBuffer;
ItemPointerData *list;
Datum entry;
Datum key;
GinNullCategory category;
uint32 nlist;
MemoryContext oldCtx;
OffsetNumber attnum;
@ -384,11 +472,12 @@ ginbuild(PG_FUNCTION_ARGS)
/* dump remaining entries to the index */
oldCtx = MemoryContextSwitchTo(buildstate.tmpCtx);
ginBeginBAScan(&buildstate.accum);
while ((list = ginGetEntry(&buildstate.accum, &attnum, &entry, &nlist)) != NULL)
while ((list = ginGetBAEntry(&buildstate.accum,
&attnum, &key, &category, &nlist)) != NULL)
{
/* there could be many entries, so be willing to abort here */
CHECK_FOR_INTERRUPTS();
ginEntryInsert(index, &buildstate.ginstate, attnum, entry,
ginEntryInsert(&buildstate.ginstate, attnum, key, category,
list, nlist, &buildstate.buildStats);
}
MemoryContextSwitchTo(oldCtx);
@ -454,25 +543,25 @@ ginbuildempty(PG_FUNCTION_ARGS)
}
/*
* Inserts value during normal insertion
* Insert index entries for a single indexable item during "normal"
* (non-fast-update) insertion
*/
static uint32
ginHeapTupleInsert(Relation index, GinState *ginstate, OffsetNumber attnum, Datum value, ItemPointer item)
static void
ginHeapTupleInsert(GinState *ginstate, OffsetNumber attnum,
Datum value, bool isNull,
ItemPointer item)
{
Datum *entries;
GinNullCategory *categories;
int32 i,
nentries;
entries = ginExtractEntriesSU(ginstate, attnum, value, &nentries);
if (nentries == 0)
/* nothing to insert */
return 0;
entries = ginExtractEntries(ginstate, attnum, value, isNull,
&nentries, &categories);
for (i = 0; i < nentries; i++)
ginEntryInsert(index, ginstate, attnum, entries[i], item, 1, NULL);
return nentries;
ginEntryInsert(ginstate, attnum, entries[i], categories[i],
item, 1, NULL);
}
Datum
@ -507,20 +596,21 @@ gininsert(PG_FUNCTION_ARGS)
GinTupleCollector collector;
memset(&collector, 0, sizeof(GinTupleCollector));
for (i = 0; i < ginstate.origTupdesc->natts; i++)
if (!isnull[i])
ginHeapTupleFastCollect(index, &ginstate, &collector,
(OffsetNumber) (i + 1), values[i], ht_ctid);
ginHeapTupleFastInsert(index, &ginstate, &collector);
for (i = 0; i < ginstate.origTupdesc->natts; i++)
ginHeapTupleFastCollect(&ginstate, &collector,
(OffsetNumber) (i + 1),
values[i], isnull[i],
ht_ctid);
ginHeapTupleFastInsert(&ginstate, &collector);
}
else
{
for (i = 0; i < ginstate.origTupdesc->natts; i++)
if (!isnull[i])
ginHeapTupleInsert(index, &ginstate,
(OffsetNumber) (i + 1), values[i], ht_ctid);
ginHeapTupleInsert(&ginstate, (OffsetNumber) (i + 1),
values[i], isnull[i],
ht_ctid);
}
MemoryContextSwitchTo(oldCtx);

View File

@ -1,7 +1,7 @@
/*-------------------------------------------------------------------------
*
* ginscan.c
* routines to manage scans inverted index relations
* routines to manage scans of inverted index relations
*
*
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
@ -14,7 +14,7 @@
#include "postgres.h"
#include "access/gin.h"
#include "access/gin_private.h"
#include "access/relscan.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
@ -52,54 +52,117 @@ ginbeginscan(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(scan);
}
/*
* Initialize a GinScanKey using the output from the extractQueryFn
*/
static void
fillScanKey(GinState *ginstate, GinScanKey key, OffsetNumber attnum, Datum query,
Datum *entryValues, bool *partial_matches, uint32 nEntryValues,
StrategyNumber strategy, Pointer *extra_data)
ginFillScanKey(GinState *ginstate, GinScanKey key,
OffsetNumber attnum, Datum query,
Datum *queryValues, GinNullCategory *queryCategories,
bool *partial_matches, uint32 nQueryValues,
StrategyNumber strategy, Pointer *extra_data,
int32 searchMode)
{
uint32 nUserQueryValues = nQueryValues;
uint32 i,
j;
key->nentries = nEntryValues;
key->entryRes = (bool *) palloc0(sizeof(bool) * nEntryValues);
key->scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData) * nEntryValues);
key->strategy = strategy;
key->attnum = attnum;
key->extra_data = extra_data;
/* Non-default search modes add one "hidden" entry to each key */
if (searchMode != GIN_SEARCH_MODE_DEFAULT)
nQueryValues++;
key->nentries = nQueryValues;
key->nuserentries = nUserQueryValues;
key->scanEntry = (GinScanEntry) palloc(sizeof(GinScanEntryData) * nQueryValues);
key->entryRes = (bool *) palloc0(sizeof(bool) * nQueryValues);
key->query = query;
key->queryValues = queryValues;
key->queryCategories = queryCategories;
key->extra_data = extra_data;
key->strategy = strategy;
key->searchMode = searchMode;
key->attnum = attnum;
key->firstCall = TRUE;
ItemPointerSetMin(&key->curItem);
for (i = 0; i < nEntryValues; i++)
for (i = 0; i < nQueryValues; i++)
{
key->scanEntry[i].pval = key->entryRes + i;
key->scanEntry[i].entry = entryValues[i];
key->scanEntry[i].attnum = attnum;
key->scanEntry[i].extra_data = (extra_data) ? extra_data[i] : NULL;
ItemPointerSetMin(&key->scanEntry[i].curItem);
key->scanEntry[i].isFinished = FALSE;
key->scanEntry[i].offset = InvalidOffsetNumber;
key->scanEntry[i].buffer = InvalidBuffer;
key->scanEntry[i].partialMatch = NULL;
key->scanEntry[i].partialMatchIterator = NULL;
key->scanEntry[i].partialMatchResult = NULL;
key->scanEntry[i].strategy = strategy;
key->scanEntry[i].list = NULL;
key->scanEntry[i].nlist = 0;
key->scanEntry[i].isPartialMatch = (ginstate->canPartialMatch[attnum - 1] && partial_matches)
? partial_matches[i] : false;
GinScanEntry scanEntry = key->scanEntry + i;
/* link to the equals entry in current scan key */
key->scanEntry[i].master = NULL;
for (j = 0; j < i; j++)
if (ginCompareEntries(ginstate, attnum,
entryValues[i], entryValues[j]) == 0 &&
key->scanEntry[i].isPartialMatch == key->scanEntry[j].isPartialMatch &&
key->scanEntry[i].strategy == key->scanEntry[j].strategy)
scanEntry->pval = key->entryRes + i;
if (i < nUserQueryValues)
{
scanEntry->queryKey = queryValues[i];
scanEntry->queryCategory = queryCategories[i];
scanEntry->isPartialMatch =
(ginstate->canPartialMatch[attnum - 1] && partial_matches)
? partial_matches[i] : false;
scanEntry->extra_data = (extra_data) ? extra_data[i] : NULL;
}
else
{
/* set up hidden entry */
scanEntry->queryKey = (Datum) 0;
switch (searchMode)
{
key->scanEntry[i].master = key->scanEntry + j;
break;
case GIN_SEARCH_MODE_INCLUDE_EMPTY:
scanEntry->queryCategory = GIN_CAT_EMPTY_ITEM;
break;
case GIN_SEARCH_MODE_ALL:
scanEntry->queryCategory = GIN_CAT_EMPTY_QUERY;
break;
case GIN_SEARCH_MODE_EVERYTHING:
scanEntry->queryCategory = GIN_CAT_EMPTY_QUERY;
break;
default:
elog(ERROR, "unexpected searchMode: %d", searchMode);
break;
}
scanEntry->isPartialMatch = false;
scanEntry->extra_data = NULL;
}
scanEntry->strategy = strategy;
scanEntry->searchMode = searchMode;
scanEntry->attnum = attnum;
ItemPointerSetMin(&scanEntry->curItem);
scanEntry->isFinished = FALSE;
scanEntry->offset = InvalidOffsetNumber;
scanEntry->buffer = InvalidBuffer;
scanEntry->list = NULL;
scanEntry->nlist = 0;
scanEntry->matchBitmap = NULL;
scanEntry->matchIterator = NULL;
scanEntry->matchResult = NULL;
/*
* Link to any preceding identical entry in current scan key.
*
* Entries with non-null extra_data are never considered identical,
* since we can't know exactly what the opclass might be doing with
* that.
*/
scanEntry->master = NULL;
if (scanEntry->extra_data == NULL)
{
for (j = 0; j < i; j++)
{
GinScanEntry prevEntry = key->scanEntry + j;
if (prevEntry->extra_data == NULL &&
scanEntry->isPartialMatch == prevEntry->isPartialMatch &&
ginCompareEntries(ginstate, attnum,
scanEntry->queryKey,
scanEntry->queryCategory,
prevEntry->queryKey,
prevEntry->queryCategory) == 0)
{
scanEntry->master = prevEntry;
break;
}
}
}
}
}
@ -132,9 +195,9 @@ resetScanKeys(GinScanKey keys, uint32 nkeys)
key->scanEntry[j].buffer = InvalidBuffer;
key->scanEntry[j].list = NULL;
key->scanEntry[j].nlist = 0;
key->scanEntry[j].partialMatch = NULL;
key->scanEntry[j].partialMatchIterator = NULL;
key->scanEntry[j].partialMatchResult = NULL;
key->scanEntry[j].matchBitmap = NULL;
key->scanEntry[j].matchIterator = NULL;
key->scanEntry[j].matchResult = NULL;
}
}
}
@ -159,10 +222,10 @@ freeScanKeys(GinScanKey keys, uint32 nkeys)
ReleaseBuffer(key->scanEntry[j].buffer);
if (key->scanEntry[j].list)
pfree(key->scanEntry[j].list);
if (key->scanEntry[j].partialMatchIterator)
tbm_end_iterate(key->scanEntry[j].partialMatchIterator);
if (key->scanEntry[j].partialMatch)
tbm_free(key->scanEntry[j].partialMatch);
if (key->scanEntry[j].matchIterator)
tbm_end_iterate(key->scanEntry[j].matchIterator);
if (key->scanEntry[j].matchBitmap)
tbm_free(key->scanEntry[j].matchBitmap);
}
pfree(key->entryRes);
@ -179,27 +242,27 @@ ginNewScanKey(IndexScanDesc scan)
GinScanOpaque so = (GinScanOpaque) scan->opaque;
int i;
uint32 nkeys = 0;
bool hasNullQuery = false;
if (scan->numberOfKeys < 1)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("GIN indexes do not support whole-index scans")));
so->keys = (GinScanKey) palloc(scan->numberOfKeys * sizeof(GinScanKeyData));
/* if no scan keys provided, allocate extra EVERYTHING GinScanKey */
so->keys = (GinScanKey)
palloc(Max(scan->numberOfKeys, 1) * sizeof(GinScanKeyData));
so->isVoidRes = false;
for (i = 0; i < scan->numberOfKeys; i++)
{
ScanKey skey = &scankey[i];
Datum *entryValues;
int32 nEntryValues = 0;
Datum *queryValues;
int32 nQueryValues = 0;
bool *partial_matches = NULL;
Pointer *extra_data = NULL;
bool *nullFlags = NULL;
int32 searchMode = GIN_SEARCH_MODE_DEFAULT;
/*
* Assume, that GIN-indexable operators are strict, so nothing could
* be found
* We assume that GIN-indexable operators are strict, so a null
* query argument means an unsatisfiable query.
*/
if (skey->sk_flags & SK_ISNULL)
{
@ -207,46 +270,106 @@ ginNewScanKey(IndexScanDesc scan)
break;
}
entryValues = (Datum *)
DatumGetPointer(FunctionCall5(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
/* OK to call the extractQueryFn */
queryValues = (Datum *)
DatumGetPointer(FunctionCall7(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
skey->sk_argument,
PointerGetDatum(&nEntryValues),
PointerGetDatum(&nQueryValues),
UInt16GetDatum(skey->sk_strategy),
PointerGetDatum(&partial_matches),
PointerGetDatum(&extra_data)));
PointerGetDatum(&extra_data),
PointerGetDatum(&nullFlags),
PointerGetDatum(&searchMode)));
if (nEntryValues < 0)
/*
* If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL;
* note in particular we don't allow extractQueryFn to select
* GIN_SEARCH_MODE_EVERYTHING.
*/
if (searchMode < GIN_SEARCH_MODE_DEFAULT ||
searchMode > GIN_SEARCH_MODE_ALL)
searchMode = GIN_SEARCH_MODE_ALL;
/* Non-default modes require the index to have placeholders */
if (searchMode != GIN_SEARCH_MODE_DEFAULT)
hasNullQuery = true;
/*
* In default mode, no keys means an unsatisfiable query.
*/
if (queryValues == NULL || nQueryValues <= 0)
{
/*
* extractQueryFn signals that nothing can match, so we can just
* set isVoidRes flag. No need to examine any more keys.
*/
so->isVoidRes = true;
break;
if (searchMode == GIN_SEARCH_MODE_DEFAULT)
{
so->isVoidRes = true;
break;
}
nQueryValues = 0; /* ensure sane value */
}
if (entryValues == NULL || nEntryValues == 0)
/*
* If the extractQueryFn didn't create a nullFlags array, create one,
* assuming that everything's non-null. Otherwise, run through the
* array and make sure each value is exactly 0 or 1; this ensures
* binary compatibility with the GinNullCategory representation.
* While at it, detect whether any null keys are present.
*/
if (nullFlags == NULL)
nullFlags = (bool *) palloc0(nQueryValues * sizeof(bool));
else
{
/*
* extractQueryFn signals that everything matches. This would
* require a full scan, which we can't do, but perhaps there is
* another scankey that provides a restriction to use. So we keep
* going and check only at the end.
*/
continue;
}
int32 j;
fillScanKey(&so->ginstate, &(so->keys[nkeys]),
skey->sk_attno, skey->sk_argument,
entryValues, partial_matches, nEntryValues,
skey->sk_strategy, extra_data);
for (j = 0; j < nQueryValues; j++)
{
if (nullFlags[j])
{
nullFlags[j] = true; /* not any other nonzero value */
hasNullQuery = true;
}
}
}
/* now we can use the nullFlags as category codes */
ginFillScanKey(&so->ginstate, &(so->keys[nkeys]),
skey->sk_attno, skey->sk_argument,
queryValues, (GinNullCategory *) nullFlags,
partial_matches, nQueryValues,
skey->sk_strategy, extra_data, searchMode);
nkeys++;
}
/*
* If there are no regular scan keys, generate an EVERYTHING scankey to
* drive a full-index scan.
*/
if (nkeys == 0 && !so->isVoidRes)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("GIN indexes do not support whole-index scans")));
{
hasNullQuery = true;
ginFillScanKey(&so->ginstate, &(so->keys[nkeys]),
FirstOffsetNumber, (Datum) 0,
NULL, NULL, NULL, 0,
InvalidStrategy, NULL, GIN_SEARCH_MODE_EVERYTHING);
nkeys++;
}
/*
* If the index is version 0, it may be missing null and placeholder
* entries, which would render searches for nulls and full-index scans
* unreliable. Throw an error if so.
*/
if (hasNullQuery && !so->isVoidRes)
{
GinStatsData ginStats;
ginGetStats(scan->indexRelation, &ginStats);
if (ginStats.ginVersion < 1)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("old GIN indexes do not support whole-index scans nor searches for nulls"),
errhint("To fix this, do REINDEX INDEX \"%s\".",
RelationGetRelationName(scan->indexRelation))));
}
so->nkeys = nkeys;

View File

@ -14,8 +14,7 @@
#include "postgres.h"
#include "access/genam.h"
#include "access/gin.h"
#include "access/gin_private.h"
#include "access/reloptions.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
@ -24,26 +23,39 @@
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
/*
* initGinState: fill in an empty GinState struct to describe the index
*
* Note: assorted subsidiary data is allocated in the CurrentMemoryContext.
*/
void
initGinState(GinState *state, Relation index)
{
TupleDesc origTupdesc = RelationGetDescr(index);
int i;
state->origTupdesc = index->rd_att;
MemSet(state, 0, sizeof(GinState));
state->oneCol = (index->rd_att->natts == 1) ? true : false;
state->index = index;
state->oneCol = (origTupdesc->natts == 1) ? true : false;
state->origTupdesc = origTupdesc;
for (i = 0; i < index->rd_att->natts; i++)
for (i = 0; i < origTupdesc->natts; i++)
{
state->tupdesc[i] = CreateTemplateTupleDesc(2, false);
if (state->oneCol)
state->tupdesc[i] = state->origTupdesc;
else
{
state->tupdesc[i] = CreateTemplateTupleDesc(2, false);
TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL,
INT2OID, -1, 0);
TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 2, NULL,
index->rd_att->attrs[i]->atttypid,
index->rd_att->attrs[i]->atttypmod,
index->rd_att->attrs[i]->attndims
);
TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL,
INT2OID, -1, 0);
TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 2, NULL,
origTupdesc->attrs[i]->atttypid,
origTupdesc->attrs[i]->atttypmod,
origTupdesc->attrs[i]->attndims);
}
fmgr_info_copy(&(state->compareFn[i]),
index_getprocinfo(index, i + 1, GIN_COMPARE_PROC),
@ -82,9 +94,14 @@ initGinState(GinState *state, Relation index)
OffsetNumber
gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
{
OffsetNumber colN = FirstOffsetNumber;
OffsetNumber colN;
if (!ginstate->oneCol)
if (ginstate->oneCol)
{
/* column number is not stored explicitly */
colN = FirstOffsetNumber;
}
else
{
Datum res;
bool isnull;
@ -105,13 +122,14 @@ gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
}
/*
* Extract stored datum from GIN tuple
* Extract stored datum (and possible null category) from GIN tuple
*/
Datum
gin_index_getattr(GinState *ginstate, IndexTuple tuple)
gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category)
{
bool isnull;
Datum res;
bool isnull;
if (ginstate->oneCol)
{
@ -134,7 +152,10 @@ gin_index_getattr(GinState *ginstate, IndexTuple tuple)
&isnull);
}
Assert(!isnull);
if (isnull)
*category = GinGetNullCategory(tuple, ginstate);
else
*category = GIN_CAT_NORM_KEY;
return res;
}
@ -144,7 +165,6 @@ gin_index_getattr(GinState *ginstate, IndexTuple tuple)
* The returned buffer is already pinned and exclusive-locked
* Caller is responsible for initializing the page by calling GinInitBuffer
*/
Buffer
GinNewBuffer(Relation index)
{
@ -233,96 +253,218 @@ GinInitMetabuffer(Buffer b)
metadata->nEntryPages = 0;
metadata->nDataPages = 0;
metadata->nEntries = 0;
metadata->ginVersion = GIN_CURRENT_VERSION;
}
/*
* Compare two keys of the same index column
*/
int
ginCompareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, Datum b)
ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
Datum a, GinNullCategory categorya,
Datum b, GinNullCategory categoryb)
{
/* if not of same null category, sort by that first */
if (categorya != categoryb)
return (categorya < categoryb) ? -1 : 1;
/* all null items in same category are equal */
if (categorya != GIN_CAT_NORM_KEY)
return 0;
/* both not null, so safe to call the compareFn */
return DatumGetInt32(FunctionCall2(&ginstate->compareFn[attnum - 1],
a, b));
}
/*
* Compare two keys of possibly different index columns
*/
int
ginCompareAttEntries(GinState *ginstate, OffsetNumber attnum_a, Datum a,
OffsetNumber attnum_b, Datum b)
ginCompareAttEntries(GinState *ginstate,
OffsetNumber attnuma, Datum a, GinNullCategory categorya,
OffsetNumber attnumb, Datum b, GinNullCategory categoryb)
{
if (attnum_a == attnum_b)
return ginCompareEntries(ginstate, attnum_a, a, b);
/* attribute number is the first sort key */
if (attnuma != attnumb)
return (attnuma < attnumb) ? -1 : 1;
return (attnum_a < attnum_b) ? -1 : 1;
return ginCompareEntries(ginstate, attnuma, a, categorya, b, categoryb);
}
/*
* Support for sorting key datums in ginExtractEntries
*
* Note: we only have to worry about null and not-null keys here;
* ginExtractEntries never generates more than one placeholder null,
* so it doesn't have to sort those.
*/
typedef struct
{
Datum datum;
bool isnull;
} keyEntryData;
typedef struct
{
FmgrInfo *cmpDatumFunc;
bool *needUnique;
} cmpEntriesData;
bool haveDups;
} cmpEntriesArg;
static int
cmpEntries(const Datum *a, const Datum *b, cmpEntriesData *arg)
cmpEntries(const void *a, const void *b, void *arg)
{
int res = DatumGetInt32(FunctionCall2(arg->cmpDatumFunc,
*a, *b));
const keyEntryData *aa = (const keyEntryData *) a;
const keyEntryData *bb = (const keyEntryData *) b;
cmpEntriesArg *data = (cmpEntriesArg *) arg;
int res;
if (aa->isnull)
{
if (bb->isnull)
res = 0; /* NULL "=" NULL */
else
res = 1; /* NULL ">" not-NULL */
}
else if (bb->isnull)
res = -1; /* not-NULL "<" NULL */
else
res = DatumGetInt32(FunctionCall2(data->cmpDatumFunc,
aa->datum, bb->datum));
/*
* Detect if we have any duplicates. If there are equal keys, qsort
* must compare them at some point, else it wouldn't know whether one
* should go before or after the other.
*/
if (res == 0)
*(arg->needUnique) = TRUE;
data->haveDups = true;
return res;
}
/*
* Extract the index key values from an indexable item
*
* The resulting key values are sorted, and any duplicates are removed.
* This avoids generating redundant index entries.
*/
Datum *
ginExtractEntriesS(GinState *ginstate, OffsetNumber attnum, Datum value, int32 *nentries,
bool *needUnique)
ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
Datum value, bool isNull,
int32 *nentries, GinNullCategory **categories)
{
Datum *entries;
bool *nullFlags;
int32 i;
entries = (Datum *) DatumGetPointer(FunctionCall2(
&ginstate->extractValueFn[attnum - 1],
value,
PointerGetDatum(nentries)
));
if (entries == NULL)
*nentries = 0;
*needUnique = FALSE;
if (*nentries > 1)
/*
* We don't call the extractValueFn on a null item. Instead generate a
* placeholder.
*/
if (isNull)
{
cmpEntriesData arg;
arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
arg.needUnique = needUnique;
qsort_arg(entries, *nentries, sizeof(Datum),
(qsort_arg_comparator) cmpEntries, (void *) &arg);
*nentries = 1;
entries = (Datum *) palloc(sizeof(Datum));
entries[0] = (Datum) 0;
*categories = (GinNullCategory *) palloc(sizeof(GinNullCategory));
(*categories)[0] = GIN_CAT_NULL_ITEM;
return entries;
}
return entries;
}
/* OK, call the opclass's extractValueFn */
nullFlags = NULL; /* in case extractValue doesn't set it */
entries = (Datum *)
DatumGetPointer(FunctionCall3(&ginstate->extractValueFn[attnum - 1],
value,
PointerGetDatum(nentries),
PointerGetDatum(&nullFlags)));
Datum *
ginExtractEntriesSU(GinState *ginstate, OffsetNumber attnum, Datum value, int32 *nentries)
{
bool needUnique;
Datum *entries = ginExtractEntriesS(ginstate, attnum, value, nentries,
&needUnique);
if (needUnique)
/*
* Generate a placeholder if the item contained no keys.
*/
if (entries == NULL || *nentries <= 0)
{
Datum *ptr,
*res;
*nentries = 1;
entries = (Datum *) palloc(sizeof(Datum));
entries[0] = (Datum) 0;
*categories = (GinNullCategory *) palloc(sizeof(GinNullCategory));
(*categories)[0] = GIN_CAT_EMPTY_ITEM;
return entries;
}
ptr = res = entries;
/*
* If the extractValueFn didn't create a nullFlags array, create one,
* assuming that everything's non-null. Otherwise, run through the
* array and make sure each value is exactly 0 or 1; this ensures
* binary compatibility with the GinNullCategory representation.
*/
if (nullFlags == NULL)
nullFlags = (bool *) palloc0(*nentries * sizeof(bool));
else
{
for (i = 0; i < *nentries; i++)
nullFlags[i] = (nullFlags[i] ? true : false);
}
/* now we can use the nullFlags as category codes */
*categories = (GinNullCategory *) nullFlags;
while (ptr - entries < *nentries)
/*
* If there's more than one key, sort and unique-ify.
*
* XXX Using qsort here is notationally painful, and the overhead is
* pretty bad too. For small numbers of keys it'd likely be better to
* use a simple insertion sort.
*/
if (*nentries > 1)
{
keyEntryData *keydata;
cmpEntriesArg arg;
keydata = (keyEntryData *) palloc(*nentries * sizeof(keyEntryData));
for (i = 0; i < *nentries; i++)
{
if (ginCompareEntries(ginstate, attnum, *ptr, *res) != 0)
*(++res) = *ptr++;
else
ptr++;
keydata[i].datum = entries[i];
keydata[i].isnull = nullFlags[i];
}
*nentries = res + 1 - entries;
arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
arg.haveDups = false;
qsort_arg(keydata, *nentries, sizeof(keyEntryData),
cmpEntries, (void *) &arg);
if (arg.haveDups)
{
/* there are duplicates, must get rid of 'em */
int32 j;
entries[0] = keydata[0].datum;
nullFlags[0] = keydata[0].isnull;
j = 1;
for (i = 1; i < *nentries; i++)
{
if (cmpEntries(&keydata[i-1], &keydata[i], &arg) != 0)
{
entries[j] = keydata[i].datum;
nullFlags[j] = keydata[i].isnull;
j++;
}
}
*nentries = j;
}
else
{
/* easy, no duplicates */
for (i = 0; i < *nentries; i++)
{
entries[i] = keydata[i].datum;
nullFlags[i] = keydata[i].isnull;
}
}
pfree(keydata);
}
return entries;
@ -361,7 +503,7 @@ ginoptions(PG_FUNCTION_ARGS)
* Fetch index's statistical data into *stats
*
* Note: in the result, nPendingPages can be trusted to be up-to-date,
* but the other fields are as of the last VACUUM.
* as can ginVersion; but the other fields are as of the last VACUUM.
*/
void
ginGetStats(Relation index, GinStatsData *stats)
@ -380,6 +522,7 @@ ginGetStats(Relation index, GinStatsData *stats)
stats->nEntryPages = metadata->nEntryPages;
stats->nDataPages = metadata->nDataPages;
stats->nEntries = metadata->nEntries;
stats->ginVersion = metadata->ginVersion;
UnlockReleaseBuffer(metabuffer);
}
@ -387,7 +530,7 @@ ginGetStats(Relation index, GinStatsData *stats)
/*
* Write the given statistics to the index's metapage
*
* Note: nPendingPages is *not* copied over
* Note: nPendingPages and ginVersion are *not* copied over
*/
void
ginUpdateStats(Relation index, const GinStatsData *stats)

View File

@ -14,8 +14,7 @@
#include "postgres.h"
#include "access/genam.h"
#include "access/gin.h"
#include "access/gin_private.h"
#include "catalog/storage.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
@ -190,7 +189,6 @@ ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber blkno, bool isRoot,
/* saves changes about deleted tuple ... */
if (oldMaxOff != newMaxOff)
{
START_CRIT_SECTION();
if (newMaxOff > 0)
@ -519,7 +517,7 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
* store posting tree's roots for further processing, we can't
* vacuum it just now due to risk of deadlocks with scans/inserts
*/
roots[*nroot] = GinItemPointerGetBlockNumber(&itup->t_tid);
roots[*nroot] = GinGetDownlink(itup);
(*nroot)++;
}
else if (GinGetNPosting(itup) > 0)
@ -533,8 +531,9 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
if (GinGetNPosting(itup) != newN)
{
Datum value;
OffsetNumber attnum;
Datum key;
GinNullCategory category;
/*
* Some ItemPointers was deleted, so we should remake our
@ -562,9 +561,9 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
}
value = gin_index_getattr(&gvs->ginstate, itup);
attnum = gintuple_get_attrnum(&gvs->ginstate, itup);
itup = GinFormTuple(gvs->index, &gvs->ginstate, attnum, value,
key = gintuple_get_key(&gvs->ginstate, itup, &category);
itup = GinFormTuple(&gvs->ginstate, attnum, key, category,
GinGetPosting(itup), newN, true);
PageIndexTupleDelete(tmppage, i);
@ -606,7 +605,7 @@ ginbulkdelete(PG_FUNCTION_ARGS)
/* Yes, so initialize stats to zeroes */
stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
/* and cleanup any pending inserts */
ginInsertCleanup(index, &gvs.ginstate, true, stats);
ginInsertCleanup(&gvs.ginstate, true, stats);
}
/* we'll re-count the tuples each time */
@ -642,7 +641,7 @@ ginbulkdelete(PG_FUNCTION_ARGS)
Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
blkno = GinGetDownlink(itup);
Assert(blkno != InvalidBlockNumber);
UnlockReleaseBuffer(buffer);
@ -719,7 +718,7 @@ ginvacuumcleanup(PG_FUNCTION_ARGS)
if (IsAutoVacuumWorkerProcess())
{
initGinState(&ginstate, index);
ginInsertCleanup(index, &ginstate, true, stats);
ginInsertCleanup(&ginstate, true, stats);
}
PG_RETURN_POINTER(stats);
}
@ -732,7 +731,7 @@ ginvacuumcleanup(PG_FUNCTION_ARGS)
{
stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
initGinState(&ginstate, index);
ginInsertCleanup(index, &ginstate, true, stats);
ginInsertCleanup(&ginstate, true, stats);
}
memset(&idxStat, 0, sizeof(idxStat));

View File

@ -13,7 +13,7 @@
*/
#include "postgres.h"
#include "access/gin.h"
#include "access/gin_private.h"
#include "access/xlogutils.h"
#include "storage/bufmgr.h"
#include "utils/memutils.h"
@ -152,7 +152,7 @@ ginRedoInsert(XLogRecPtr lsn, XLogRecord *record)
itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsert));
forgetIncompleteSplit(data->node,
GinItemPointerGetBlockNumber(&itup->t_tid),
GinGetDownlink(itup),
data->updateBlkno);
}
}
@ -213,7 +213,7 @@ ginRedoInsert(XLogRecPtr lsn, XLogRecord *record)
Assert(!GinPageIsLeaf(page));
Assert(data->offset >= FirstOffsetNumber && data->offset <= PageGetMaxOffsetNumber(page));
itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, data->offset));
ItemPointerSet(&itup->t_tid, data->updateBlkno, InvalidOffsetNumber);
GinSetDownlink(itup, data->updateBlkno);
}
if (data->isDelete)
@ -270,7 +270,7 @@ ginRedoSplit(XLogRecPtr lsn, XLogRecord *record)
if (data->isData)
{
char *ptr = XLogRecGetData(record) + sizeof(ginxlogSplit);
Size sizeofitem = GinSizeOfItem(lpage);
Size sizeofitem = GinSizeOfDataPageItem(lpage);
OffsetNumber i;
ItemPointer bound;
@ -380,8 +380,9 @@ ginRedoVacuumPage(XLogRecPtr lsn, XLogRecord *record)
{
if (GinPageIsData(page))
{
memcpy(GinDataPageGetData(page), XLogRecGetData(record) + sizeof(ginxlogVacuumPage),
GinSizeOfItem(page) *data->nitem);
memcpy(GinDataPageGetData(page),
XLogRecGetData(record) + sizeof(ginxlogVacuumPage),
data->nitem * GinSizeOfDataPageItem(page));
GinPageGetOpaque(page)->maxoff = data->nitem;
}
else
@ -792,6 +793,7 @@ static void
ginContinueSplit(ginIncompleteSplit *split)
{
GinBtreeData btree;
GinState ginstate;
Relation reln;
Buffer buffer;
GinBtreeStack stack;
@ -813,7 +815,12 @@ ginContinueSplit(ginIncompleteSplit *split)
if (split->rootBlkno == GIN_ROOT_BLKNO)
{
ginPrepareEntryScan(&btree, reln, InvalidOffsetNumber, (Datum) 0, NULL);
MemSet(&ginstate, 0, sizeof(ginstate));
ginstate.index = reln;
ginPrepareEntryScan(&btree,
InvalidOffsetNumber, (Datum) 0, GIN_CAT_NULL_KEY,
&ginstate);
btree.entry = ginPageGetLinkItup(buffer);
}
else

View File

@ -1,6 +1,6 @@
/*--------------------------------------------------------------------------
* gin.h
* header file for postgres inverted index access method implementation.
* Public header file for Generalized Inverted Index access method.
*
* Copyright (c) 2006-2011, PostgreSQL Global Development Group
*
@ -10,11 +10,9 @@
#ifndef GIN_H
#define GIN_H
#include "access/genam.h"
#include "access/itup.h"
#include "access/xlog.h"
#include "utils/rbtree.h"
#include "fmgr.h"
#include "storage/block.h"
#include "utils/relcache.h"
/*
@ -28,349 +26,12 @@
#define GINNProcs 5
/*
* Page opaque data in a inverted index page.
*
* Note: GIN does not include a page ID word as do the other index types.
* This is OK because the opaque data is only 8 bytes and so can be reliably
* distinguished by size. Revisit this if the size ever increases.
* searchMode settings for extractQueryFn.
*/
typedef struct GinPageOpaqueData
{
BlockNumber rightlink; /* next page if any */
OffsetNumber maxoff; /* number entries on GIN_DATA page; number of
* heap ItemPointer on GIN_DATA|GIN_LEAF page
* and number of records on GIN_DATA &
* ~GIN_LEAF page. On GIN_LIST page, number of
* heap tuples. */
uint16 flags; /* see bit definitions below */
} GinPageOpaqueData;
typedef GinPageOpaqueData *GinPageOpaque;
#define GIN_DATA (1 << 0)
#define GIN_LEAF (1 << 1)
#define GIN_DELETED (1 << 2)
#define GIN_META (1 << 3)
#define GIN_LIST (1 << 4)
#define GIN_LIST_FULLROW (1 << 5) /* makes sense only on GIN_LIST page */
/* Page numbers of fixed-location pages */
#define GIN_METAPAGE_BLKNO (0)
#define GIN_ROOT_BLKNO (1)
typedef struct GinMetaPageData
{
/*
* Pointers to head and tail of pending list, which consists of GIN_LIST
* pages. These store fast-inserted entries that haven't yet been moved
* into the regular GIN structure.
*/
BlockNumber head;
BlockNumber tail;
/*
* Free space in bytes in the pending list's tail page.
*/
uint32 tailFreeSize;
/*
* We store both number of pages and number of heap tuples that are in the
* pending list.
*/
BlockNumber nPendingPages;
int64 nPendingHeapTuples;
/*
* Statistics for planner use (accurate as of last VACUUM)
*/
BlockNumber nTotalPages;
BlockNumber nEntryPages;
BlockNumber nDataPages;
int64 nEntries;
} GinMetaPageData;
#define GinPageGetMeta(p) \
((GinMetaPageData *) PageGetContents(p))
/*
* Works on page
*/
#define GinPageGetOpaque(page) ( (GinPageOpaque) PageGetSpecialPointer(page) )
#define GinPageIsLeaf(page) ( GinPageGetOpaque(page)->flags & GIN_LEAF )
#define GinPageSetLeaf(page) ( GinPageGetOpaque(page)->flags |= GIN_LEAF )
#define GinPageSetNonLeaf(page) ( GinPageGetOpaque(page)->flags &= ~GIN_LEAF )
#define GinPageIsData(page) ( GinPageGetOpaque(page)->flags & GIN_DATA )
#define GinPageSetData(page) ( GinPageGetOpaque(page)->flags |= GIN_DATA )
#define GinPageIsList(page) ( GinPageGetOpaque(page)->flags & GIN_LIST )
#define GinPageSetList(page) ( GinPageGetOpaque(page)->flags |= GIN_LIST )
#define GinPageHasFullRow(page) ( GinPageGetOpaque(page)->flags & GIN_LIST_FULLROW )
#define GinPageSetFullRow(page) ( GinPageGetOpaque(page)->flags |= GIN_LIST_FULLROW )
#define GinPageIsDeleted(page) ( GinPageGetOpaque(page)->flags & GIN_DELETED)
#define GinPageSetDeleted(page) ( GinPageGetOpaque(page)->flags |= GIN_DELETED)
#define GinPageSetNonDeleted(page) ( GinPageGetOpaque(page)->flags &= ~GIN_DELETED)
#define GinPageRightMost(page) ( GinPageGetOpaque(page)->rightlink == InvalidBlockNumber)
/*
* We use our own ItemPointerGet(BlockNumber|GetOffsetNumber)
* to avoid Asserts, since sometimes the ip_posid isn't "valid"
*/
#define GinItemPointerGetBlockNumber(pointer) \
BlockIdGetBlockNumber(&(pointer)->ip_blkid)
#define GinItemPointerGetOffsetNumber(pointer) \
((pointer)->ip_posid)
/*
* Special-case item pointer values needed by the GIN search logic.
* MIN: sorts less than any valid item pointer
* MAX: sorts greater than any valid item pointer
* LOSSY PAGE: indicates a whole heap page, sorts after normal item
* pointers for that page
* Note that these are all distinguishable from an "invalid" item pointer
* (which is InvalidBlockNumber/0) as well as from all normal item
* pointers (which have item numbers in the range 1..MaxHeapTuplesPerPage).
*/
#define ItemPointerSetMin(p) \
ItemPointerSet((p), (BlockNumber)0, (OffsetNumber)0)
#define ItemPointerIsMin(p) \
(GinItemPointerGetOffsetNumber(p) == (OffsetNumber)0 && \
GinItemPointerGetBlockNumber(p) == (BlockNumber)0)
#define ItemPointerSetMax(p) \
ItemPointerSet((p), InvalidBlockNumber, (OffsetNumber)0xffff)
#define ItemPointerIsMax(p) \
(GinItemPointerGetOffsetNumber(p) == (OffsetNumber)0xffff && \
GinItemPointerGetBlockNumber(p) == InvalidBlockNumber)
#define ItemPointerSetLossyPage(p, b) \
ItemPointerSet((p), (b), (OffsetNumber)0xffff)
#define ItemPointerIsLossyPage(p) \
(GinItemPointerGetOffsetNumber(p) == (OffsetNumber)0xffff && \
GinItemPointerGetBlockNumber(p) != InvalidBlockNumber)
typedef struct
{
BlockIdData child_blkno; /* use it instead of BlockNumber to save space
* on page */
ItemPointerData key;
} PostingItem;
#define PostingItemGetBlockNumber(pointer) \
BlockIdGetBlockNumber(&(pointer)->child_blkno)
#define PostingItemSetBlockNumber(pointer, blockNumber) \
BlockIdSet(&((pointer)->child_blkno), (blockNumber))
/*
* Support work on IndexTuple on leaf pages
*/
#define GinGetNPosting(itup) GinItemPointerGetOffsetNumber(&(itup)->t_tid)
#define GinSetNPosting(itup,n) ItemPointerSetOffsetNumber(&(itup)->t_tid,(n))
#define GIN_TREE_POSTING ((OffsetNumber)0xffff)
#define GinIsPostingTree(itup) ( GinGetNPosting(itup)==GIN_TREE_POSTING )
#define GinSetPostingTree(itup, blkno) ( GinSetNPosting((itup),GIN_TREE_POSTING ), ItemPointerSetBlockNumber(&(itup)->t_tid, blkno) )
#define GinGetPostingTree(itup) GinItemPointerGetBlockNumber(&(itup)->t_tid)
#define GinGetOrigSizePosting(itup) GinItemPointerGetBlockNumber(&(itup)->t_tid)
#define GinSetOrigSizePosting(itup,n) ItemPointerSetBlockNumber(&(itup)->t_tid,(n))
#define GinGetPosting(itup) ( (ItemPointer)(( ((char*)(itup)) + SHORTALIGN(GinGetOrigSizePosting(itup)) )) )
#define GinMaxItemSize \
MAXALIGN_DOWN(((BLCKSZ - SizeOfPageHeaderData - \
MAXALIGN(sizeof(GinPageOpaqueData))) / 3 - sizeof(ItemIdData)))
/*
* Data (posting tree) pages
*/
#define GinDataPageGetRightBound(page) ((ItemPointer) PageGetContents(page))
#define GinDataPageGetData(page) \
(PageGetContents(page) + MAXALIGN(sizeof(ItemPointerData)))
#define GinSizeOfItem(page) \
(GinPageIsLeaf(page) ? sizeof(ItemPointerData) : sizeof(PostingItem))
#define GinDataPageGetItem(page,i) \
(GinDataPageGetData(page) + ((i)-1) * GinSizeOfItem(page))
#define GinDataPageGetFreeSpace(page) \
(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
- MAXALIGN(sizeof(ItemPointerData)) \
- GinPageGetOpaque(page)->maxoff * GinSizeOfItem(page) \
- MAXALIGN(sizeof(GinPageOpaqueData)))
/*
* List pages
*/
#define GinListPageSize \
( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GinPageOpaqueData)) )
/*
* Storage type for GIN's reloptions
*/
typedef struct GinOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
bool useFastUpdate; /* use fast updates? */
} GinOptions;
#define GIN_DEFAULT_USE_FASTUPDATE true
#define GinGetUseFastUpdate(relation) \
((relation)->rd_options ? \
((GinOptions *) (relation)->rd_options)->useFastUpdate : GIN_DEFAULT_USE_FASTUPDATE)
#define GIN_UNLOCK BUFFER_LOCK_UNLOCK
#define GIN_SHARE BUFFER_LOCK_SHARE
#define GIN_EXCLUSIVE BUFFER_LOCK_EXCLUSIVE
typedef struct GinState
{
FmgrInfo compareFn[INDEX_MAX_KEYS];
FmgrInfo extractValueFn[INDEX_MAX_KEYS];
FmgrInfo extractQueryFn[INDEX_MAX_KEYS];
FmgrInfo consistentFn[INDEX_MAX_KEYS];
FmgrInfo comparePartialFn[INDEX_MAX_KEYS]; /* optional method */
bool canPartialMatch[INDEX_MAX_KEYS]; /* can opclass perform
* partial match (prefix
* search)? */
TupleDesc tupdesc[INDEX_MAX_KEYS];
TupleDesc origTupdesc;
bool oneCol;
} GinState;
/* XLog stuff */
#define XLOG_GIN_CREATE_INDEX 0x00
#define XLOG_GIN_CREATE_PTREE 0x10
typedef struct ginxlogCreatePostingTree
{
RelFileNode node;
BlockNumber blkno;
uint32 nitem;
/* follows list of heap's ItemPointer */
} ginxlogCreatePostingTree;
#define XLOG_GIN_INSERT 0x20
typedef struct ginxlogInsert
{
RelFileNode node;
BlockNumber blkno;
BlockNumber updateBlkno;
OffsetNumber offset;
bool isDelete;
bool isData;
bool isLeaf;
OffsetNumber nitem;
/*
* follows: tuples or ItemPointerData or PostingItem or list of
* ItemPointerData
*/
} ginxlogInsert;
#define XLOG_GIN_SPLIT 0x30
typedef struct ginxlogSplit
{
RelFileNode node;
BlockNumber lblkno;
BlockNumber rootBlkno;
BlockNumber rblkno;
BlockNumber rrlink;
OffsetNumber separator;
OffsetNumber nitem;
bool isData;
bool isLeaf;
bool isRootSplit;
BlockNumber leftChildBlkno;
BlockNumber updateBlkno;
ItemPointerData rightbound; /* used only in posting tree */
/* follows: list of tuple or ItemPointerData or PostingItem */
} ginxlogSplit;
#define XLOG_GIN_VACUUM_PAGE 0x40
typedef struct ginxlogVacuumPage
{
RelFileNode node;
BlockNumber blkno;
OffsetNumber nitem;
/* follows content of page */
} ginxlogVacuumPage;
#define XLOG_GIN_DELETE_PAGE 0x50
typedef struct ginxlogDeletePage
{
RelFileNode node;
BlockNumber blkno;
BlockNumber parentBlkno;
OffsetNumber parentOffset;
BlockNumber leftBlkno;
BlockNumber rightLink;
} ginxlogDeletePage;
#define XLOG_GIN_UPDATE_META_PAGE 0x60
typedef struct ginxlogUpdateMeta
{
RelFileNode node;
GinMetaPageData metadata;
BlockNumber prevTail;
BlockNumber newRightlink;
int32 ntuples; /* if ntuples > 0 then metadata.tail was
* updated with that many tuples; else new sub
* list was inserted */
/* array of inserted tuples follows */
} ginxlogUpdateMeta;
#define XLOG_GIN_INSERT_LISTPAGE 0x70
typedef struct ginxlogInsertListPage
{
RelFileNode node;
BlockNumber blkno;
BlockNumber rightlink;
int32 ntuples;
/* array of inserted tuples follows */
} ginxlogInsertListPage;
#define XLOG_GIN_DELETE_LISTPAGE 0x80
#define GIN_NDELETE_AT_ONCE 16
typedef struct ginxlogDeleteListPages
{
RelFileNode node;
GinMetaPageData metadata;
int32 ndeleted;
BlockNumber toDelete[GIN_NDELETE_AT_ONCE];
} ginxlogDeleteListPages;
/* ginutil.c */
extern Datum ginoptions(PG_FUNCTION_ARGS);
extern void initGinState(GinState *state, Relation index);
extern Buffer GinNewBuffer(Relation index);
extern void GinInitBuffer(Buffer b, uint32 f);
extern void GinInitPage(Page page, uint32 f, Size pageSize);
extern void GinInitMetabuffer(Buffer b);
extern int ginCompareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, Datum b);
extern int ginCompareAttEntries(GinState *ginstate, OffsetNumber attnum_a, Datum a,
OffsetNumber attnum_b, Datum b);
extern Datum *ginExtractEntriesS(GinState *ginstate, OffsetNumber attnum, Datum value,
int32 *nentries, bool *needUnique);
extern Datum *ginExtractEntriesSU(GinState *ginstate, OffsetNumber attnum, Datum value, int32 *nentries);
extern Datum gin_index_getattr(GinState *ginstate, IndexTuple tuple);
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
#define GIN_SEARCH_MODE_DEFAULT 0
#define GIN_SEARCH_MODE_INCLUDE_EMPTY 1
#define GIN_SEARCH_MODE_ALL 2
#define GIN_SEARCH_MODE_EVERYTHING 3 /* for internal use only */
/*
* GinStatsData represents stats data for planner use
@ -382,20 +43,16 @@ typedef struct GinStatsData
BlockNumber nEntryPages;
BlockNumber nDataPages;
int64 nEntries;
int32 ginVersion;
} GinStatsData;
/* GUC parameter */
extern PGDLLIMPORT int GinFuzzySearchLimit;
/* ginutil.c */
extern void ginGetStats(Relation index, GinStatsData *stats);
extern void ginUpdateStats(Relation index, const GinStatsData *stats);
/* gininsert.c */
extern Datum ginbuild(PG_FUNCTION_ARGS);
extern Datum ginbuildempty(PG_FUNCTION_ARGS);
extern Datum gininsert(PG_FUNCTION_ARGS);
extern void ginEntryInsert(Relation index, GinState *ginstate,
OffsetNumber attnum, Datum value,
ItemPointerData *items, uint32 nitem,
GinStatsData *buildStats);
/* ginxlog.c */
extern void gin_redo(XLogRecPtr lsn, XLogRecord *record);
extern void gin_desc(StringInfo buf, uint8 xl_info, char *rec);
@ -403,247 +60,4 @@ extern void gin_xlog_startup(void);
extern void gin_xlog_cleanup(void);
extern bool gin_safe_restartpoint(void);
/* ginbtree.c */
typedef struct GinBtreeStack
{
BlockNumber blkno;
Buffer buffer;
OffsetNumber off;
/* predictNumber contains prediction number of pages on current level */
uint32 predictNumber;
struct GinBtreeStack *parent;
} GinBtreeStack;
typedef struct GinBtreeData *GinBtree;
typedef struct GinBtreeData
{
/* search methods */
BlockNumber (*findChildPage) (GinBtree, GinBtreeStack *);
bool (*isMoveRight) (GinBtree, Page);
bool (*findItem) (GinBtree, GinBtreeStack *);
/* insert methods */
OffsetNumber (*findChildPtr) (GinBtree, Page, BlockNumber, OffsetNumber);
BlockNumber (*getLeftMostPage) (GinBtree, Page);
bool (*isEnoughSpace) (GinBtree, Buffer, OffsetNumber);
void (*placeToPage) (GinBtree, Buffer, OffsetNumber, XLogRecData **);
Page (*splitPage) (GinBtree, Buffer, Buffer, OffsetNumber, XLogRecData **);
void (*fillRoot) (GinBtree, Buffer, Buffer, Buffer);
bool isData;
bool searchMode;
Relation index;
GinState *ginstate;
bool fullScan;
bool isBuild;
BlockNumber rightblkno;
/* Entry options */
OffsetNumber entryAttnum;
Datum entryValue;
IndexTuple entry;
bool isDelete;
/* Data (posting tree) option */
ItemPointerData *items;
uint32 nitem;
uint32 curitem;
PostingItem pitem;
} GinBtreeData;
extern GinBtreeStack *ginPrepareFindLeafPage(GinBtree btree, BlockNumber blkno);
extern GinBtreeStack *ginFindLeafPage(GinBtree btree, GinBtreeStack *stack);
extern void freeGinBtreeStack(GinBtreeStack *stack);
extern void ginInsertValue(GinBtree btree, GinBtreeStack *stack,
GinStatsData *buildStats);
extern void ginFindParents(GinBtree btree, GinBtreeStack *stack, BlockNumber rootBlkno);
/* ginentrypage.c */
extern IndexTuple GinFormTuple(Relation index, GinState *ginstate,
OffsetNumber attnum, Datum key,
ItemPointerData *ipd, uint32 nipd, bool errorTooBig);
extern void GinShortenTuple(IndexTuple itup, uint32 nipd);
extern void ginPrepareEntryScan(GinBtree btree, Relation index, OffsetNumber attnum,
Datum value, GinState *ginstate);
extern void ginEntryFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf);
extern IndexTuple ginPageGetLinkItup(Buffer buf);
/* gindatapage.c */
extern int ginCompareItemPointers(ItemPointer a, ItemPointer b);
extern uint32 ginMergeItemPointers(ItemPointerData *dst,
ItemPointerData *a, uint32 na,
ItemPointerData *b, uint32 nb);
extern void GinDataPageAddItem(Page page, void *data, OffsetNumber offset);
extern void GinPageDeletePostingItem(Page page, OffsetNumber offset);
typedef struct
{
GinBtreeData btree;
GinBtreeStack *stack;
} GinPostingTreeScan;
extern GinPostingTreeScan *ginPrepareScanPostingTree(Relation index,
BlockNumber rootBlkno, bool searchMode);
extern void ginInsertItemPointer(GinPostingTreeScan *gdi,
ItemPointerData *items, uint32 nitem,
GinStatsData *buildStats);
extern Buffer ginScanBeginPostingTree(GinPostingTreeScan *gdi);
extern void ginDataFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf);
extern void ginPrepareDataScan(GinBtree btree, Relation index);
/* ginscan.c */
typedef struct GinScanEntryData *GinScanEntry;
typedef struct GinScanEntryData
{
/* link to the equals entry in current scan key */
GinScanEntry master;
/*
* link to values reported to consistentFn, points to
* GinScanKey->entryRes[i]
*/
bool *pval;
/* entry, got from extractQueryFn */
Datum entry;
OffsetNumber attnum;
Pointer extra_data;
/* Current page in posting tree */
Buffer buffer;
/* current ItemPointer to heap */
ItemPointerData curItem;
/* partial match support */
bool isPartialMatch;
TIDBitmap *partialMatch;
TBMIterator *partialMatchIterator;
TBMIterateResult *partialMatchResult;
StrategyNumber strategy;
/* used for Posting list and one page in Posting tree */
ItemPointerData *list;
uint32 nlist;
OffsetNumber offset;
bool isFinished;
bool reduceResult;
uint32 predictNumberResult;
} GinScanEntryData;
typedef struct GinScanKeyData
{
/* Number of entries in query (got by extractQueryFn) */
uint32 nentries;
/* array of ItemPointer result, reported to consistentFn */
bool *entryRes;
/* array of scans per entry */
GinScanEntry scanEntry;
Pointer *extra_data;
/* for calling consistentFn(GinScanKey->entryRes, strategy, query) */
StrategyNumber strategy;
Datum query;
OffsetNumber attnum;
ItemPointerData curItem;
bool recheckCurItem;
bool firstCall;
bool isFinished;
} GinScanKeyData;
typedef GinScanKeyData *GinScanKey;
typedef struct GinScanOpaqueData
{
MemoryContext tempCtx;
GinState ginstate;
GinScanKey keys;
uint32 nkeys;
bool isVoidRes; /* true if ginstate.extractQueryFn guarantees
* that nothing will be found */
} GinScanOpaqueData;
typedef GinScanOpaqueData *GinScanOpaque;
extern Datum ginbeginscan(PG_FUNCTION_ARGS);
extern Datum ginendscan(PG_FUNCTION_ARGS);
extern Datum ginrescan(PG_FUNCTION_ARGS);
extern Datum ginmarkpos(PG_FUNCTION_ARGS);
extern Datum ginrestrpos(PG_FUNCTION_ARGS);
extern void ginNewScanKey(IndexScanDesc scan);
/* ginget.c */
extern PGDLLIMPORT int GinFuzzySearchLimit;
extern Datum gingetbitmap(PG_FUNCTION_ARGS);
/* ginvacuum.c */
extern Datum ginbulkdelete(PG_FUNCTION_ARGS);
extern Datum ginvacuumcleanup(PG_FUNCTION_ARGS);
/* ginarrayproc.c */
extern Datum ginarrayextract(PG_FUNCTION_ARGS);
extern Datum ginqueryarrayextract(PG_FUNCTION_ARGS);
extern Datum ginarrayconsistent(PG_FUNCTION_ARGS);
/* ginbulk.c */
typedef struct EntryAccumulator
{
RBNode rbnode;
Datum value;
uint32 length;
uint32 number;
OffsetNumber attnum;
bool shouldSort;
ItemPointerData *list;
} EntryAccumulator;
typedef struct
{
GinState *ginstate;
long allocatedMemory;
uint32 length;
EntryAccumulator *entryallocator;
RBTree *tree;
} BuildAccumulator;
extern void ginInitBA(BuildAccumulator *accum);
extern void ginInsertRecordBA(BuildAccumulator *accum,
ItemPointer heapptr,
OffsetNumber attnum, Datum *entries, int32 nentry);
extern void ginBeginBAScan(BuildAccumulator *accum);
extern ItemPointerData *ginGetEntry(BuildAccumulator *accum, OffsetNumber *attnum, Datum *entry, uint32 *n);
/* ginfast.c */
typedef struct GinTupleCollector
{
IndexTuple *tuples;
uint32 ntuples;
uint32 lentuples;
uint32 sumsize;
} GinTupleCollector;
extern void ginHeapTupleFastInsert(Relation index, GinState *ginstate,
GinTupleCollector *collector);
extern uint32 ginHeapTupleFastCollect(Relation index, GinState *ginstate,
GinTupleCollector *collector,
OffsetNumber attnum, Datum value, ItemPointer item);
extern void ginInsertCleanup(Relation index, GinState *ginstate,
bool vac_delay, IndexBulkDeleteResult *stats);
#endif /* GIN_H */

View File

@ -0,0 +1,710 @@
/*--------------------------------------------------------------------------
* gin_private.h
* header file for postgres inverted index access method implementation.
*
* Copyright (c) 2006-2011, PostgreSQL Global Development Group
*
* src/include/access/gin_private.h
*--------------------------------------------------------------------------
*/
#ifndef GIN_PRIVATE_H
#define GIN_PRIVATE_H
#include "access/genam.h"
#include "access/gin.h"
#include "access/itup.h"
#include "fmgr.h"
#include "utils/rbtree.h"
/*
* Page opaque data in a inverted index page.
*
* Note: GIN does not include a page ID word as do the other index types.
* This is OK because the opaque data is only 8 bytes and so can be reliably
* distinguished by size. Revisit this if the size ever increases.
*/
typedef struct GinPageOpaqueData
{
BlockNumber rightlink; /* next page if any */
OffsetNumber maxoff; /* number entries on GIN_DATA page: number of
* heap ItemPointers on GIN_DATA|GIN_LEAF page
* or number of PostingItems on GIN_DATA &
* ~GIN_LEAF page. On GIN_LIST page, number of
* heap tuples. */
uint16 flags; /* see bit definitions below */
} GinPageOpaqueData;
typedef GinPageOpaqueData *GinPageOpaque;
#define GIN_DATA (1 << 0)
#define GIN_LEAF (1 << 1)
#define GIN_DELETED (1 << 2)
#define GIN_META (1 << 3)
#define GIN_LIST (1 << 4)
#define GIN_LIST_FULLROW (1 << 5) /* makes sense only on GIN_LIST page */
/* Page numbers of fixed-location pages */
#define GIN_METAPAGE_BLKNO (0)
#define GIN_ROOT_BLKNO (1)
typedef struct GinMetaPageData
{
/*
* Pointers to head and tail of pending list, which consists of GIN_LIST
* pages. These store fast-inserted entries that haven't yet been moved
* into the regular GIN structure.
*/
BlockNumber head;
BlockNumber tail;
/*
* Free space in bytes in the pending list's tail page.
*/
uint32 tailFreeSize;
/*
* We store both number of pages and number of heap tuples that are in the
* pending list.
*/
BlockNumber nPendingPages;
int64 nPendingHeapTuples;
/*
* Statistics for planner use (accurate as of last VACUUM)
*/
BlockNumber nTotalPages;
BlockNumber nEntryPages;
BlockNumber nDataPages;
int64 nEntries;
/*
* GIN version number (ideally this should have been at the front, but
* too late now. Don't move it!)
*
* Currently 1 (for indexes initialized in 9.1 or later)
*
* Version 0 (indexes initialized in 9.0 or before) is compatible but may
* be missing null entries, including both null keys and placeholders.
* Reject full-index-scan attempts on such indexes.
*/
int32 ginVersion;
} GinMetaPageData;
#define GIN_CURRENT_VERSION 1
#define GinPageGetMeta(p) \
((GinMetaPageData *) PageGetContents(p))
/*
* Macros for accessing a GIN index page's opaque data
*/
#define GinPageGetOpaque(page) ( (GinPageOpaque) PageGetSpecialPointer(page) )
#define GinPageIsLeaf(page) ( GinPageGetOpaque(page)->flags & GIN_LEAF )
#define GinPageSetLeaf(page) ( GinPageGetOpaque(page)->flags |= GIN_LEAF )
#define GinPageSetNonLeaf(page) ( GinPageGetOpaque(page)->flags &= ~GIN_LEAF )
#define GinPageIsData(page) ( GinPageGetOpaque(page)->flags & GIN_DATA )
#define GinPageSetData(page) ( GinPageGetOpaque(page)->flags |= GIN_DATA )
#define GinPageIsList(page) ( GinPageGetOpaque(page)->flags & GIN_LIST )
#define GinPageSetList(page) ( GinPageGetOpaque(page)->flags |= GIN_LIST )
#define GinPageHasFullRow(page) ( GinPageGetOpaque(page)->flags & GIN_LIST_FULLROW )
#define GinPageSetFullRow(page) ( GinPageGetOpaque(page)->flags |= GIN_LIST_FULLROW )
#define GinPageIsDeleted(page) ( GinPageGetOpaque(page)->flags & GIN_DELETED)
#define GinPageSetDeleted(page) ( GinPageGetOpaque(page)->flags |= GIN_DELETED)
#define GinPageSetNonDeleted(page) ( GinPageGetOpaque(page)->flags &= ~GIN_DELETED)
#define GinPageRightMost(page) ( GinPageGetOpaque(page)->rightlink == InvalidBlockNumber)
/*
* We use our own ItemPointerGet(BlockNumber|GetOffsetNumber)
* to avoid Asserts, since sometimes the ip_posid isn't "valid"
*/
#define GinItemPointerGetBlockNumber(pointer) \
BlockIdGetBlockNumber(&(pointer)->ip_blkid)
#define GinItemPointerGetOffsetNumber(pointer) \
((pointer)->ip_posid)
/*
* Special-case item pointer values needed by the GIN search logic.
* MIN: sorts less than any valid item pointer
* MAX: sorts greater than any valid item pointer
* LOSSY PAGE: indicates a whole heap page, sorts after normal item
* pointers for that page
* Note that these are all distinguishable from an "invalid" item pointer
* (which is InvalidBlockNumber/0) as well as from all normal item
* pointers (which have item numbers in the range 1..MaxHeapTuplesPerPage).
*/
#define ItemPointerSetMin(p) \
ItemPointerSet((p), (BlockNumber)0, (OffsetNumber)0)
#define ItemPointerIsMin(p) \
(GinItemPointerGetOffsetNumber(p) == (OffsetNumber)0 && \
GinItemPointerGetBlockNumber(p) == (BlockNumber)0)
#define ItemPointerSetMax(p) \
ItemPointerSet((p), InvalidBlockNumber, (OffsetNumber)0xffff)
#define ItemPointerIsMax(p) \
(GinItemPointerGetOffsetNumber(p) == (OffsetNumber)0xffff && \
GinItemPointerGetBlockNumber(p) == InvalidBlockNumber)
#define ItemPointerSetLossyPage(p, b) \
ItemPointerSet((p), (b), (OffsetNumber)0xffff)
#define ItemPointerIsLossyPage(p) \
(GinItemPointerGetOffsetNumber(p) == (OffsetNumber)0xffff && \
GinItemPointerGetBlockNumber(p) != InvalidBlockNumber)
/*
* Posting item in a non-leaf posting-tree page
*/
typedef struct
{
/* We use BlockIdData not BlockNumber to avoid padding space wastage */
BlockIdData child_blkno;
ItemPointerData key;
} PostingItem;
#define PostingItemGetBlockNumber(pointer) \
BlockIdGetBlockNumber(&(pointer)->child_blkno)
#define PostingItemSetBlockNumber(pointer, blockNumber) \
BlockIdSet(&((pointer)->child_blkno), (blockNumber))
/*
* Category codes to distinguish placeholder nulls from ordinary NULL keys.
* Note that the datatype size and the first two code values are chosen to be
* compatible with the usual usage of bool isNull flags.
*
* GIN_CAT_EMPTY_QUERY is never stored in the index; and notice that it is
* chosen to sort before not after regular key values.
*/
typedef signed char GinNullCategory;
#define GIN_CAT_NORM_KEY 0 /* normal, non-null key value */
#define GIN_CAT_NULL_KEY 1 /* null key value */
#define GIN_CAT_EMPTY_ITEM 2 /* placeholder for zero-key item */
#define GIN_CAT_NULL_ITEM 3 /* placeholder for null item */
#define GIN_CAT_EMPTY_QUERY (-1) /* placeholder for full-scan query */
/*
* Access macros for null category byte in entry tuples
*/
#define GinCategoryOffset(itup,ginstate) \
(IndexInfoFindDataOffset((itup)->t_info) + \
((ginstate)->oneCol ? 0 : sizeof(int2)))
#define GinGetNullCategory(itup,ginstate) \
(*((GinNullCategory *) ((char*)(itup) + GinCategoryOffset(itup,ginstate))))
#define GinSetNullCategory(itup,ginstate,c) \
(*((GinNullCategory *) ((char*)(itup) + GinCategoryOffset(itup,ginstate))) = (c))
/*
* Access macros for leaf-page entry tuples (see discussion in README)
*/
#define GinGetNPosting(itup) GinItemPointerGetOffsetNumber(&(itup)->t_tid)
#define GinSetNPosting(itup,n) ItemPointerSetOffsetNumber(&(itup)->t_tid,n)
#define GIN_TREE_POSTING ((OffsetNumber)0xffff)
#define GinIsPostingTree(itup) (GinGetNPosting(itup) == GIN_TREE_POSTING)
#define GinSetPostingTree(itup, blkno) ( GinSetNPosting((itup),GIN_TREE_POSTING), ItemPointerSetBlockNumber(&(itup)->t_tid, blkno) )
#define GinGetPostingTree(itup) GinItemPointerGetBlockNumber(&(itup)->t_tid)
#define GinGetPostingOffset(itup) GinItemPointerGetBlockNumber(&(itup)->t_tid)
#define GinSetPostingOffset(itup,n) ItemPointerSetBlockNumber(&(itup)->t_tid,n)
#define GinGetPosting(itup) ((ItemPointer) ((char*)(itup) + GinGetPostingOffset(itup)))
#define GinMaxItemSize \
MAXALIGN_DOWN(((BLCKSZ - SizeOfPageHeaderData - \
MAXALIGN(sizeof(GinPageOpaqueData))) / 3 - sizeof(ItemIdData)))
/*
* Access macros for non-leaf entry tuples
*/
#define GinGetDownlink(itup) GinItemPointerGetBlockNumber(&(itup)->t_tid)
#define GinSetDownlink(itup,blkno) ItemPointerSet(&(itup)->t_tid, blkno, InvalidOffsetNumber)
/*
* Data (posting tree) pages
*/
#define GinDataPageGetRightBound(page) ((ItemPointer) PageGetContents(page))
#define GinDataPageGetData(page) \
(PageGetContents(page) + MAXALIGN(sizeof(ItemPointerData)))
#define GinSizeOfDataPageItem(page) \
(GinPageIsLeaf(page) ? sizeof(ItemPointerData) : sizeof(PostingItem))
#define GinDataPageGetItem(page,i) \
(GinDataPageGetData(page) + ((i)-1) * GinSizeOfDataPageItem(page))
#define GinDataPageGetFreeSpace(page) \
(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
- MAXALIGN(sizeof(ItemPointerData)) \
- GinPageGetOpaque(page)->maxoff * GinSizeOfDataPageItem(page) \
- MAXALIGN(sizeof(GinPageOpaqueData)))
#define GinMaxLeafDataItems \
((BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - \
MAXALIGN(sizeof(ItemPointerData)) - \
MAXALIGN(sizeof(GinPageOpaqueData))) \
/ sizeof(ItemPointerData))
/*
* List pages
*/
#define GinListPageSize \
( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GinPageOpaqueData)) )
/*
* Storage type for GIN's reloptions
*/
typedef struct GinOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
bool useFastUpdate; /* use fast updates? */
} GinOptions;
#define GIN_DEFAULT_USE_FASTUPDATE true
#define GinGetUseFastUpdate(relation) \
((relation)->rd_options ? \
((GinOptions *) (relation)->rd_options)->useFastUpdate : GIN_DEFAULT_USE_FASTUPDATE)
/* Macros for buffer lock/unlock operations */
#define GIN_UNLOCK BUFFER_LOCK_UNLOCK
#define GIN_SHARE BUFFER_LOCK_SHARE
#define GIN_EXCLUSIVE BUFFER_LOCK_EXCLUSIVE
/*
* GinState: working data structure describing the index being worked on
*/
typedef struct GinState
{
Relation index;
bool oneCol; /* true if single-column index */
/*
* origTupDesc is the nominal tuple descriptor of the index, ie, the i'th
* attribute shows the key type (not the input data type!) of the i'th
* index column. In a single-column index this describes the actual leaf
* index tuples. In a multi-column index, the actual leaf tuples contain
* a smallint column number followed by a key datum of the appropriate
* type for that column. We set up tupdesc[i] to describe the actual
* rowtype of the index tuples for the i'th column, ie, (int2, keytype).
* Note that in any case, leaf tuples contain more data than is known to
* the TupleDesc; see access/gin/README for details.
*/
TupleDesc origTupdesc;
TupleDesc tupdesc[INDEX_MAX_KEYS];
/*
* Per-index-column opclass support functions
*/
FmgrInfo compareFn[INDEX_MAX_KEYS];
FmgrInfo extractValueFn[INDEX_MAX_KEYS];
FmgrInfo extractQueryFn[INDEX_MAX_KEYS];
FmgrInfo consistentFn[INDEX_MAX_KEYS];
FmgrInfo comparePartialFn[INDEX_MAX_KEYS]; /* optional method */
/* canPartialMatch[i] is true if comparePartialFn[i] is valid */
bool canPartialMatch[INDEX_MAX_KEYS];
} GinState;
/* XLog stuff */
#define XLOG_GIN_CREATE_INDEX 0x00
#define XLOG_GIN_CREATE_PTREE 0x10
typedef struct ginxlogCreatePostingTree
{
RelFileNode node;
BlockNumber blkno;
uint32 nitem;
/* follows list of heap's ItemPointer */
} ginxlogCreatePostingTree;
#define XLOG_GIN_INSERT 0x20
typedef struct ginxlogInsert
{
RelFileNode node;
BlockNumber blkno;
BlockNumber updateBlkno;
OffsetNumber offset;
bool isDelete;
bool isData;
bool isLeaf;
OffsetNumber nitem;
/*
* follows: tuples or ItemPointerData or PostingItem or list of
* ItemPointerData
*/
} ginxlogInsert;
#define XLOG_GIN_SPLIT 0x30
typedef struct ginxlogSplit
{
RelFileNode node;
BlockNumber lblkno;
BlockNumber rootBlkno;
BlockNumber rblkno;
BlockNumber rrlink;
OffsetNumber separator;
OffsetNumber nitem;
bool isData;
bool isLeaf;
bool isRootSplit;
BlockNumber leftChildBlkno;
BlockNumber updateBlkno;
ItemPointerData rightbound; /* used only in posting tree */
/* follows: list of tuple or ItemPointerData or PostingItem */
} ginxlogSplit;
#define XLOG_GIN_VACUUM_PAGE 0x40
typedef struct ginxlogVacuumPage
{
RelFileNode node;
BlockNumber blkno;
OffsetNumber nitem;
/* follows content of page */
} ginxlogVacuumPage;
#define XLOG_GIN_DELETE_PAGE 0x50
typedef struct ginxlogDeletePage
{
RelFileNode node;
BlockNumber blkno;
BlockNumber parentBlkno;
OffsetNumber parentOffset;
BlockNumber leftBlkno;
BlockNumber rightLink;
} ginxlogDeletePage;
#define XLOG_GIN_UPDATE_META_PAGE 0x60
typedef struct ginxlogUpdateMeta
{
RelFileNode node;
GinMetaPageData metadata;
BlockNumber prevTail;
BlockNumber newRightlink;
int32 ntuples; /* if ntuples > 0 then metadata.tail was
* updated with that many tuples; else new sub
* list was inserted */
/* array of inserted tuples follows */
} ginxlogUpdateMeta;
#define XLOG_GIN_INSERT_LISTPAGE 0x70
typedef struct ginxlogInsertListPage
{
RelFileNode node;
BlockNumber blkno;
BlockNumber rightlink;
int32 ntuples;
/* array of inserted tuples follows */
} ginxlogInsertListPage;
#define XLOG_GIN_DELETE_LISTPAGE 0x80
#define GIN_NDELETE_AT_ONCE 16
typedef struct ginxlogDeleteListPages
{
RelFileNode node;
GinMetaPageData metadata;
int32 ndeleted;
BlockNumber toDelete[GIN_NDELETE_AT_ONCE];
} ginxlogDeleteListPages;
/* ginutil.c */
extern Datum ginoptions(PG_FUNCTION_ARGS);
extern void initGinState(GinState *state, Relation index);
extern Buffer GinNewBuffer(Relation index);
extern void GinInitBuffer(Buffer b, uint32 f);
extern void GinInitPage(Page page, uint32 f, Size pageSize);
extern void GinInitMetabuffer(Buffer b);
extern int ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
Datum a, GinNullCategory categorya,
Datum b, GinNullCategory categoryb);
extern int ginCompareAttEntries(GinState *ginstate,
OffsetNumber attnuma, Datum a, GinNullCategory categorya,
OffsetNumber attnumb, Datum b, GinNullCategory categoryb);
extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
Datum value, bool isNull,
int32 *nentries, GinNullCategory **categories);
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
/* gininsert.c */
extern Datum ginbuild(PG_FUNCTION_ARGS);
extern Datum ginbuildempty(PG_FUNCTION_ARGS);
extern Datum gininsert(PG_FUNCTION_ARGS);
extern void ginEntryInsert(GinState *ginstate,
OffsetNumber attnum, Datum key, GinNullCategory category,
ItemPointerData *items, uint32 nitem,
GinStatsData *buildStats);
/* ginbtree.c */
typedef struct GinBtreeStack
{
BlockNumber blkno;
Buffer buffer;
OffsetNumber off;
/* predictNumber contains predicted number of pages on current level */
uint32 predictNumber;
struct GinBtreeStack *parent;
} GinBtreeStack;
typedef struct GinBtreeData *GinBtree;
typedef struct GinBtreeData
{
/* search methods */
BlockNumber (*findChildPage) (GinBtree, GinBtreeStack *);
bool (*isMoveRight) (GinBtree, Page);
bool (*findItem) (GinBtree, GinBtreeStack *);
/* insert methods */
OffsetNumber (*findChildPtr) (GinBtree, Page, BlockNumber, OffsetNumber);
BlockNumber (*getLeftMostPage) (GinBtree, Page);
bool (*isEnoughSpace) (GinBtree, Buffer, OffsetNumber);
void (*placeToPage) (GinBtree, Buffer, OffsetNumber, XLogRecData **);
Page (*splitPage) (GinBtree, Buffer, Buffer, OffsetNumber, XLogRecData **);
void (*fillRoot) (GinBtree, Buffer, Buffer, Buffer);
bool isData;
bool searchMode;
Relation index;
GinState *ginstate; /* not valid in a data scan */
bool fullScan;
bool isBuild;
BlockNumber rightblkno;
/* Entry options */
OffsetNumber entryAttnum;
Datum entryKey;
GinNullCategory entryCategory;
IndexTuple entry;
bool isDelete;
/* Data (posting tree) options */
ItemPointerData *items;
uint32 nitem;
uint32 curitem;
PostingItem pitem;
} GinBtreeData;
extern GinBtreeStack *ginPrepareFindLeafPage(GinBtree btree, BlockNumber blkno);
extern GinBtreeStack *ginFindLeafPage(GinBtree btree, GinBtreeStack *stack);
extern void freeGinBtreeStack(GinBtreeStack *stack);
extern void ginInsertValue(GinBtree btree, GinBtreeStack *stack,
GinStatsData *buildStats);
extern void ginFindParents(GinBtree btree, GinBtreeStack *stack, BlockNumber rootBlkno);
/* ginentrypage.c */
extern IndexTuple GinFormTuple(GinState *ginstate,
OffsetNumber attnum, Datum key, GinNullCategory category,
ItemPointerData *ipd, uint32 nipd, bool errorTooBig);
extern void GinShortenTuple(IndexTuple itup, uint32 nipd);
extern void ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum,
Datum key, GinNullCategory category,
GinState *ginstate);
extern void ginEntryFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf);
extern IndexTuple ginPageGetLinkItup(Buffer buf);
/* gindatapage.c */
extern int ginCompareItemPointers(ItemPointer a, ItemPointer b);
extern uint32 ginMergeItemPointers(ItemPointerData *dst,
ItemPointerData *a, uint32 na,
ItemPointerData *b, uint32 nb);
extern void GinDataPageAddItem(Page page, void *data, OffsetNumber offset);
extern void GinPageDeletePostingItem(Page page, OffsetNumber offset);
typedef struct
{
GinBtreeData btree;
GinBtreeStack *stack;
} GinPostingTreeScan;
extern GinPostingTreeScan *ginPrepareScanPostingTree(Relation index,
BlockNumber rootBlkno, bool searchMode);
extern void ginInsertItemPointers(GinPostingTreeScan *gdi,
ItemPointerData *items, uint32 nitem,
GinStatsData *buildStats);
extern Buffer ginScanBeginPostingTree(GinPostingTreeScan *gdi);
extern void ginDataFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf);
extern void ginPrepareDataScan(GinBtree btree, Relation index);
/* ginscan.c */
/*
* GinScanKeyData describes a single GIN index qualification condition.
* It contains one GinScanEntryData for each key datum extracted from
* the qual by the extractQueryFn or added implicitly by ginFillScanKey.
* nentries is the true number of entries, nuserentries is the number
* that extractQueryFn returned (which is what we report to consistentFn).
* The "user" entries must come first.
*/
typedef struct GinScanKeyData *GinScanKey;
typedef struct GinScanEntryData *GinScanEntry;
typedef struct GinScanKeyData
{
/* Real number of entries in scanEntry[] (always > 0) */
uint32 nentries;
/* Number of entries that extractQueryFn and consistentFn know about */
uint32 nuserentries;
/* array of GinScanEntryData, one per key datum */
GinScanEntry scanEntry;
/* array of ItemPointer result, reported to consistentFn */
bool *entryRes;
/* other data needed for calling consistentFn */
Datum query;
/* NB: these three arrays have only nuserentries elements! */
Datum *queryValues;
GinNullCategory *queryCategories;
Pointer *extra_data;
StrategyNumber strategy;
int32 searchMode;
OffsetNumber attnum;
/* scan status data */
ItemPointerData curItem;
bool recheckCurItem;
bool firstCall;
bool isFinished;
} GinScanKeyData;
typedef struct GinScanEntryData
{
/* link to any preceding identical entry in current scan key */
GinScanEntry master;
/* ptr to value reported to consistentFn, points to parent->entryRes[i] */
bool *pval;
/* query key and other information from extractQueryFn */
Datum queryKey;
GinNullCategory queryCategory;
bool isPartialMatch;
Pointer extra_data;
StrategyNumber strategy;
int32 searchMode;
OffsetNumber attnum;
/* Current page in posting tree */
Buffer buffer;
/* current ItemPointer to heap */
ItemPointerData curItem;
/* for a partial-match or full-scan query, we accumulate all TIDs here */
TIDBitmap *matchBitmap;
TBMIterator *matchIterator;
TBMIterateResult *matchResult;
/* used for Posting list and one page in Posting tree */
ItemPointerData *list;
uint32 nlist;
OffsetNumber offset;
bool isFinished;
bool reduceResult;
uint32 predictNumberResult;
} GinScanEntryData;
typedef struct GinScanOpaqueData
{
MemoryContext tempCtx;
GinState ginstate;
GinScanKey keys;
uint32 nkeys;
bool isVoidRes; /* true if ginstate.extractQueryFn guarantees
* that nothing will be found */
} GinScanOpaqueData;
typedef GinScanOpaqueData *GinScanOpaque;
extern Datum ginbeginscan(PG_FUNCTION_ARGS);
extern Datum ginendscan(PG_FUNCTION_ARGS);
extern Datum ginrescan(PG_FUNCTION_ARGS);
extern Datum ginmarkpos(PG_FUNCTION_ARGS);
extern Datum ginrestrpos(PG_FUNCTION_ARGS);
extern void ginNewScanKey(IndexScanDesc scan);
/* ginget.c */
extern Datum gingetbitmap(PG_FUNCTION_ARGS);
/* ginvacuum.c */
extern Datum ginbulkdelete(PG_FUNCTION_ARGS);
extern Datum ginvacuumcleanup(PG_FUNCTION_ARGS);
/* ginbulk.c */
typedef struct GinEntryAccumulator
{
RBNode rbnode;
Datum key;
GinNullCategory category;
OffsetNumber attnum;
bool shouldSort;
ItemPointerData *list;
uint32 maxcount; /* allocated size of list[] */
uint32 count; /* current number of list[] entries */
} GinEntryAccumulator;
typedef struct
{
GinState *ginstate;
long allocatedMemory;
GinEntryAccumulator *entryallocator;
uint32 eas_used;
RBTree *tree;
} BuildAccumulator;
extern void ginInitBA(BuildAccumulator *accum);
extern void ginInsertBAEntries(BuildAccumulator *accum,
ItemPointer heapptr, OffsetNumber attnum,
Datum *entries, GinNullCategory *categories,
int32 nentries);
extern void ginBeginBAScan(BuildAccumulator *accum);
extern ItemPointerData *ginGetBAEntry(BuildAccumulator *accum,
OffsetNumber *attnum, Datum *key, GinNullCategory *category,
uint32 *n);
/* ginfast.c */
typedef struct GinTupleCollector
{
IndexTuple *tuples;
uint32 ntuples;
uint32 lentuples;
uint32 sumsize;
} GinTupleCollector;
extern void ginHeapTupleFastInsert(GinState *ginstate,
GinTupleCollector *collector);
extern void ginHeapTupleFastCollect(GinState *ginstate,
GinTupleCollector *collector,
OffsetNumber attnum, Datum value, bool isNull,
ItemPointer ht_ctid);
extern void ginInsertCleanup(GinState *ginstate,
bool vac_delay, IndexBulkDeleteResult *stats);
#endif /* GIN_PRIVATE_H */

View File

@ -1047,6 +1047,11 @@ extern Datum window_first_value(PG_FUNCTION_ARGS);
extern Datum window_last_value(PG_FUNCTION_ARGS);
extern Datum window_nth_value(PG_FUNCTION_ARGS);
/* access/gin/ginarrayproc.c */
extern Datum ginarrayextract(PG_FUNCTION_ARGS);
extern Datum ginqueryarrayextract(PG_FUNCTION_ARGS);
extern Datum ginarrayconsistent(PG_FUNCTION_ARGS);
/* access/transam/twophase.c */
extern Datum pg_prepared_xact(PG_FUNCTION_ARGS);

View File

@ -98,3 +98,6 @@
98 {38,34,32,89} {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
99 {37,86} {AAAAAAAAAAAAAAAAAA32918,AAAAA70514,AAAAAAAAA10012,AAAAAAAAAAAAAAAAA59387,AAAAAAAAAA64777,AAAAAAAAAAAAAAAAAAA15356}
100 {85,32,57,39,49,84,32,3,30} {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
101 {} {}
102 {NULL} {NULL}
103 \N \N

View File

@ -499,7 +499,154 @@ SELECT * FROM array_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno;
40 | {34} | {AAAAAAAAAAAAAA10611,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAA31334,AAAAA70466,AAAAAAAA81587,AAAAAAA74623}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
(3 rows)
101 | {} | {}
(4 rows)
SELECT * FROM array_op_test WHERE i = '{}' ORDER BY seqno;
seqno | i | t
-------+----+----
101 | {} | {}
(1 row)
SELECT * FROM array_op_test WHERE i @> '{}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 | {92,75,71,52,64,83} | {AAAAAAAA44066,AAAAAA1059,AAAAAAAAAAA176,AAAAAAA48038}
2 | {3,6} | {AAAAAA98232,AAAAAAAA79710,AAAAAAAAAAAAAAAAA69675,AAAAAAAAAAAAAAAA55798,AAAAAAAAA12793}
3 | {37,64,95,43,3,41,13,30,11,43} | {AAAAAAAAAA48845,AAAAA75968,AAAAA95309,AAA54451,AAAAAAAAAA22292,AAAAAAA99836,A96617,AA17009,AAAAAAAAAAAAAA95246}
4 | {71,39,99,55,33,75,45} | {AAAAAAAAA53663,AAAAAAAAAAAAAAA67062,AAAAAAAAAA64777,AAA99043,AAAAAAAAAAAAAAAAAAA91804,39557}
5 | {50,42,77,50,4} | {AAAAAAAAAAAAAAAAA26540,AAAAAAA79710,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA176,AAAAA95309,AAAAAAAAAAA46154,AAAAAA66777,AAAAAAAAA27249,AAAAAAAAAA64777,AAAAAAAAAAAAAAAAAAA70104}
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
7 | {12,51,88,64,8} | {AAAAAAAAAAAAAAAAAA12591,AAAAAAAAAAAAAAAAA50407,AAAAAAAAAAAA67946}
8 | {60,84} | {AAAAAAA81898,AAAAAA1059,AAAAAAAAAAAA81511,AAAAA961,AAAAAAAAAAAAAAAA31334,AAAAA64741,AA6416,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAAAA50407}
9 | {56,52,35,27,80,44,81,22} | {AAAAAAAAAAAAAAA73034,AAAAAAAAAAAAA7929,AAAAAAA66161,AA88409,39557,A27153,AAAAAAAA9523,AAAAAAAAAAA99000}
10 | {71,5,45} | {AAAAAAAAAAA21658,AAAAAAAAAAAA21089,AAA54451,AAAAAAAAAAAAAAAAAA54141,AAAAAAAAAAAAAA28620,AAAAAAAAAAA21658,AAAAAAAAAAA74076,AAAAAAAAA27249}
11 | {41,86,74,48,22,74,47,50} | {AAAAAAAA9523,AAAAAAAAAAAA37562,AAAAAAAAAAAAAAAA14047,AAAAAAAAAAA46154,AAAA41702,AAAAAAAAAAAAAAAAA764,AAAAA62737,39557}
12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
13 | {3,52,34,23} | {AAAAAA98232,AAAA49534,AAAAAAAAAAA21658}
14 | {78,57,19} | {AAAA8857,AAAAAAAAAAAAAAA73034,AAAAAAAA81587,AAAAAAAAAAAAAAA68526,AAAAA75968,AAAAAAAAAAAAAA65909,AAAAAAAAA10012,AAAAAAAAAAAAAA65909}
15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
16 | {14,63,85,11} | {AAAAAA66777}
17 | {7,10,81,85} | {AAAAAA43678,AAAAAAA12144,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAAAAA15356}
18 | {1} | {AAAAAAAAAAA33576,AAAAA95309,64261,AAA59323,AAAAAAAAAAAAAA95246,55847,AAAAAAAAAAAA67946,AAAAAAAAAAAAAAAAAA64374}
19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
20 | {72,89,70,51,54,37,8,49,79} | {AAAAAA58494}
21 | {2,8,65,10,5,79,43} | {AAAAAAAAAAAAAAAAA88852,AAAAAAAAAAAAAAAAAAA91804,AAAAA64669,AAAAAAAAAAAAAAAA1443,AAAAAAAAAAAAAAAA23657,AAAAA12179,AAAAAAAAAAAAAAAAA88852,AAAAAAAAAAAAAAAA31334,AAAAAAAAAAAAAAAA41303,AAAAAAAAAAAAAAAAAAA85420}
22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
23 | {40,90,5,38,72,40,30,10,43,55} | {A6053,AAAAAAAAAAA6119,AA44673,AAAAAAAAAAAAAAAAA764,AA17009,AAAAA17383,AAAAA70514,AAAAA33250,AAAAA95309,AAAAAAAAAAAA37562}
24 | {94,61,99,35,48} | {AAAAAAAAAAA50956,AAAAAAAAAAA15165,AAAA85070,AAAAAAAAAAAAAAA36627,AAAAA961,AAAAAAAAAA55219}
25 | {31,1,10,11,27,79,38} | {AAAAAAAAAAAAAAAAAA59334,45449}
26 | {71,10,9,69,75} | {47735,AAAAAAA21462,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAAAAA91804,AAAAAAAAA72121,AAAAAAAAAAAAAAAAAAA1205,AAAAA41597,AAAA8857,AAAAAAAAAAAAAAAAAAA15356,AA17009}
27 | {94} | {AA6416,A6053,AAAAAAA21462,AAAAAAA57334,AAAAAAAAAAAAAAAAAA12591,AA88409,AAAAAAAAAAAAA70254}
28 | {14,33,6,34,14} | {AAAAAAAAAAAAAAA13198,AAAAAAAA69452,AAAAAAAAAAA82945,AAAAAAA12144,AAAAAAAAA72121,AAAAAAAAAA18601}
29 | {39,21} | {AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAAAAA38885,AAAA85070,AAAAAAAAAAAAAAAAAAA70104,AAAAA66674,AAAAAAAAAAAAA62007,AAAAAAAA69452,AAAAAAA1242,AAAAAAAAAAAAAAAA1729,AAAA35194}
30 | {26,81,47,91,34} | {AAAAAAAAAAAAAAAAAAA70104,AAAAAAA80240}
31 | {80,24,18,21,54} | {AAAAAAAAAAAAAAA13198,AAAAAAAAAAAAAAAAAAA70415,A27153,AAAAAAAAA53663,AAAAAAAAAAAAAAAAA50407,A68938}
32 | {58,79,82,80,67,75,98,10,41} | {AAAAAAAAAAAAAAAAAA61286,AAA54451,AAAAAAAAAAAAAAAAAAA87527,A96617,51533}
33 | {74,73} | {A85417,AAAAAAA56483,AAAAA17383,AAAAAAAAAAAAA62159,AAAAAAAAAAAA52814,AAAAAAAAAAAAA85723,AAAAAAAAAAAAAAAAAA55796}
34 | {70,45} | {AAAAAAAAAAAAAAAAAA71621,AAAAAAAAAAAAAA28620,AAAAAAAAAA55219,AAAAAAAA23648,AAAAAAAAAA22292,AAAAAAA1242}
35 | {23,40} | {AAAAAAAAAAAA52814,AAAA48949,AAAAAAAAA34727,AAAA8857,AAAAAAAAAAAAAAAAAAA62179,AAAAAAAAAAAAAAA68526,AAAAAAA99836,AAAAAAAA50094,AAAA91194,AAAAAAAAAAAAA73084}
36 | {79,82,14,52,30,5,79} | {AAAAAAAAA53663,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA89194,AA88409,AAAAAAAAAAAAAAA81326,AAAAAAAAAAAAAAAAA63050,AAAAAAAAAAAAAAAA33598}
37 | {53,11,81,39,3,78,58,64,74} | {AAAAAAAAAAAAAAAAAAA17075,AAAAAAA66161,AAAAAAAA23648,AAAAAAAAAAAAAA10611}
38 | {59,5,4,95,28} | {AAAAAAAAAAA82945,A96617,47735,AAAAA12179,AAAAA64669,AAAAAA99807,AA74433,AAAAAAAAAAAAAAAAA59387}
39 | {82,43,99,16,74} | {AAAAAAAAAAAAAAA67062,AAAAAAA57334,AAAAAAAAAAAAAA65909,A27153,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAA64777,AAAAAAAAAAAA81511,AAAAAAAAAAAAAA65909,AAAAAAAAAAAAAA28620}
40 | {34} | {AAAAAAAAAAAAAA10611,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAA31334,AAAAA70466,AAAAAAAA81587,AAAAAAA74623}
41 | {19,26,63,12,93,73,27,94} | {AAAAAAA79710,AAAAAAAAAA55219,AAAA41702,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA71621,AAAAAAAAAAAAAAAAA63050,AAAAAAA99836,AAAAAAAAAAAAAA8666}
42 | {15,76,82,75,8,91} | {AAAAAAAAAAA176,AAAAAA38063,45449,AAAAAA54032,AAAAAAA81898,AA6416,AAAAAAAAAAAAAAAAAAA62179,45449,AAAAA60038,AAAAAAAA81587}
43 | {39,87,91,97,79,28} | {AAAAAAAAAAA74076,A96617,AAAAAAAAAAAAAAAAAAA89194,AAAAAAAAAAAAAAAAAA55796,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAA67946}
44 | {40,58,68,29,54} | {AAAAAAA81898,AAAAAA66777,AAAAAA98232}
45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
46 | {53,24} | {AAAAAAAAAAA53908,AAAAAA54032,AAAAA17383,AAAA48949,AAAAAAAAAA18601,AAAAA64669,45449,AAAAAAAAAAA98051,AAAAAAAAAAAAAAAAAA71621}
47 | {98,23,64,12,75,61} | {AAA59323,AAAAA95309,AAAAAAAAAAAAAAAA31334,AAAAAAAAA27249,AAAAA17383,AAAAAAAAAAAA37562,AAAAAA1059,A84822,55847,AAAAA70466}
48 | {76,14} | {AAAAAAAAAAAAA59671,AAAAAAAAAAAAAAAAAAA91804,AAAAAA66777,AAAAAAAAAAAAAAAAAAA89194,AAAAAAAAAAAAAAA36627,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAA73084,AAAAAAA79710,AAAAAAAAAAAAAAA40402,AAAAAAAAAAAAAAAAAAA65037}
49 | {56,5,54,37,49} | {AA21643,AAAAAAAAAAA92631,AAAAAAAA81587}
50 | {20,12,37,64,93} | {AAAAAAAAAA5483,AAAAAAAAAAAAAAAAAAA1205,AA6416,AAAAAAAAAAAAAAAAA63050,AAAAAAAAAAAAAAAAAA47955}
51 | {47} | {AAAAAAAAAAAAAA96505,AAAAAAAAAAAAAAAAAA36842,AAAAA95309,AAAAAAAA81587,AA6416,AAAA91194,AAAAAA58494,AAAAAA1059,AAAAAAAA69452}
52 | {89,0} | {AAAAAAAAAAAAAAAAAA47955,AAAAAAA48038,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAAAAA73084,AAAAA70466,AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA46154,AA66862}
53 | {38,17} | {AAAAAAAAAAA21658}
54 | {70,47} | {AAAAAAAAAAAAAAAAAA54141,AAAAA40681,AAAAAAA48038,AAAAAAAAAAAAAAAA29150,AAAAA41597,AAAAAAAAAAAAAAAAAA59334,AA15322}
55 | {47,79,47,64,72,25,71,24,93} | {AAAAAAAAAAAAAAAAAA55796,AAAAA62737}
56 | {33,7,60,54,93,90,77,85,39} | {AAAAAAAAAAAAAAAAAA32918,AA42406}
57 | {23,45,10,42,36,21,9,96} | {AAAAAAAAAAAAAAAAAAA70415}
58 | {92} | {AAAAAAAAAAAAAAAA98414,AAAAAAAA23648,AAAAAAAAAAAAAAAAAA55796,AA25381,AAAAAAAAAAA6119}
59 | {9,69,46,77} | {39557,AAAAAAA89932,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAAAAAAAAA26540,AAA20874,AA6416,AAAAAAAAAAAAAAAAAA47955}
60 | {62,2,59,38,89} | {AAAAAAA89932,AAAAAAAAAAAAAAAAAAA15356,AA99927,AA17009,AAAAAAAAAAAAAAA35875}
61 | {72,2,44,95,54,54,13} | {AAAAAAAAAAAAAAAAAAA91804}
62 | {83,72,29,73} | {AAAAAAAAAAAAA15097,AAAA8857,AAAAAAAAAAAA35809,AAAAAAAAAAAA52814,AAAAAAAAAAAAAAAAAAA38885,AAAAAAAAAAAAAAAAAA24183,AAAAAA43678,A96617}
63 | {11,4,61,87} | {AAAAAAAAA27249,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAA13198,AAA20874,39557,51533,AAAAAAAAAAA53908,AAAAAAAAAAAAAA96505,AAAAAAAA78938}
64 | {26,19,34,24,81,78} | {A96617,AAAAAAAAAAAAAAAAAAA70104,A68938,AAAAAAAAAAA53908,AAAAAAAAAAAAAAA453,AA17009,AAAAAAA80240}
65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
66 | {31,23,70,52,4,33,48,25} | {AAAAAAAAAAAAAAAAA69675,AAAAAAAA50094,AAAAAAAAAAA92631,AAAA35194,39557,AAAAAAA99836}
67 | {31,94,7,10} | {AAAAAA38063,A96617,AAAA35194,AAAAAAAAAAAA67946}
68 | {90,43,38} | {AA75092,AAAAAAAAAAAAAAAAA69675,AAAAAAAAAAA92631,AAAAAAAAA10012,AAAAAAAAAAAAA7929,AA21643}
69 | {67,35,99,85,72,86,44} | {AAAAAAAAAAAAAAAAAAA1205,AAAAAAAA50094,AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAAAAAAA47955}
70 | {56,70,83} | {AAAA41702,AAAAAAAAAAA82945,AA21643,AAAAAAAAAAA99000,A27153,AA25381,AAAAAAAAAAAAAA96505,AAAAAAA1242}
71 | {74,26} | {AAAAAAAAAAA50956,AA74433,AAAAAAA21462,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAA36627,AAAAAAAAAAAAA70254,AAAAAAAAAA43419,39557}
72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407}
73 | {88,25,96,78,65,15,29,19} | {AAA54451,AAAAAAAAA27249,AAAAAAA9228,AAAAAAAAAAAAAAA67062,AAAAAAAAAAAAAAAAAAA70415,AAAAA17383,AAAAAAAAAAAAAAAA33598}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
75 | {12,96,83,24,71,89,55} | {AAAA48949,AAAAAAAA29716,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAAA67946,AAAAAAAAAAAAAAAA29150,AAA28075,AAAAAAAAAAAAAAAAA43052}
76 | {92,55,10,7} | {AAAAAAAAAAAAAAA67062}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
78 | {55,89,44,84,34} | {AAAAAAAAAAA6119,AAAAAAAAAAAAAA8666,AA99927,AA42406,AAAAAAA81898,AAAAAAA9228,AAAAAAAAAAA92631,AA21643,AAAAAAAAAAAAAA28620}
79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
80 | {74,89,44,80,0} | {AAAA35194,AAAAAAAA79710,AAA20874,AAAAAAAAAAAAAAAAAAA70104,AAAAAAAAAAAAA73084,AAAAAAA57334,AAAAAAA9228,AAAAAAAAAAAAA62007}
81 | {63,77,54,48,61,53,97} | {AAAAAAAAAAAAAAA81326,AAAAAAAAAA22292,AA25381,AAAAAAAAAAA74076,AAAAAAA81898,AAAAAAAAA72121}
82 | {34,60,4,79,78,16,86,89,42,50} | {AAAAA40681,AAAAAAAAAAAAAAAAAA12591,AAAAAAA80240,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA70104}
83 | {14,10} | {AAAAAAAAAA22292,AAAAAAAAAAAAA70254,AAAAAAAAAAA6119}
84 | {11,83,35,13,96,94} | {AAAAA95309,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAAAAA24183}
85 | {39,60} | {AAAAAAAAAAAAAAAA55798,AAAAAAAAAA22292,AAAAAAA66161,AAAAAAA21462,AAAAAAAAAAAAAAAAAA12591,55847,AAAAAA98232,AAAAAAAAAAA46154}
86 | {33,81,72,74,45,36,82} | {AAAAAAAA81587,AAAAAAAAAAAAAA96505,45449,AAAA80176}
87 | {57,27,50,12,97,68} | {AAAAAAAAAAAAAAAAA26540,AAAAAAAAA10012,AAAAAAAAAAAA35809,AAAAAAAAAAAAAAAA29150,AAAAAAAAAAA82945,AAAAAA66777,31228,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAA96505}
88 | {41,90,77,24,6,24} | {AAAA35194,AAAA35194,AAAAAAA80240,AAAAAAAAAAA46154,AAAAAA58494,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA59334,AAAAAAAAAAAAAAAAAAA91804,AA74433}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
90 | {88,75} | {AAAAA60038,AAAAAAAA23648,AAAAAAAAAAA99000,AAAA41702,AAAAAAAAAAAAA22860,AAAAAAAAAAAAAAA68526}
91 | {78} | {AAAAAAAAAAAAA62007,AAA99043}
92 | {85,63,49,45} | {AAAAAAA89932,AAAAAAAAAAAAA22860,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAAA21089}
93 | {11} | {AAAAAAAAAAA176,AAAAAAAAAAAAAA8666,AAAAAAAAAAAAAAA453,AAAAAAAAAAAAA85723,A68938,AAAAAAAAAAAAA9821,AAAAAAA48038,AAAAAAAAAAAAAAAAA59387,AA99927,AAAAA17383}
94 | {98,9,85,62,88,91,60,61,38,86} | {AAAAAAAA81587,AAAAA17383,AAAAAAAA81587}
95 | {47,77} | {AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA74076,AAAAAAAAAA18107,AAAAA40681,AAAAAAAAAAAAAAA35875,AAAAA60038,AAAAAAA56483}
96 | {23,97,43} | {AAAAAAAAAA646,A87088}
97 | {54,2,86,65} | {47735,AAAAAAA99836,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAA29150,AAAAAAA80240,AAAAAAAAAAAAAAAA98414,AAAAAAA56483,AAAAAAAAAAAAAAAA29150,AAAAAAA39692,AA21643}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
99 | {37,86} | {AAAAAAAAAAAAAAAAAA32918,AAAAA70514,AAAAAAAAA10012,AAAAAAAAAAAAAAAAA59387,AAAAAAAAAA64777,AAAAAAAAAAAAAAAAAAA15356}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
101 | {} | {}
102 | {NULL} | {NULL}
(102 rows)
SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno;
seqno | i | t
-------+---+---
(0 rows)
SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno;
seqno | i | t
-------+----+----
101 | {} | {}
(1 row)
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
seqno | i | t
-------+--------+--------
102 | {NULL} | {NULL}
(1 row)
SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
seqno | i | t
-------+---+---
(0 rows)
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
seqno | i | t
-------+---+---
(0 rows)
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
seqno | i | t
-------+----+----
101 | {} | {}
(1 row)
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
seqno | i | t
@ -557,7 +704,132 @@ SELECT * FROM array_op_test WHERE t <@ '{AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,
-------+--------------------+-----------------------------------------------------------------------------------------------------------
22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
(2 rows)
101 | {} | {}
(3 rows)
SELECT * FROM array_op_test WHERE t = '{}' ORDER BY seqno;
seqno | i | t
-------+----+----
101 | {} | {}
(1 row)
SELECT * FROM array_op_test WHERE t @> '{}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 | {92,75,71,52,64,83} | {AAAAAAAA44066,AAAAAA1059,AAAAAAAAAAA176,AAAAAAA48038}
2 | {3,6} | {AAAAAA98232,AAAAAAAA79710,AAAAAAAAAAAAAAAAA69675,AAAAAAAAAAAAAAAA55798,AAAAAAAAA12793}
3 | {37,64,95,43,3,41,13,30,11,43} | {AAAAAAAAAA48845,AAAAA75968,AAAAA95309,AAA54451,AAAAAAAAAA22292,AAAAAAA99836,A96617,AA17009,AAAAAAAAAAAAAA95246}
4 | {71,39,99,55,33,75,45} | {AAAAAAAAA53663,AAAAAAAAAAAAAAA67062,AAAAAAAAAA64777,AAA99043,AAAAAAAAAAAAAAAAAAA91804,39557}
5 | {50,42,77,50,4} | {AAAAAAAAAAAAAAAAA26540,AAAAAAA79710,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA176,AAAAA95309,AAAAAAAAAAA46154,AAAAAA66777,AAAAAAAAA27249,AAAAAAAAAA64777,AAAAAAAAAAAAAAAAAAA70104}
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
7 | {12,51,88,64,8} | {AAAAAAAAAAAAAAAAAA12591,AAAAAAAAAAAAAAAAA50407,AAAAAAAAAAAA67946}
8 | {60,84} | {AAAAAAA81898,AAAAAA1059,AAAAAAAAAAAA81511,AAAAA961,AAAAAAAAAAAAAAAA31334,AAAAA64741,AA6416,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAAAA50407}
9 | {56,52,35,27,80,44,81,22} | {AAAAAAAAAAAAAAA73034,AAAAAAAAAAAAA7929,AAAAAAA66161,AA88409,39557,A27153,AAAAAAAA9523,AAAAAAAAAAA99000}
10 | {71,5,45} | {AAAAAAAAAAA21658,AAAAAAAAAAAA21089,AAA54451,AAAAAAAAAAAAAAAAAA54141,AAAAAAAAAAAAAA28620,AAAAAAAAAAA21658,AAAAAAAAAAA74076,AAAAAAAAA27249}
11 | {41,86,74,48,22,74,47,50} | {AAAAAAAA9523,AAAAAAAAAAAA37562,AAAAAAAAAAAAAAAA14047,AAAAAAAAAAA46154,AAAA41702,AAAAAAAAAAAAAAAAA764,AAAAA62737,39557}
12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
13 | {3,52,34,23} | {AAAAAA98232,AAAA49534,AAAAAAAAAAA21658}
14 | {78,57,19} | {AAAA8857,AAAAAAAAAAAAAAA73034,AAAAAAAA81587,AAAAAAAAAAAAAAA68526,AAAAA75968,AAAAAAAAAAAAAA65909,AAAAAAAAA10012,AAAAAAAAAAAAAA65909}
15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
16 | {14,63,85,11} | {AAAAAA66777}
17 | {7,10,81,85} | {AAAAAA43678,AAAAAAA12144,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAAAAA15356}
18 | {1} | {AAAAAAAAAAA33576,AAAAA95309,64261,AAA59323,AAAAAAAAAAAAAA95246,55847,AAAAAAAAAAAA67946,AAAAAAAAAAAAAAAAAA64374}
19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
20 | {72,89,70,51,54,37,8,49,79} | {AAAAAA58494}
21 | {2,8,65,10,5,79,43} | {AAAAAAAAAAAAAAAAA88852,AAAAAAAAAAAAAAAAAAA91804,AAAAA64669,AAAAAAAAAAAAAAAA1443,AAAAAAAAAAAAAAAA23657,AAAAA12179,AAAAAAAAAAAAAAAAA88852,AAAAAAAAAAAAAAAA31334,AAAAAAAAAAAAAAAA41303,AAAAAAAAAAAAAAAAAAA85420}
22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
23 | {40,90,5,38,72,40,30,10,43,55} | {A6053,AAAAAAAAAAA6119,AA44673,AAAAAAAAAAAAAAAAA764,AA17009,AAAAA17383,AAAAA70514,AAAAA33250,AAAAA95309,AAAAAAAAAAAA37562}
24 | {94,61,99,35,48} | {AAAAAAAAAAA50956,AAAAAAAAAAA15165,AAAA85070,AAAAAAAAAAAAAAA36627,AAAAA961,AAAAAAAAAA55219}
25 | {31,1,10,11,27,79,38} | {AAAAAAAAAAAAAAAAAA59334,45449}
26 | {71,10,9,69,75} | {47735,AAAAAAA21462,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAAAAA91804,AAAAAAAAA72121,AAAAAAAAAAAAAAAAAAA1205,AAAAA41597,AAAA8857,AAAAAAAAAAAAAAAAAAA15356,AA17009}
27 | {94} | {AA6416,A6053,AAAAAAA21462,AAAAAAA57334,AAAAAAAAAAAAAAAAAA12591,AA88409,AAAAAAAAAAAAA70254}
28 | {14,33,6,34,14} | {AAAAAAAAAAAAAAA13198,AAAAAAAA69452,AAAAAAAAAAA82945,AAAAAAA12144,AAAAAAAAA72121,AAAAAAAAAA18601}
29 | {39,21} | {AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAAAAA38885,AAAA85070,AAAAAAAAAAAAAAAAAAA70104,AAAAA66674,AAAAAAAAAAAAA62007,AAAAAAAA69452,AAAAAAA1242,AAAAAAAAAAAAAAAA1729,AAAA35194}
30 | {26,81,47,91,34} | {AAAAAAAAAAAAAAAAAAA70104,AAAAAAA80240}
31 | {80,24,18,21,54} | {AAAAAAAAAAAAAAA13198,AAAAAAAAAAAAAAAAAAA70415,A27153,AAAAAAAAA53663,AAAAAAAAAAAAAAAAA50407,A68938}
32 | {58,79,82,80,67,75,98,10,41} | {AAAAAAAAAAAAAAAAAA61286,AAA54451,AAAAAAAAAAAAAAAAAAA87527,A96617,51533}
33 | {74,73} | {A85417,AAAAAAA56483,AAAAA17383,AAAAAAAAAAAAA62159,AAAAAAAAAAAA52814,AAAAAAAAAAAAA85723,AAAAAAAAAAAAAAAAAA55796}
34 | {70,45} | {AAAAAAAAAAAAAAAAAA71621,AAAAAAAAAAAAAA28620,AAAAAAAAAA55219,AAAAAAAA23648,AAAAAAAAAA22292,AAAAAAA1242}
35 | {23,40} | {AAAAAAAAAAAA52814,AAAA48949,AAAAAAAAA34727,AAAA8857,AAAAAAAAAAAAAAAAAAA62179,AAAAAAAAAAAAAAA68526,AAAAAAA99836,AAAAAAAA50094,AAAA91194,AAAAAAAAAAAAA73084}
36 | {79,82,14,52,30,5,79} | {AAAAAAAAA53663,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA89194,AA88409,AAAAAAAAAAAAAAA81326,AAAAAAAAAAAAAAAAA63050,AAAAAAAAAAAAAAAA33598}
37 | {53,11,81,39,3,78,58,64,74} | {AAAAAAAAAAAAAAAAAAA17075,AAAAAAA66161,AAAAAAAA23648,AAAAAAAAAAAAAA10611}
38 | {59,5,4,95,28} | {AAAAAAAAAAA82945,A96617,47735,AAAAA12179,AAAAA64669,AAAAAA99807,AA74433,AAAAAAAAAAAAAAAAA59387}
39 | {82,43,99,16,74} | {AAAAAAAAAAAAAAA67062,AAAAAAA57334,AAAAAAAAAAAAAA65909,A27153,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAA64777,AAAAAAAAAAAA81511,AAAAAAAAAAAAAA65909,AAAAAAAAAAAAAA28620}
40 | {34} | {AAAAAAAAAAAAAA10611,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAA31334,AAAAA70466,AAAAAAAA81587,AAAAAAA74623}
41 | {19,26,63,12,93,73,27,94} | {AAAAAAA79710,AAAAAAAAAA55219,AAAA41702,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA71621,AAAAAAAAAAAAAAAAA63050,AAAAAAA99836,AAAAAAAAAAAAAA8666}
42 | {15,76,82,75,8,91} | {AAAAAAAAAAA176,AAAAAA38063,45449,AAAAAA54032,AAAAAAA81898,AA6416,AAAAAAAAAAAAAAAAAAA62179,45449,AAAAA60038,AAAAAAAA81587}
43 | {39,87,91,97,79,28} | {AAAAAAAAAAA74076,A96617,AAAAAAAAAAAAAAAAAAA89194,AAAAAAAAAAAAAAAAAA55796,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAA67946}
44 | {40,58,68,29,54} | {AAAAAAA81898,AAAAAA66777,AAAAAA98232}
45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
46 | {53,24} | {AAAAAAAAAAA53908,AAAAAA54032,AAAAA17383,AAAA48949,AAAAAAAAAA18601,AAAAA64669,45449,AAAAAAAAAAA98051,AAAAAAAAAAAAAAAAAA71621}
47 | {98,23,64,12,75,61} | {AAA59323,AAAAA95309,AAAAAAAAAAAAAAAA31334,AAAAAAAAA27249,AAAAA17383,AAAAAAAAAAAA37562,AAAAAA1059,A84822,55847,AAAAA70466}
48 | {76,14} | {AAAAAAAAAAAAA59671,AAAAAAAAAAAAAAAAAAA91804,AAAAAA66777,AAAAAAAAAAAAAAAAAAA89194,AAAAAAAAAAAAAAA36627,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAA73084,AAAAAAA79710,AAAAAAAAAAAAAAA40402,AAAAAAAAAAAAAAAAAAA65037}
49 | {56,5,54,37,49} | {AA21643,AAAAAAAAAAA92631,AAAAAAAA81587}
50 | {20,12,37,64,93} | {AAAAAAAAAA5483,AAAAAAAAAAAAAAAAAAA1205,AA6416,AAAAAAAAAAAAAAAAA63050,AAAAAAAAAAAAAAAAAA47955}
51 | {47} | {AAAAAAAAAAAAAA96505,AAAAAAAAAAAAAAAAAA36842,AAAAA95309,AAAAAAAA81587,AA6416,AAAA91194,AAAAAA58494,AAAAAA1059,AAAAAAAA69452}
52 | {89,0} | {AAAAAAAAAAAAAAAAAA47955,AAAAAAA48038,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAAAAA73084,AAAAA70466,AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA46154,AA66862}
53 | {38,17} | {AAAAAAAAAAA21658}
54 | {70,47} | {AAAAAAAAAAAAAAAAAA54141,AAAAA40681,AAAAAAA48038,AAAAAAAAAAAAAAAA29150,AAAAA41597,AAAAAAAAAAAAAAAAAA59334,AA15322}
55 | {47,79,47,64,72,25,71,24,93} | {AAAAAAAAAAAAAAAAAA55796,AAAAA62737}
56 | {33,7,60,54,93,90,77,85,39} | {AAAAAAAAAAAAAAAAAA32918,AA42406}
57 | {23,45,10,42,36,21,9,96} | {AAAAAAAAAAAAAAAAAAA70415}
58 | {92} | {AAAAAAAAAAAAAAAA98414,AAAAAAAA23648,AAAAAAAAAAAAAAAAAA55796,AA25381,AAAAAAAAAAA6119}
59 | {9,69,46,77} | {39557,AAAAAAA89932,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAAAAAAAAA26540,AAA20874,AA6416,AAAAAAAAAAAAAAAAAA47955}
60 | {62,2,59,38,89} | {AAAAAAA89932,AAAAAAAAAAAAAAAAAAA15356,AA99927,AA17009,AAAAAAAAAAAAAAA35875}
61 | {72,2,44,95,54,54,13} | {AAAAAAAAAAAAAAAAAAA91804}
62 | {83,72,29,73} | {AAAAAAAAAAAAA15097,AAAA8857,AAAAAAAAAAAA35809,AAAAAAAAAAAA52814,AAAAAAAAAAAAAAAAAAA38885,AAAAAAAAAAAAAAAAAA24183,AAAAAA43678,A96617}
63 | {11,4,61,87} | {AAAAAAAAA27249,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAA13198,AAA20874,39557,51533,AAAAAAAAAAA53908,AAAAAAAAAAAAAA96505,AAAAAAAA78938}
64 | {26,19,34,24,81,78} | {A96617,AAAAAAAAAAAAAAAAAAA70104,A68938,AAAAAAAAAAA53908,AAAAAAAAAAAAAAA453,AA17009,AAAAAAA80240}
65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
66 | {31,23,70,52,4,33,48,25} | {AAAAAAAAAAAAAAAAA69675,AAAAAAAA50094,AAAAAAAAAAA92631,AAAA35194,39557,AAAAAAA99836}
67 | {31,94,7,10} | {AAAAAA38063,A96617,AAAA35194,AAAAAAAAAAAA67946}
68 | {90,43,38} | {AA75092,AAAAAAAAAAAAAAAAA69675,AAAAAAAAAAA92631,AAAAAAAAA10012,AAAAAAAAAAAAA7929,AA21643}
69 | {67,35,99,85,72,86,44} | {AAAAAAAAAAAAAAAAAAA1205,AAAAAAAA50094,AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAAAAAAA47955}
70 | {56,70,83} | {AAAA41702,AAAAAAAAAAA82945,AA21643,AAAAAAAAAAA99000,A27153,AA25381,AAAAAAAAAAAAAA96505,AAAAAAA1242}
71 | {74,26} | {AAAAAAAAAAA50956,AA74433,AAAAAAA21462,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAA36627,AAAAAAAAAAAAA70254,AAAAAAAAAA43419,39557}
72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407}
73 | {88,25,96,78,65,15,29,19} | {AAA54451,AAAAAAAAA27249,AAAAAAA9228,AAAAAAAAAAAAAAA67062,AAAAAAAAAAAAAAAAAAA70415,AAAAA17383,AAAAAAAAAAAAAAAA33598}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
75 | {12,96,83,24,71,89,55} | {AAAA48949,AAAAAAAA29716,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAAA67946,AAAAAAAAAAAAAAAA29150,AAA28075,AAAAAAAAAAAAAAAAA43052}
76 | {92,55,10,7} | {AAAAAAAAAAAAAAA67062}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
78 | {55,89,44,84,34} | {AAAAAAAAAAA6119,AAAAAAAAAAAAAA8666,AA99927,AA42406,AAAAAAA81898,AAAAAAA9228,AAAAAAAAAAA92631,AA21643,AAAAAAAAAAAAAA28620}
79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
80 | {74,89,44,80,0} | {AAAA35194,AAAAAAAA79710,AAA20874,AAAAAAAAAAAAAAAAAAA70104,AAAAAAAAAAAAA73084,AAAAAAA57334,AAAAAAA9228,AAAAAAAAAAAAA62007}
81 | {63,77,54,48,61,53,97} | {AAAAAAAAAAAAAAA81326,AAAAAAAAAA22292,AA25381,AAAAAAAAAAA74076,AAAAAAA81898,AAAAAAAAA72121}
82 | {34,60,4,79,78,16,86,89,42,50} | {AAAAA40681,AAAAAAAAAAAAAAAAAA12591,AAAAAAA80240,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA70104}
83 | {14,10} | {AAAAAAAAAA22292,AAAAAAAAAAAAA70254,AAAAAAAAAAA6119}
84 | {11,83,35,13,96,94} | {AAAAA95309,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAAAAA24183}
85 | {39,60} | {AAAAAAAAAAAAAAAA55798,AAAAAAAAAA22292,AAAAAAA66161,AAAAAAA21462,AAAAAAAAAAAAAAAAAA12591,55847,AAAAAA98232,AAAAAAAAAAA46154}
86 | {33,81,72,74,45,36,82} | {AAAAAAAA81587,AAAAAAAAAAAAAA96505,45449,AAAA80176}
87 | {57,27,50,12,97,68} | {AAAAAAAAAAAAAAAAA26540,AAAAAAAAA10012,AAAAAAAAAAAA35809,AAAAAAAAAAAAAAAA29150,AAAAAAAAAAA82945,AAAAAA66777,31228,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAA96505}
88 | {41,90,77,24,6,24} | {AAAA35194,AAAA35194,AAAAAAA80240,AAAAAAAAAAA46154,AAAAAA58494,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA59334,AAAAAAAAAAAAAAAAAAA91804,AA74433}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
90 | {88,75} | {AAAAA60038,AAAAAAAA23648,AAAAAAAAAAA99000,AAAA41702,AAAAAAAAAAAAA22860,AAAAAAAAAAAAAAA68526}
91 | {78} | {AAAAAAAAAAAAA62007,AAA99043}
92 | {85,63,49,45} | {AAAAAAA89932,AAAAAAAAAAAAA22860,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAAA21089}
93 | {11} | {AAAAAAAAAAA176,AAAAAAAAAAAAAA8666,AAAAAAAAAAAAAAA453,AAAAAAAAAAAAA85723,A68938,AAAAAAAAAAAAA9821,AAAAAAA48038,AAAAAAAAAAAAAAAAA59387,AA99927,AAAAA17383}
94 | {98,9,85,62,88,91,60,61,38,86} | {AAAAAAAA81587,AAAAA17383,AAAAAAAA81587}
95 | {47,77} | {AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA74076,AAAAAAAAAA18107,AAAAA40681,AAAAAAAAAAAAAAA35875,AAAAA60038,AAAAAAA56483}
96 | {23,97,43} | {AAAAAAAAAA646,A87088}
97 | {54,2,86,65} | {47735,AAAAAAA99836,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAA29150,AAAAAAA80240,AAAAAAAAAAAAAAAA98414,AAAAAAA56483,AAAAAAAAAAAAAAAA29150,AAAAAAA39692,AA21643}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
99 | {37,86} | {AAAAAAAAAAAAAAAAAA32918,AAAAA70514,AAAAAAAAA10012,AAAAAAAAAAAAAAAAA59387,AAAAAAAAAA64777,AAAAAAAAAAAAAAAAAAA15356}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
101 | {} | {}
102 | {NULL} | {NULL}
(102 rows)
SELECT * FROM array_op_test WHERE t && '{}' ORDER BY seqno;
seqno | i | t
-------+---+---
(0 rows)
SELECT * FROM array_op_test WHERE t <@ '{}' ORDER BY seqno;
seqno | i | t
-------+----+----
101 | {} | {}
(1 row)
-- array casts
SELECT ARRAY[1,2,3]::text[]::int[]::float8[] AS "{1,2,3}";

View File

@ -577,10 +577,24 @@ RESET enable_bitmapscan;
--
-- GIN over int[] and text[]
--
-- Note: GIN currently supports only bitmap scans, not plain indexscans
--
SET enable_seqscan = OFF;
SET enable_indexscan = ON;
SET enable_bitmapscan = OFF;
SET enable_indexscan = OFF;
SET enable_bitmapscan = ON;
CREATE INDEX intarrayidx ON array_index_op_test USING gin (i);
explain (costs off)
SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
QUERY PLAN
----------------------------------------------------
Sort
Sort Key: seqno
-> Bitmap Heap Scan on array_index_op_test
Recheck Cond: (i @> '{32}'::integer[])
-> Bitmap Index Scan on intarrayidx
Index Cond: (i @> '{32}'::integer[])
(6 rows)
SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@ -659,7 +673,8 @@ SELECT * FROM array_index_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno;
40 | {34} | {AAAAAAAAAAAAAA10611,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAA31334,AAAAA70466,AAAAAAAA81587,AAAAAAA74623}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
(3 rows)
101 | {} | {}
(4 rows)
SELECT * FROM array_index_op_test WHERE i = '{47,77}' ORDER BY seqno;
seqno | i | t
@ -667,7 +682,165 @@ SELECT * FROM array_index_op_test WHERE i = '{47,77}' ORDER BY seqno;
95 | {47,77} | {AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA74076,AAAAAAAAAA18107,AAAAA40681,AAAAAAAAAAAAAAA35875,AAAAA60038,AAAAAAA56483}
(1 row)
SELECT * FROM array_index_op_test WHERE i = '{}' ORDER BY seqno;
seqno | i | t
-------+----+----
101 | {} | {}
(1 row)
SELECT * FROM array_index_op_test WHERE i @> '{}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 | {92,75,71,52,64,83} | {AAAAAAAA44066,AAAAAA1059,AAAAAAAAAAA176,AAAAAAA48038}
2 | {3,6} | {AAAAAA98232,AAAAAAAA79710,AAAAAAAAAAAAAAAAA69675,AAAAAAAAAAAAAAAA55798,AAAAAAAAA12793}
3 | {37,64,95,43,3,41,13,30,11,43} | {AAAAAAAAAA48845,AAAAA75968,AAAAA95309,AAA54451,AAAAAAAAAA22292,AAAAAAA99836,A96617,AA17009,AAAAAAAAAAAAAA95246}
4 | {71,39,99,55,33,75,45} | {AAAAAAAAA53663,AAAAAAAAAAAAAAA67062,AAAAAAAAAA64777,AAA99043,AAAAAAAAAAAAAAAAAAA91804,39557}
5 | {50,42,77,50,4} | {AAAAAAAAAAAAAAAAA26540,AAAAAAA79710,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA176,AAAAA95309,AAAAAAAAAAA46154,AAAAAA66777,AAAAAAAAA27249,AAAAAAAAAA64777,AAAAAAAAAAAAAAAAAAA70104}
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
7 | {12,51,88,64,8} | {AAAAAAAAAAAAAAAAAA12591,AAAAAAAAAAAAAAAAA50407,AAAAAAAAAAAA67946}
8 | {60,84} | {AAAAAAA81898,AAAAAA1059,AAAAAAAAAAAA81511,AAAAA961,AAAAAAAAAAAAAAAA31334,AAAAA64741,AA6416,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAAAA50407}
9 | {56,52,35,27,80,44,81,22} | {AAAAAAAAAAAAAAA73034,AAAAAAAAAAAAA7929,AAAAAAA66161,AA88409,39557,A27153,AAAAAAAA9523,AAAAAAAAAAA99000}
10 | {71,5,45} | {AAAAAAAAAAA21658,AAAAAAAAAAAA21089,AAA54451,AAAAAAAAAAAAAAAAAA54141,AAAAAAAAAAAAAA28620,AAAAAAAAAAA21658,AAAAAAAAAAA74076,AAAAAAAAA27249}
11 | {41,86,74,48,22,74,47,50} | {AAAAAAAA9523,AAAAAAAAAAAA37562,AAAAAAAAAAAAAAAA14047,AAAAAAAAAAA46154,AAAA41702,AAAAAAAAAAAAAAAAA764,AAAAA62737,39557}
12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
13 | {3,52,34,23} | {AAAAAA98232,AAAA49534,AAAAAAAAAAA21658}
14 | {78,57,19} | {AAAA8857,AAAAAAAAAAAAAAA73034,AAAAAAAA81587,AAAAAAAAAAAAAAA68526,AAAAA75968,AAAAAAAAAAAAAA65909,AAAAAAAAA10012,AAAAAAAAAAAAAA65909}
15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
16 | {14,63,85,11} | {AAAAAA66777}
17 | {7,10,81,85} | {AAAAAA43678,AAAAAAA12144,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAAAAA15356}
18 | {1} | {AAAAAAAAAAA33576,AAAAA95309,64261,AAA59323,AAAAAAAAAAAAAA95246,55847,AAAAAAAAAAAA67946,AAAAAAAAAAAAAAAAAA64374}
19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
20 | {72,89,70,51,54,37,8,49,79} | {AAAAAA58494}
21 | {2,8,65,10,5,79,43} | {AAAAAAAAAAAAAAAAA88852,AAAAAAAAAAAAAAAAAAA91804,AAAAA64669,AAAAAAAAAAAAAAAA1443,AAAAAAAAAAAAAAAA23657,AAAAA12179,AAAAAAAAAAAAAAAAA88852,AAAAAAAAAAAAAAAA31334,AAAAAAAAAAAAAAAA41303,AAAAAAAAAAAAAAAAAAA85420}
22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
23 | {40,90,5,38,72,40,30,10,43,55} | {A6053,AAAAAAAAAAA6119,AA44673,AAAAAAAAAAAAAAAAA764,AA17009,AAAAA17383,AAAAA70514,AAAAA33250,AAAAA95309,AAAAAAAAAAAA37562}
24 | {94,61,99,35,48} | {AAAAAAAAAAA50956,AAAAAAAAAAA15165,AAAA85070,AAAAAAAAAAAAAAA36627,AAAAA961,AAAAAAAAAA55219}
25 | {31,1,10,11,27,79,38} | {AAAAAAAAAAAAAAAAAA59334,45449}
26 | {71,10,9,69,75} | {47735,AAAAAAA21462,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAAAAA91804,AAAAAAAAA72121,AAAAAAAAAAAAAAAAAAA1205,AAAAA41597,AAAA8857,AAAAAAAAAAAAAAAAAAA15356,AA17009}
27 | {94} | {AA6416,A6053,AAAAAAA21462,AAAAAAA57334,AAAAAAAAAAAAAAAAAA12591,AA88409,AAAAAAAAAAAAA70254}
28 | {14,33,6,34,14} | {AAAAAAAAAAAAAAA13198,AAAAAAAA69452,AAAAAAAAAAA82945,AAAAAAA12144,AAAAAAAAA72121,AAAAAAAAAA18601}
29 | {39,21} | {AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAAAAA38885,AAAA85070,AAAAAAAAAAAAAAAAAAA70104,AAAAA66674,AAAAAAAAAAAAA62007,AAAAAAAA69452,AAAAAAA1242,AAAAAAAAAAAAAAAA1729,AAAA35194}
30 | {26,81,47,91,34} | {AAAAAAAAAAAAAAAAAAA70104,AAAAAAA80240}
31 | {80,24,18,21,54} | {AAAAAAAAAAAAAAA13198,AAAAAAAAAAAAAAAAAAA70415,A27153,AAAAAAAAA53663,AAAAAAAAAAAAAAAAA50407,A68938}
32 | {58,79,82,80,67,75,98,10,41} | {AAAAAAAAAAAAAAAAAA61286,AAA54451,AAAAAAAAAAAAAAAAAAA87527,A96617,51533}
33 | {74,73} | {A85417,AAAAAAA56483,AAAAA17383,AAAAAAAAAAAAA62159,AAAAAAAAAAAA52814,AAAAAAAAAAAAA85723,AAAAAAAAAAAAAAAAAA55796}
34 | {70,45} | {AAAAAAAAAAAAAAAAAA71621,AAAAAAAAAAAAAA28620,AAAAAAAAAA55219,AAAAAAAA23648,AAAAAAAAAA22292,AAAAAAA1242}
35 | {23,40} | {AAAAAAAAAAAA52814,AAAA48949,AAAAAAAAA34727,AAAA8857,AAAAAAAAAAAAAAAAAAA62179,AAAAAAAAAAAAAAA68526,AAAAAAA99836,AAAAAAAA50094,AAAA91194,AAAAAAAAAAAAA73084}
36 | {79,82,14,52,30,5,79} | {AAAAAAAAA53663,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA89194,AA88409,AAAAAAAAAAAAAAA81326,AAAAAAAAAAAAAAAAA63050,AAAAAAAAAAAAAAAA33598}
37 | {53,11,81,39,3,78,58,64,74} | {AAAAAAAAAAAAAAAAAAA17075,AAAAAAA66161,AAAAAAAA23648,AAAAAAAAAAAAAA10611}
38 | {59,5,4,95,28} | {AAAAAAAAAAA82945,A96617,47735,AAAAA12179,AAAAA64669,AAAAAA99807,AA74433,AAAAAAAAAAAAAAAAA59387}
39 | {82,43,99,16,74} | {AAAAAAAAAAAAAAA67062,AAAAAAA57334,AAAAAAAAAAAAAA65909,A27153,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAA64777,AAAAAAAAAAAA81511,AAAAAAAAAAAAAA65909,AAAAAAAAAAAAAA28620}
40 | {34} | {AAAAAAAAAAAAAA10611,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAA31334,AAAAA70466,AAAAAAAA81587,AAAAAAA74623}
41 | {19,26,63,12,93,73,27,94} | {AAAAAAA79710,AAAAAAAAAA55219,AAAA41702,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA71621,AAAAAAAAAAAAAAAAA63050,AAAAAAA99836,AAAAAAAAAAAAAA8666}
42 | {15,76,82,75,8,91} | {AAAAAAAAAAA176,AAAAAA38063,45449,AAAAAA54032,AAAAAAA81898,AA6416,AAAAAAAAAAAAAAAAAAA62179,45449,AAAAA60038,AAAAAAAA81587}
43 | {39,87,91,97,79,28} | {AAAAAAAAAAA74076,A96617,AAAAAAAAAAAAAAAAAAA89194,AAAAAAAAAAAAAAAAAA55796,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAA67946}
44 | {40,58,68,29,54} | {AAAAAAA81898,AAAAAA66777,AAAAAA98232}
45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
46 | {53,24} | {AAAAAAAAAAA53908,AAAAAA54032,AAAAA17383,AAAA48949,AAAAAAAAAA18601,AAAAA64669,45449,AAAAAAAAAAA98051,AAAAAAAAAAAAAAAAAA71621}
47 | {98,23,64,12,75,61} | {AAA59323,AAAAA95309,AAAAAAAAAAAAAAAA31334,AAAAAAAAA27249,AAAAA17383,AAAAAAAAAAAA37562,AAAAAA1059,A84822,55847,AAAAA70466}
48 | {76,14} | {AAAAAAAAAAAAA59671,AAAAAAAAAAAAAAAAAAA91804,AAAAAA66777,AAAAAAAAAAAAAAAAAAA89194,AAAAAAAAAAAAAAA36627,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAA73084,AAAAAAA79710,AAAAAAAAAAAAAAA40402,AAAAAAAAAAAAAAAAAAA65037}
49 | {56,5,54,37,49} | {AA21643,AAAAAAAAAAA92631,AAAAAAAA81587}
50 | {20,12,37,64,93} | {AAAAAAAAAA5483,AAAAAAAAAAAAAAAAAAA1205,AA6416,AAAAAAAAAAAAAAAAA63050,AAAAAAAAAAAAAAAAAA47955}
51 | {47} | {AAAAAAAAAAAAAA96505,AAAAAAAAAAAAAAAAAA36842,AAAAA95309,AAAAAAAA81587,AA6416,AAAA91194,AAAAAA58494,AAAAAA1059,AAAAAAAA69452}
52 | {89,0} | {AAAAAAAAAAAAAAAAAA47955,AAAAAAA48038,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAAAAA73084,AAAAA70466,AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA46154,AA66862}
53 | {38,17} | {AAAAAAAAAAA21658}
54 | {70,47} | {AAAAAAAAAAAAAAAAAA54141,AAAAA40681,AAAAAAA48038,AAAAAAAAAAAAAAAA29150,AAAAA41597,AAAAAAAAAAAAAAAAAA59334,AA15322}
55 | {47,79,47,64,72,25,71,24,93} | {AAAAAAAAAAAAAAAAAA55796,AAAAA62737}
56 | {33,7,60,54,93,90,77,85,39} | {AAAAAAAAAAAAAAAAAA32918,AA42406}
57 | {23,45,10,42,36,21,9,96} | {AAAAAAAAAAAAAAAAAAA70415}
58 | {92} | {AAAAAAAAAAAAAAAA98414,AAAAAAAA23648,AAAAAAAAAAAAAAAAAA55796,AA25381,AAAAAAAAAAA6119}
59 | {9,69,46,77} | {39557,AAAAAAA89932,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAAAAAAAAA26540,AAA20874,AA6416,AAAAAAAAAAAAAAAAAA47955}
60 | {62,2,59,38,89} | {AAAAAAA89932,AAAAAAAAAAAAAAAAAAA15356,AA99927,AA17009,AAAAAAAAAAAAAAA35875}
61 | {72,2,44,95,54,54,13} | {AAAAAAAAAAAAAAAAAAA91804}
62 | {83,72,29,73} | {AAAAAAAAAAAAA15097,AAAA8857,AAAAAAAAAAAA35809,AAAAAAAAAAAA52814,AAAAAAAAAAAAAAAAAAA38885,AAAAAAAAAAAAAAAAAA24183,AAAAAA43678,A96617}
63 | {11,4,61,87} | {AAAAAAAAA27249,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAA13198,AAA20874,39557,51533,AAAAAAAAAAA53908,AAAAAAAAAAAAAA96505,AAAAAAAA78938}
64 | {26,19,34,24,81,78} | {A96617,AAAAAAAAAAAAAAAAAAA70104,A68938,AAAAAAAAAAA53908,AAAAAAAAAAAAAAA453,AA17009,AAAAAAA80240}
65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
66 | {31,23,70,52,4,33,48,25} | {AAAAAAAAAAAAAAAAA69675,AAAAAAAA50094,AAAAAAAAAAA92631,AAAA35194,39557,AAAAAAA99836}
67 | {31,94,7,10} | {AAAAAA38063,A96617,AAAA35194,AAAAAAAAAAAA67946}
68 | {90,43,38} | {AA75092,AAAAAAAAAAAAAAAAA69675,AAAAAAAAAAA92631,AAAAAAAAA10012,AAAAAAAAAAAAA7929,AA21643}
69 | {67,35,99,85,72,86,44} | {AAAAAAAAAAAAAAAAAAA1205,AAAAAAAA50094,AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAAAAAAA47955}
70 | {56,70,83} | {AAAA41702,AAAAAAAAAAA82945,AA21643,AAAAAAAAAAA99000,A27153,AA25381,AAAAAAAAAAAAAA96505,AAAAAAA1242}
71 | {74,26} | {AAAAAAAAAAA50956,AA74433,AAAAAAA21462,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAA36627,AAAAAAAAAAAAA70254,AAAAAAAAAA43419,39557}
72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407}
73 | {88,25,96,78,65,15,29,19} | {AAA54451,AAAAAAAAA27249,AAAAAAA9228,AAAAAAAAAAAAAAA67062,AAAAAAAAAAAAAAAAAAA70415,AAAAA17383,AAAAAAAAAAAAAAAA33598}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
75 | {12,96,83,24,71,89,55} | {AAAA48949,AAAAAAAA29716,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAAA67946,AAAAAAAAAAAAAAAA29150,AAA28075,AAAAAAAAAAAAAAAAA43052}
76 | {92,55,10,7} | {AAAAAAAAAAAAAAA67062}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
78 | {55,89,44,84,34} | {AAAAAAAAAAA6119,AAAAAAAAAAAAAA8666,AA99927,AA42406,AAAAAAA81898,AAAAAAA9228,AAAAAAAAAAA92631,AA21643,AAAAAAAAAAAAAA28620}
79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
80 | {74,89,44,80,0} | {AAAA35194,AAAAAAAA79710,AAA20874,AAAAAAAAAAAAAAAAAAA70104,AAAAAAAAAAAAA73084,AAAAAAA57334,AAAAAAA9228,AAAAAAAAAAAAA62007}
81 | {63,77,54,48,61,53,97} | {AAAAAAAAAAAAAAA81326,AAAAAAAAAA22292,AA25381,AAAAAAAAAAA74076,AAAAAAA81898,AAAAAAAAA72121}
82 | {34,60,4,79,78,16,86,89,42,50} | {AAAAA40681,AAAAAAAAAAAAAAAAAA12591,AAAAAAA80240,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA70104}
83 | {14,10} | {AAAAAAAAAA22292,AAAAAAAAAAAAA70254,AAAAAAAAAAA6119}
84 | {11,83,35,13,96,94} | {AAAAA95309,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAAAAA24183}
85 | {39,60} | {AAAAAAAAAAAAAAAA55798,AAAAAAAAAA22292,AAAAAAA66161,AAAAAAA21462,AAAAAAAAAAAAAAAAAA12591,55847,AAAAAA98232,AAAAAAAAAAA46154}
86 | {33,81,72,74,45,36,82} | {AAAAAAAA81587,AAAAAAAAAAAAAA96505,45449,AAAA80176}
87 | {57,27,50,12,97,68} | {AAAAAAAAAAAAAAAAA26540,AAAAAAAAA10012,AAAAAAAAAAAA35809,AAAAAAAAAAAAAAAA29150,AAAAAAAAAAA82945,AAAAAA66777,31228,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAA96505}
88 | {41,90,77,24,6,24} | {AAAA35194,AAAA35194,AAAAAAA80240,AAAAAAAAAAA46154,AAAAAA58494,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA59334,AAAAAAAAAAAAAAAAAAA91804,AA74433}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
90 | {88,75} | {AAAAA60038,AAAAAAAA23648,AAAAAAAAAAA99000,AAAA41702,AAAAAAAAAAAAA22860,AAAAAAAAAAAAAAA68526}
91 | {78} | {AAAAAAAAAAAAA62007,AAA99043}
92 | {85,63,49,45} | {AAAAAAA89932,AAAAAAAAAAAAA22860,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAAA21089}
93 | {11} | {AAAAAAAAAAA176,AAAAAAAAAAAAAA8666,AAAAAAAAAAAAAAA453,AAAAAAAAAAAAA85723,A68938,AAAAAAAAAAAAA9821,AAAAAAA48038,AAAAAAAAAAAAAAAAA59387,AA99927,AAAAA17383}
94 | {98,9,85,62,88,91,60,61,38,86} | {AAAAAAAA81587,AAAAA17383,AAAAAAAA81587}
95 | {47,77} | {AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA74076,AAAAAAAAAA18107,AAAAA40681,AAAAAAAAAAAAAAA35875,AAAAA60038,AAAAAAA56483}
96 | {23,97,43} | {AAAAAAAAAA646,A87088}
97 | {54,2,86,65} | {47735,AAAAAAA99836,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAA29150,AAAAAAA80240,AAAAAAAAAAAAAAAA98414,AAAAAAA56483,AAAAAAAAAAAAAAAA29150,AAAAAAA39692,AA21643}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
99 | {37,86} | {AAAAAAAAAAAAAAAAAA32918,AAAAA70514,AAAAAAAAA10012,AAAAAAAAAAAAAAAAA59387,AAAAAAAAAA64777,AAAAAAAAAAAAAAAAAAA15356}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
101 | {} | {}
102 | {NULL} | {NULL}
(102 rows)
SELECT * FROM array_index_op_test WHERE i && '{}' ORDER BY seqno;
seqno | i | t
-------+---+---
(0 rows)
SELECT * FROM array_index_op_test WHERE i <@ '{}' ORDER BY seqno;
seqno | i | t
-------+----+----
101 | {} | {}
(1 row)
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
seqno | i | t
-------+--------+--------
102 | {NULL} | {NULL}
(1 row)
SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
seqno | i | t
-------+---+---
(0 rows)
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
seqno | i | t
-------+---+---
(0 rows)
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
seqno | i | t
-------+----+----
101 | {} | {}
(1 row)
CREATE INDEX textarrayidx ON array_index_op_test USING gin (t);
explain (costs off)
SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
QUERY PLAN
------------------------------------------------------------
Sort
Sort Key: seqno
-> Bitmap Heap Scan on array_index_op_test
Recheck Cond: (t @> '{AAAAAAAA72908}'::text[])
-> Bitmap Index Scan on textarrayidx
Index Cond: (t @> '{AAAAAAAA72908}'::text[])
(6 rows)
SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
seqno | i | t
-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
@ -724,7 +897,8 @@ SELECT * FROM array_index_op_test WHERE t <@ '{AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA
-------+--------------------+-----------------------------------------------------------------------------------------------------------
22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
(2 rows)
101 | {} | {}
(3 rows)
SELECT * FROM array_index_op_test WHERE t = '{AAAAAAAAAA646,A87088}' ORDER BY seqno;
seqno | i | t
@ -732,101 +906,133 @@ SELECT * FROM array_index_op_test WHERE t = '{AAAAAAAAAA646,A87088}' ORDER BY se
96 | {23,97,43} | {AAAAAAAAAA646,A87088}
(1 row)
-- Repeat some of the above tests but exercising bitmapscans instead
SET enable_indexscan = OFF;
SET enable_bitmapscan = ON;
SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
SELECT * FROM array_index_op_test WHERE t = '{}' ORDER BY seqno;
seqno | i | t
-------+----+----
101 | {} | {}
(1 row)
SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
SELECT * FROM array_index_op_test WHERE i @> '{17}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
53 | {38,17} | {AAAAAAAAAAA21658}
65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
(8 rows)
SELECT * FROM array_index_op_test WHERE i && '{17}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
53 | {38,17} | {AAAAAAAAAAA21658}
65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
(8 rows)
SELECT * FROM array_index_op_test WHERE i @> '{32,17}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
(3 rows)
SELECT * FROM array_index_op_test WHERE i && '{32,17}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
SELECT * FROM array_index_op_test WHERE t @> '{}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 | {92,75,71,52,64,83} | {AAAAAAAA44066,AAAAAA1059,AAAAAAAAAAA176,AAAAAAA48038}
2 | {3,6} | {AAAAAA98232,AAAAAAAA79710,AAAAAAAAAAAAAAAAA69675,AAAAAAAAAAAAAAAA55798,AAAAAAAAA12793}
3 | {37,64,95,43,3,41,13,30,11,43} | {AAAAAAAAAA48845,AAAAA75968,AAAAA95309,AAA54451,AAAAAAAAAA22292,AAAAAAA99836,A96617,AA17009,AAAAAAAAAAAAAA95246}
4 | {71,39,99,55,33,75,45} | {AAAAAAAAA53663,AAAAAAAAAAAAAAA67062,AAAAAAAAAA64777,AAA99043,AAAAAAAAAAAAAAAAAAA91804,39557}
5 | {50,42,77,50,4} | {AAAAAAAAAAAAAAAAA26540,AAAAAAA79710,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA176,AAAAA95309,AAAAAAAAAAA46154,AAAAAA66777,AAAAAAAAA27249,AAAAAAAAAA64777,AAAAAAAAAAAAAAAAAAA70104}
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
7 | {12,51,88,64,8} | {AAAAAAAAAAAAAAAAAA12591,AAAAAAAAAAAAAAAAA50407,AAAAAAAAAAAA67946}
8 | {60,84} | {AAAAAAA81898,AAAAAA1059,AAAAAAAAAAAA81511,AAAAA961,AAAAAAAAAAAAAAAA31334,AAAAA64741,AA6416,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAAAA50407}
9 | {56,52,35,27,80,44,81,22} | {AAAAAAAAAAAAAAA73034,AAAAAAAAAAAAA7929,AAAAAAA66161,AA88409,39557,A27153,AAAAAAAA9523,AAAAAAAAAAA99000}
10 | {71,5,45} | {AAAAAAAAAAA21658,AAAAAAAAAAAA21089,AAA54451,AAAAAAAAAAAAAAAAAA54141,AAAAAAAAAAAAAA28620,AAAAAAAAAAA21658,AAAAAAAAAAA74076,AAAAAAAAA27249}
11 | {41,86,74,48,22,74,47,50} | {AAAAAAAA9523,AAAAAAAAAAAA37562,AAAAAAAAAAAAAAAA14047,AAAAAAAAAAA46154,AAAA41702,AAAAAAAAAAAAAAAAA764,AAAAA62737,39557}
12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
13 | {3,52,34,23} | {AAAAAA98232,AAAA49534,AAAAAAAAAAA21658}
14 | {78,57,19} | {AAAA8857,AAAAAAAAAAAAAAA73034,AAAAAAAA81587,AAAAAAAAAAAAAAA68526,AAAAA75968,AAAAAAAAAAAAAA65909,AAAAAAAAA10012,AAAAAAAAAAAAAA65909}
15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
16 | {14,63,85,11} | {AAAAAA66777}
17 | {7,10,81,85} | {AAAAAA43678,AAAAAAA12144,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAAAAA15356}
18 | {1} | {AAAAAAAAAAA33576,AAAAA95309,64261,AAA59323,AAAAAAAAAAAAAA95246,55847,AAAAAAAAAAAA67946,AAAAAAAAAAAAAAAAAA64374}
19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
20 | {72,89,70,51,54,37,8,49,79} | {AAAAAA58494}
21 | {2,8,65,10,5,79,43} | {AAAAAAAAAAAAAAAAA88852,AAAAAAAAAAAAAAAAAAA91804,AAAAA64669,AAAAAAAAAAAAAAAA1443,AAAAAAAAAAAAAAAA23657,AAAAA12179,AAAAAAAAAAAAAAAAA88852,AAAAAAAAAAAAAAAA31334,AAAAAAAAAAAAAAAA41303,AAAAAAAAAAAAAAAAAAA85420}
22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
23 | {40,90,5,38,72,40,30,10,43,55} | {A6053,AAAAAAAAAAA6119,AA44673,AAAAAAAAAAAAAAAAA764,AA17009,AAAAA17383,AAAAA70514,AAAAA33250,AAAAA95309,AAAAAAAAAAAA37562}
24 | {94,61,99,35,48} | {AAAAAAAAAAA50956,AAAAAAAAAAA15165,AAAA85070,AAAAAAAAAAAAAAA36627,AAAAA961,AAAAAAAAAA55219}
25 | {31,1,10,11,27,79,38} | {AAAAAAAAAAAAAAAAAA59334,45449}
26 | {71,10,9,69,75} | {47735,AAAAAAA21462,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAAAAA91804,AAAAAAAAA72121,AAAAAAAAAAAAAAAAAAA1205,AAAAA41597,AAAA8857,AAAAAAAAAAAAAAAAAAA15356,AA17009}
27 | {94} | {AA6416,A6053,AAAAAAA21462,AAAAAAA57334,AAAAAAAAAAAAAAAAAA12591,AA88409,AAAAAAAAAAAAA70254}
28 | {14,33,6,34,14} | {AAAAAAAAAAAAAAA13198,AAAAAAAA69452,AAAAAAAAAAA82945,AAAAAAA12144,AAAAAAAAA72121,AAAAAAAAAA18601}
29 | {39,21} | {AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAAAAA38885,AAAA85070,AAAAAAAAAAAAAAAAAAA70104,AAAAA66674,AAAAAAAAAAAAA62007,AAAAAAAA69452,AAAAAAA1242,AAAAAAAAAAAAAAAA1729,AAAA35194}
30 | {26,81,47,91,34} | {AAAAAAAAAAAAAAAAAAA70104,AAAAAAA80240}
31 | {80,24,18,21,54} | {AAAAAAAAAAAAAAA13198,AAAAAAAAAAAAAAAAAAA70415,A27153,AAAAAAAAA53663,AAAAAAAAAAAAAAAAA50407,A68938}
32 | {58,79,82,80,67,75,98,10,41} | {AAAAAAAAAAAAAAAAAA61286,AAA54451,AAAAAAAAAAAAAAAAAAA87527,A96617,51533}
33 | {74,73} | {A85417,AAAAAAA56483,AAAAA17383,AAAAAAAAAAAAA62159,AAAAAAAAAAAA52814,AAAAAAAAAAAAA85723,AAAAAAAAAAAAAAAAAA55796}
34 | {70,45} | {AAAAAAAAAAAAAAAAAA71621,AAAAAAAAAAAAAA28620,AAAAAAAAAA55219,AAAAAAAA23648,AAAAAAAAAA22292,AAAAAAA1242}
35 | {23,40} | {AAAAAAAAAAAA52814,AAAA48949,AAAAAAAAA34727,AAAA8857,AAAAAAAAAAAAAAAAAAA62179,AAAAAAAAAAAAAAA68526,AAAAAAA99836,AAAAAAAA50094,AAAA91194,AAAAAAAAAAAAA73084}
36 | {79,82,14,52,30,5,79} | {AAAAAAAAA53663,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA89194,AA88409,AAAAAAAAAAAAAAA81326,AAAAAAAAAAAAAAAAA63050,AAAAAAAAAAAAAAAA33598}
37 | {53,11,81,39,3,78,58,64,74} | {AAAAAAAAAAAAAAAAAAA17075,AAAAAAA66161,AAAAAAAA23648,AAAAAAAAAAAAAA10611}
38 | {59,5,4,95,28} | {AAAAAAAAAAA82945,A96617,47735,AAAAA12179,AAAAA64669,AAAAAA99807,AA74433,AAAAAAAAAAAAAAAAA59387}
39 | {82,43,99,16,74} | {AAAAAAAAAAAAAAA67062,AAAAAAA57334,AAAAAAAAAAAAAA65909,A27153,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAA64777,AAAAAAAAAAAA81511,AAAAAAAAAAAAAA65909,AAAAAAAAAAAAAA28620}
40 | {34} | {AAAAAAAAAAAAAA10611,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAA31334,AAAAA70466,AAAAAAAA81587,AAAAAAA74623}
41 | {19,26,63,12,93,73,27,94} | {AAAAAAA79710,AAAAAAAAAA55219,AAAA41702,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA71621,AAAAAAAAAAAAAAAAA63050,AAAAAAA99836,AAAAAAAAAAAAAA8666}
42 | {15,76,82,75,8,91} | {AAAAAAAAAAA176,AAAAAA38063,45449,AAAAAA54032,AAAAAAA81898,AA6416,AAAAAAAAAAAAAAAAAAA62179,45449,AAAAA60038,AAAAAAAA81587}
43 | {39,87,91,97,79,28} | {AAAAAAAAAAA74076,A96617,AAAAAAAAAAAAAAAAAAA89194,AAAAAAAAAAAAAAAAAA55796,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAA67946}
44 | {40,58,68,29,54} | {AAAAAAA81898,AAAAAA66777,AAAAAA98232}
45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
46 | {53,24} | {AAAAAAAAAAA53908,AAAAAA54032,AAAAA17383,AAAA48949,AAAAAAAAAA18601,AAAAA64669,45449,AAAAAAAAAAA98051,AAAAAAAAAAAAAAAAAA71621}
47 | {98,23,64,12,75,61} | {AAA59323,AAAAA95309,AAAAAAAAAAAAAAAA31334,AAAAAAAAA27249,AAAAA17383,AAAAAAAAAAAA37562,AAAAAA1059,A84822,55847,AAAAA70466}
48 | {76,14} | {AAAAAAAAAAAAA59671,AAAAAAAAAAAAAAAAAAA91804,AAAAAA66777,AAAAAAAAAAAAAAAAAAA89194,AAAAAAAAAAAAAAA36627,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAA73084,AAAAAAA79710,AAAAAAAAAAAAAAA40402,AAAAAAAAAAAAAAAAAAA65037}
49 | {56,5,54,37,49} | {AA21643,AAAAAAAAAAA92631,AAAAAAAA81587}
50 | {20,12,37,64,93} | {AAAAAAAAAA5483,AAAAAAAAAAAAAAAAAAA1205,AA6416,AAAAAAAAAAAAAAAAA63050,AAAAAAAAAAAAAAAAAA47955}
51 | {47} | {AAAAAAAAAAAAAA96505,AAAAAAAAAAAAAAAAAA36842,AAAAA95309,AAAAAAAA81587,AA6416,AAAA91194,AAAAAA58494,AAAAAA1059,AAAAAAAA69452}
52 | {89,0} | {AAAAAAAAAAAAAAAAAA47955,AAAAAAA48038,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAAAAA73084,AAAAA70466,AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA46154,AA66862}
53 | {38,17} | {AAAAAAAAAAA21658}
54 | {70,47} | {AAAAAAAAAAAAAAAAAA54141,AAAAA40681,AAAAAAA48038,AAAAAAAAAAAAAAAA29150,AAAAA41597,AAAAAAAAAAAAAAAAAA59334,AA15322}
55 | {47,79,47,64,72,25,71,24,93} | {AAAAAAAAAAAAAAAAAA55796,AAAAA62737}
56 | {33,7,60,54,93,90,77,85,39} | {AAAAAAAAAAAAAAAAAA32918,AA42406}
57 | {23,45,10,42,36,21,9,96} | {AAAAAAAAAAAAAAAAAAA70415}
58 | {92} | {AAAAAAAAAAAAAAAA98414,AAAAAAAA23648,AAAAAAAAAAAAAAAAAA55796,AA25381,AAAAAAAAAAA6119}
59 | {9,69,46,77} | {39557,AAAAAAA89932,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAAAAAAAAA26540,AAA20874,AA6416,AAAAAAAAAAAAAAAAAA47955}
60 | {62,2,59,38,89} | {AAAAAAA89932,AAAAAAAAAAAAAAAAAAA15356,AA99927,AA17009,AAAAAAAAAAAAAAA35875}
61 | {72,2,44,95,54,54,13} | {AAAAAAAAAAAAAAAAAAA91804}
62 | {83,72,29,73} | {AAAAAAAAAAAAA15097,AAAA8857,AAAAAAAAAAAA35809,AAAAAAAAAAAA52814,AAAAAAAAAAAAAAAAAAA38885,AAAAAAAAAAAAAAAAAA24183,AAAAAA43678,A96617}
63 | {11,4,61,87} | {AAAAAAAAA27249,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAA13198,AAA20874,39557,51533,AAAAAAAAAAA53908,AAAAAAAAAAAAAA96505,AAAAAAAA78938}
64 | {26,19,34,24,81,78} | {A96617,AAAAAAAAAAAAAAAAAAA70104,A68938,AAAAAAAAAAA53908,AAAAAAAAAAAAAAA453,AA17009,AAAAAAA80240}
65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
66 | {31,23,70,52,4,33,48,25} | {AAAAAAAAAAAAAAAAA69675,AAAAAAAA50094,AAAAAAAAAAA92631,AAAA35194,39557,AAAAAAA99836}
67 | {31,94,7,10} | {AAAAAA38063,A96617,AAAA35194,AAAAAAAAAAAA67946}
68 | {90,43,38} | {AA75092,AAAAAAAAAAAAAAAAA69675,AAAAAAAAAAA92631,AAAAAAAAA10012,AAAAAAAAAAAAA7929,AA21643}
69 | {67,35,99,85,72,86,44} | {AAAAAAAAAAAAAAAAAAA1205,AAAAAAAA50094,AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAAAAAAA47955}
70 | {56,70,83} | {AAAA41702,AAAAAAAAAAA82945,AA21643,AAAAAAAAAAA99000,A27153,AA25381,AAAAAAAAAAAAAA96505,AAAAAAA1242}
71 | {74,26} | {AAAAAAAAAAA50956,AA74433,AAAAAAA21462,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAA36627,AAAAAAAAAAAAA70254,AAAAAAAAAA43419,39557}
72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407}
73 | {88,25,96,78,65,15,29,19} | {AAA54451,AAAAAAAAA27249,AAAAAAA9228,AAAAAAAAAAAAAAA67062,AAAAAAAAAAAAAAAAAAA70415,AAAAA17383,AAAAAAAAAAAAAAAA33598}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
75 | {12,96,83,24,71,89,55} | {AAAA48949,AAAAAAAA29716,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAAA67946,AAAAAAAAAAAAAAAA29150,AAA28075,AAAAAAAAAAAAAAAAA43052}
76 | {92,55,10,7} | {AAAAAAAAAAAAAAA67062}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
78 | {55,89,44,84,34} | {AAAAAAAAAAA6119,AAAAAAAAAAAAAA8666,AA99927,AA42406,AAAAAAA81898,AAAAAAA9228,AAAAAAAAAAA92631,AA21643,AAAAAAAAAAAAAA28620}
79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
80 | {74,89,44,80,0} | {AAAA35194,AAAAAAAA79710,AAA20874,AAAAAAAAAAAAAAAAAAA70104,AAAAAAAAAAAAA73084,AAAAAAA57334,AAAAAAA9228,AAAAAAAAAAAAA62007}
81 | {63,77,54,48,61,53,97} | {AAAAAAAAAAAAAAA81326,AAAAAAAAAA22292,AA25381,AAAAAAAAAAA74076,AAAAAAA81898,AAAAAAAAA72121}
82 | {34,60,4,79,78,16,86,89,42,50} | {AAAAA40681,AAAAAAAAAAAAAAAAAA12591,AAAAAAA80240,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA70104}
83 | {14,10} | {AAAAAAAAAA22292,AAAAAAAAAAAAA70254,AAAAAAAAAAA6119}
84 | {11,83,35,13,96,94} | {AAAAA95309,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAAAAA24183}
85 | {39,60} | {AAAAAAAAAAAAAAAA55798,AAAAAAAAAA22292,AAAAAAA66161,AAAAAAA21462,AAAAAAAAAAAAAAAAAA12591,55847,AAAAAA98232,AAAAAAAAAAA46154}
86 | {33,81,72,74,45,36,82} | {AAAAAAAA81587,AAAAAAAAAAAAAA96505,45449,AAAA80176}
87 | {57,27,50,12,97,68} | {AAAAAAAAAAAAAAAAA26540,AAAAAAAAA10012,AAAAAAAAAAAA35809,AAAAAAAAAAAAAAAA29150,AAAAAAAAAAA82945,AAAAAA66777,31228,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAA96505}
88 | {41,90,77,24,6,24} | {AAAA35194,AAAA35194,AAAAAAA80240,AAAAAAAAAAA46154,AAAAAA58494,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA59334,AAAAAAAAAAAAAAAAAAA91804,AA74433}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
90 | {88,75} | {AAAAA60038,AAAAAAAA23648,AAAAAAAAAAA99000,AAAA41702,AAAAAAAAAAAAA22860,AAAAAAAAAAAAAAA68526}
91 | {78} | {AAAAAAAAAAAAA62007,AAA99043}
92 | {85,63,49,45} | {AAAAAAA89932,AAAAAAAAAAAAA22860,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAAA21089}
93 | {11} | {AAAAAAAAAAA176,AAAAAAAAAAAAAA8666,AAAAAAAAAAAAAAA453,AAAAAAAAAAAAA85723,A68938,AAAAAAAAAAAAA9821,AAAAAAA48038,AAAAAAAAAAAAAAAAA59387,AA99927,AAAAA17383}
94 | {98,9,85,62,88,91,60,61,38,86} | {AAAAAAAA81587,AAAAA17383,AAAAAAAA81587}
95 | {47,77} | {AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA74076,AAAAAAAAAA18107,AAAAA40681,AAAAAAAAAAAAAAA35875,AAAAA60038,AAAAAAA56483}
96 | {23,97,43} | {AAAAAAAAAA646,A87088}
97 | {54,2,86,65} | {47735,AAAAAAA99836,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAA29150,AAAAAAA80240,AAAAAAAAAAAAAAAA98414,AAAAAAA56483,AAAAAAAAAAAAAAAA29150,AAAAAAA39692,AA21643}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
99 | {37,86} | {AAAAAAAAAAAAAAAAAA32918,AAAAA70514,AAAAAAAAA10012,AAAAAAAAAAAAAAAAA59387,AAAAAAAAAA64777,AAAAAAAAAAAAAAAAAAA15356}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(11 rows)
101 | {} | {}
102 | {NULL} | {NULL}
(102 rows)
SELECT * FROM array_index_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno;
seqno | i | t
-------+---------------+----------------------------------------------------------------------------------------------------------------------------
40 | {34} | {AAAAAAAAAAAAAA10611,AAAAAAAAAAAAAAAAAAA1205,AAAAAAAAAAA50956,AAAAAAAAAAAAAAAA31334,AAAAA70466,AAAAAAAA81587,AAAAAAA74623}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
(3 rows)
SELECT * FROM array_index_op_test WHERE t && '{}' ORDER BY seqno;
seqno | i | t
-------+---+---
(0 rows)
SELECT * FROM array_index_op_test WHERE i = '{47,77}' ORDER BY seqno;
seqno | i | t
-------+---------+-----------------------------------------------------------------------------------------------------------------
95 | {47,77} | {AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA74076,AAAAAAAAAA18107,AAAAA40681,AAAAAAAAAAAAAAA35875,AAAAA60038,AAAAAAA56483}
SELECT * FROM array_index_op_test WHERE t <@ '{}' ORDER BY seqno;
seqno | i | t
-------+----+----
101 | {} | {}
(1 row)
-- And try it with a multicolumn GIN index
DROP INDEX intarrayidx, textarrayidx;
CREATE INDEX botharrayidx ON array_index_op_test USING gin (i, t);
SET enable_seqscan = OFF;
SET enable_indexscan = ON;
SET enable_bitmapscan = OFF;
SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@ -885,64 +1091,22 @@ SELECT * FROM array_index_op_test WHERE i && '{32}' AND t @> '{AAAAAAA80240}' OR
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(1 row)
SET enable_indexscan = OFF;
SET enable_bitmapscan = ON;
SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAA80240}' ORDER BY seqno;
seqno | i | t
-------+--------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------
19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
30 | {26,81,47,91,34} | {AAAAAAAAAAAAAAAAAAA70104,AAAAAAA80240}
64 | {26,19,34,24,81,78} | {A96617,AAAAAAAAAAAAAAAAAAA70104,A68938,AAAAAAAAAAA53908,AAAAAAAAAAAAAAA453,AA17009,AAAAAAA80240}
82 | {34,60,4,79,78,16,86,89,42,50} | {AAAAA40681,AAAAAAAAAAAAAAAAAA12591,AAAAAAA80240,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA70104}
88 | {41,90,77,24,6,24} | {AAAA35194,AAAA35194,AAAAAAA80240,AAAAAAAAAAA46154,AAAAAA58494,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA59334,AAAAAAAAAAAAAAAAAAA91804,AA74433}
97 | {54,2,86,65} | {47735,AAAAAAA99836,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAA29150,AAAAAAA80240,AAAAAAAAAAAAAAAA98414,AAAAAAA56483,AAAAAAAAAAAAAAAA29150,AAAAAAA39692,AA21643}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(7 rows)
SELECT * FROM array_index_op_test WHERE t && '{AAAAAAA80240}' ORDER BY seqno;
seqno | i | t
-------+--------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------
19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
30 | {26,81,47,91,34} | {AAAAAAAAAAAAAAAAAAA70104,AAAAAAA80240}
64 | {26,19,34,24,81,78} | {A96617,AAAAAAAAAAAAAAAAAAA70104,A68938,AAAAAAAAAAA53908,AAAAAAAAAAAAAAA453,AA17009,AAAAAAA80240}
82 | {34,60,4,79,78,16,86,89,42,50} | {AAAAA40681,AAAAAAAAAAAAAAAAAA12591,AAAAAAA80240,AAAAAAAAAAAAAAAA55798,AAAAAAAAAAAAAAAAAAA70104}
88 | {41,90,77,24,6,24} | {AAAA35194,AAAA35194,AAAAAAA80240,AAAAAAAAAAA46154,AAAAAA58494,AAAAAAAAAAAAAAAAAAA17075,AAAAAAAAAAAAAAAAAA59334,AAAAAAAAAAAAAAAAAAA91804,AA74433}
97 | {54,2,86,65} | {47735,AAAAAAA99836,AAAAAAAAAAAAAAAAA6897,AAAAAAAAAAAAAAAA29150,AAAAAAA80240,AAAAAAAAAAAAAAAA98414,AAAAAAA56483,AAAAAAAAAAAAAAAA29150,AAAAAAA39692,AA21643}
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(7 rows)
SELECT * FROM array_index_op_test WHERE i @> '{32}' AND t && '{AAAAAAA80240}' ORDER BY seqno;
seqno | i | t
-------+-----------------------------+------------------------------------------------------------------------------
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
SELECT * FROM array_index_op_test WHERE t = '{}' ORDER BY seqno;
seqno | i | t
-------+----+----
101 | {} | {}
(1 row)
SELECT * FROM array_index_op_test WHERE i && '{32}' AND t @> '{AAAAAAA80240}' ORDER BY seqno;
seqno | i | t
-------+-----------------------------+------------------------------------------------------------------------------
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
seqno | i | t
-------+--------+--------
102 | {NULL} | {NULL}
(1 row)
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
seqno | i | t
-------+----+----
101 | {} | {}
(1 row)
RESET enable_seqscan;

View File

@ -203,6 +203,14 @@ SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{32,17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32,17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i = '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
@ -211,6 +219,10 @@ SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t <@ '{AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t = '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t <@ '{}' ORDER BY seqno;
-- array casts
SELECT ARRAY[1,2,3]::text[]::int[]::float8[] AS "{1,2,3}";

View File

@ -249,13 +249,18 @@ RESET enable_bitmapscan;
--
-- GIN over int[] and text[]
--
-- Note: GIN currently supports only bitmap scans, not plain indexscans
--
SET enable_seqscan = OFF;
SET enable_indexscan = ON;
SET enable_bitmapscan = OFF;
SET enable_indexscan = OFF;
SET enable_bitmapscan = ON;
CREATE INDEX intarrayidx ON array_index_op_test USING gin (i);
explain (costs off)
SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i @> '{17}' ORDER BY seqno;
@ -264,9 +269,20 @@ SELECT * FROM array_index_op_test WHERE i @> '{32,17}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i && '{32,17}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i = '{47,77}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i = '{}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i @> '{}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i && '{}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i <@ '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
CREATE INDEX textarrayidx ON array_index_op_test USING gin (t);
explain (costs off)
SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
@ -275,19 +291,10 @@ SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORD
SELECT * FROM array_index_op_test WHERE t && '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t <@ '{AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t = '{AAAAAAAAAA646,A87088}' ORDER BY seqno;
-- Repeat some of the above tests but exercising bitmapscans instead
SET enable_indexscan = OFF;
SET enable_bitmapscan = ON;
SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i @> '{17}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i && '{17}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i @> '{32,17}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i && '{32,17}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i = '{47,77}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t = '{}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t @> '{}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t && '{}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t <@ '{}' ORDER BY seqno;
-- And try it with a multicolumn GIN index
@ -295,26 +302,15 @@ DROP INDEX intarrayidx, textarrayidx;
CREATE INDEX botharrayidx ON array_index_op_test USING gin (i, t);
SET enable_seqscan = OFF;
SET enable_indexscan = ON;
SET enable_bitmapscan = OFF;
SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAA80240}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t && '{AAAAAAA80240}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i @> '{32}' AND t && '{AAAAAAA80240}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i && '{32}' AND t @> '{AAAAAAA80240}' ORDER BY seqno;
SET enable_indexscan = OFF;
SET enable_bitmapscan = ON;
SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAA80240}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t && '{AAAAAAA80240}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i @> '{32}' AND t && '{AAAAAAA80240}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE i && '{32}' AND t @> '{AAAAAAA80240}' ORDER BY seqno;
SELECT * FROM array_index_op_test WHERE t = '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
RESET enable_seqscan;
RESET enable_indexscan;