Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Postgres FD Implementation
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Abuhujair Javed
Postgres FD Implementation
Commits
27cb66fd
Commit
27cb66fd
authored
Jul 11, 2008
by
Tom Lane
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Multi-column GIN indexes. Teodor Sigaev
parent
2d6599f4
Changes
15
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
468 additions
and
185 deletions
+468
-185
doc/src/sgml/indices.sgml
doc/src/sgml/indices.sgml
+22
-15
doc/src/sgml/ref/create_index.sgml
doc/src/sgml/ref/create_index.sgml
+3
-3
src/backend/access/gin/ginbulk.c
src/backend/access/gin/ginbulk.c
+21
-17
src/backend/access/gin/ginentrypage.c
src/backend/access/gin/ginentrypage.c
+33
-27
src/backend/access/gin/ginget.c
src/backend/access/gin/ginget.c
+21
-12
src/backend/access/gin/gininsert.c
src/backend/access/gin/gininsert.c
+32
-27
src/backend/access/gin/ginscan.c
src/backend/access/gin/ginscan.c
+12
-12
src/backend/access/gin/ginutil.c
src/backend/access/gin/ginutil.c
+124
-37
src/backend/access/gin/ginvacuum.c
src/backend/access/gin/ginvacuum.c
+6
-5
src/backend/access/gin/ginxlog.c
src/backend/access/gin/ginxlog.c
+2
-2
src/include/access/gin.h
src/include/access/gin.h
+35
-22
src/include/catalog/catversion.h
src/include/catalog/catversion.h
+2
-2
src/include/catalog/pg_am.h
src/include/catalog/pg_am.h
+2
-2
src/test/regress/expected/create_index.out
src/test/regress/expected/create_index.out
+125
-1
src/test/regress/sql/create_index.sql
src/test/regress/sql/create_index.sql
+28
-1
No files found.
doc/src/sgml/indices.sgml
View file @
27cb66fd
<!-- $PostgreSQL: pgsql/doc/src/sgml/indices.sgml,v 1.7
3 2008/05/27 00:13:0
8 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/indices.sgml,v 1.7
4 2008/07/11 21:06:2
8 tgl Exp $ -->
<chapter id="indexes">
<title id="indexes-title">Indexes</title>
...
...
@@ -198,7 +198,7 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
after a database crash.
For these reasons, hash index use is presently discouraged.
</para>
</note>
</note>
<para>
<indexterm>
...
...
@@ -250,9 +250,9 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
</indexterm>
GIN indexes are inverted indexes which can handle values that contain more
than one key, arrays for example. Like GiST, GIN can support
many different user-defined indexing strategies and the particular
operators with which a GIN index can be used vary depending on the
indexing strategy.
many different user-defined indexing strategies and the particular
operators with which a GIN index can be used vary depending on the
indexing strategy.
As an example, the standard distribution of
<productname>PostgreSQL</productname> includes GIN operator classes
for one-dimensional arrays, which support indexed
...
...
@@ -306,7 +306,7 @@ CREATE INDEX test2_mm_idx ON test2 (major, minor);
</para>
<para>
Currently, only the B-tree
and GiST
index types support multicolumn
Currently, only the B-tree
, GiST and GIN
index types support multicolumn
indexes. Up to 32 columns can be specified. (This limit can be
altered when building <productname>PostgreSQL</productname>; see the
file <filename>pg_config_manual.h</filename>.)
...
...
@@ -336,14 +336,21 @@ CREATE INDEX test2_mm_idx ON test2 (major, minor);
<para>
A multicolumn GiST index can be used with query conditions that
involve any subset of the index's columns. Conditions on additional
columns restrict the entries returned by the index, but the condition on
the first column is the most important one for determining how much of
the index needs to be scanned. A GiST index will be relatively
ineffective if its first column has only a few distinct values, even if
involve any subset of the index's columns. Conditions on additional
columns restrict the entries returned by the index, but the condition on
the first column is the most important one for determining how much of
the index needs to be scanned. A GiST index will be relatively
ineffective if its first column has only a few distinct values, even if
there are many distinct values in additional columns.
</para>
<para>
A multicolumn GIN index can be used with query conditions that
involve any subset of the index's columns. Unlike B-tree or GiST,
index search effectiveness is the same regardless of which index column(s)
the query conditions use.
</para>
<para>
Of course, each column must be used with operators appropriate to the index
type; clauses that involve other operators will not be considered.
...
...
@@ -551,7 +558,7 @@ CREATE UNIQUE INDEX <replaceable>name</replaceable> ON <replaceable>table</repla
<para>
<productname>PostgreSQL</productname> automatically creates a unique
index when a unique constraint or a primary key is defined for a table.
The index covers the columns that make up the primary key or unique
The index covers the columns that make up the primary key or unique
columns (a multicolumn index, if appropriate), and is the mechanism
that enforces the constraint.
</para>
...
...
@@ -798,9 +805,9 @@ SELECT * FROM orders WHERE order_nr = 3501;
or the index will not be recognized to be usable. Matching takes
place at query planning time, not at run time. As a result,
parameterized query clauses will not work with a partial index. For
example a prepared query with a parameter might specify
<quote>x < ?</quote> which will never imply
<quote>x < 2</quote> for all possible values of the parameter.
example a prepared query with a parameter might specify
<quote>x < ?</quote> which will never imply
<quote>x < 2</quote> for all possible values of the parameter.
</para>
<para>
...
...
doc/src/sgml/ref/create_index.sgml
View file @
27cb66fd
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_index.sgml,v 1.6
7 2008/03/16 23:57:51
tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/create_index.sgml,v 1.6
8 2008/07/11 21:06:29
tgl Exp $
PostgreSQL documentation
-->
...
...
@@ -394,7 +394,7 @@ Indexes:
</para>
<para>
Currently, only the B-tree
and GiST
index methods support
Currently, only the B-tree
, GiST and GIN
index methods support
multicolumn indexes. Up to 32 fields can be specified by default.
(This limit can be altered when building
<productname>PostgreSQL</productname>.) Only B-tree currently
...
...
@@ -423,7 +423,7 @@ Indexes:
the optional clauses <literal>ASC</>, <literal>DESC</>, <literal>NULLS
FIRST</>, and/or <literal>NULLS LAST</> can be specified to reverse
the normal sort direction of the index. Since an ordered index can be
scanned either forward or backward, it is not normally useful to create a
scanned either forward or backward, it is not normally useful to create a
single-column <literal>DESC</> index — that sort ordering is already
available with a regular index. The value of these options is that
multicolumn indexes can be created that match the sort ordering requested
...
...
src/backend/access/gin/ginbulk.c
View file @
27cb66fd
...
...
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginbulk.c,v 1.1
2 2008/06/29 21:04:01
tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginbulk.c,v 1.1
3 2008/07/11 21:06:29
tgl Exp $
*-------------------------------------------------------------------------
*/
...
...
@@ -82,16 +82,16 @@ ginInsertData(BuildAccumulator *accum, EntryAccumulator *entry, ItemPointer heap
* palloc'd space in accum.
*/
static
Datum
getDatumCopy
(
BuildAccumulator
*
accum
,
Datum
value
)
getDatumCopy
(
BuildAccumulator
*
accum
,
OffsetNumber
attnum
,
Datum
value
)
{
Form_pg_attribute
*
att
=
accum
->
ginstate
->
tupdesc
->
attrs
;
Form_pg_attribute
att
=
accum
->
ginstate
->
origTupdesc
->
attrs
[
attnum
-
1
]
;
Datum
res
;
if
(
att
[
0
]
->
attbyval
)
if
(
att
->
attbyval
)
res
=
value
;
else
{
res
=
datumCopy
(
value
,
false
,
att
[
0
]
->
attlen
);
res
=
datumCopy
(
value
,
false
,
att
->
attlen
);
accum
->
allocatedMemory
+=
GetMemoryChunkSpace
(
DatumGetPointer
(
res
));
}
return
res
;
...
...
@@ -101,7 +101,7 @@ getDatumCopy(BuildAccumulator *accum, Datum value)
* Find/store one entry from indexed value.
*/
static
void
ginInsertEntry
(
BuildAccumulator
*
accum
,
ItemPointer
heapptr
,
Datum
entry
)
ginInsertEntry
(
BuildAccumulator
*
accum
,
ItemPointer
heapptr
,
OffsetNumber
attnum
,
Datum
entry
)
{
EntryAccumulator
*
ea
=
accum
->
entries
,
*
pea
=
NULL
;
...
...
@@ -110,7 +110,7 @@ ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry)
while
(
ea
)
{
res
=
compare
Entries
(
accum
->
ginstate
,
entry
,
ea
->
value
);
res
=
compare
AttEntries
(
accum
->
ginstate
,
attnum
,
entry
,
ea
->
attnum
,
ea
->
value
);
if
(
res
==
0
)
break
;
/* found */
else
...
...
@@ -132,7 +132,8 @@ ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry)
ea
=
EAAllocate
(
accum
);
ea
->
left
=
ea
->
right
=
NULL
;
ea
->
value
=
getDatumCopy
(
accum
,
entry
);
ea
->
attnum
=
attnum
;
ea
->
value
=
getDatumCopy
(
accum
,
attnum
,
entry
);
ea
->
length
=
DEF_NPTR
;
ea
->
number
=
1
;
ea
->
shouldSort
=
FALSE
;
...
...
@@ -160,7 +161,8 @@ ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, Datum entry)
* then calls itself for each parts
*/
static
void
ginChooseElem
(
BuildAccumulator
*
accum
,
ItemPointer
heapptr
,
Datum
*
entries
,
uint32
nentry
,
ginChooseElem
(
BuildAccumulator
*
accum
,
ItemPointer
heapptr
,
OffsetNumber
attnum
,
Datum
*
entries
,
uint32
nentry
,
uint32
low
,
uint32
high
,
uint32
offset
)
{
uint32
pos
;
...
...
@@ -168,15 +170,15 @@ ginChooseElem(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, uint
pos
=
(
low
+
middle
)
>>
1
;
if
(
low
!=
middle
&&
pos
>=
offset
&&
pos
-
offset
<
nentry
)
ginInsertEntry
(
accum
,
heapptr
,
entries
[
pos
-
offset
]);
ginInsertEntry
(
accum
,
heapptr
,
attnum
,
entries
[
pos
-
offset
]);
pos
=
(
high
+
middle
+
1
)
>>
1
;
if
(
middle
+
1
!=
high
&&
pos
>=
offset
&&
pos
-
offset
<
nentry
)
ginInsertEntry
(
accum
,
heapptr
,
entries
[
pos
-
offset
]);
ginInsertEntry
(
accum
,
heapptr
,
attnum
,
entries
[
pos
-
offset
]);
if
(
low
!=
middle
)
ginChooseElem
(
accum
,
heapptr
,
entries
,
nentry
,
low
,
middle
,
offset
);
ginChooseElem
(
accum
,
heapptr
,
attnum
,
entries
,
nentry
,
low
,
middle
,
offset
);
if
(
high
!=
middle
+
1
)
ginChooseElem
(
accum
,
heapptr
,
entries
,
nentry
,
middle
+
1
,
high
,
offset
);
ginChooseElem
(
accum
,
heapptr
,
attnum
,
entries
,
nentry
,
middle
+
1
,
high
,
offset
);
}
/*
...
...
@@ -185,7 +187,8 @@ ginChooseElem(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries, uint
* next middle on left part and middle of right part.
*/
void
ginInsertRecordBA
(
BuildAccumulator
*
accum
,
ItemPointer
heapptr
,
Datum
*
entries
,
int32
nentry
)
ginInsertRecordBA
(
BuildAccumulator
*
accum
,
ItemPointer
heapptr
,
OffsetNumber
attnum
,
Datum
*
entries
,
int32
nentry
)
{
uint32
i
,
nbit
=
0
,
...
...
@@ -201,8 +204,8 @@ ginInsertRecordBA(BuildAccumulator *accum, ItemPointer heapptr, Datum *entries,
nbit
=
1
<<
nbit
;
offset
=
(
nbit
-
nentry
)
/
2
;
ginInsertEntry
(
accum
,
heapptr
,
entries
[(
nbit
>>
1
)
-
offset
]);
ginChooseElem
(
accum
,
heapptr
,
entries
,
nentry
,
0
,
nbit
,
offset
);
ginInsertEntry
(
accum
,
heapptr
,
attnum
,
entries
[(
nbit
>>
1
)
-
offset
]);
ginChooseElem
(
accum
,
heapptr
,
attnum
,
entries
,
nentry
,
0
,
nbit
,
offset
);
}
static
int
...
...
@@ -259,7 +262,7 @@ walkTree(BuildAccumulator *accum)
}
ItemPointerData
*
ginGetEntry
(
BuildAccumulator
*
accum
,
Datum
*
value
,
uint32
*
n
)
ginGetEntry
(
BuildAccumulator
*
accum
,
OffsetNumber
*
attnum
,
Datum
*
value
,
uint32
*
n
)
{
EntryAccumulator
*
entry
;
ItemPointerData
*
list
;
...
...
@@ -299,6 +302,7 @@ ginGetEntry(BuildAccumulator *accum, Datum *value, uint32 *n)
return
NULL
;
*
n
=
entry
->
number
;
*
attnum
=
entry
->
attnum
;
*
value
=
entry
->
value
;
list
=
entry
->
list
;
...
...
src/backend/access/gin/ginentrypage.c
View file @
27cb66fd
...
...
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginentrypage.c,v 1.1
6 2008/06/19 00:46:03 alvherre
Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginentrypage.c,v 1.1
7 2008/07/11 21:06:29 tgl
Exp $
*-------------------------------------------------------------------------
*/
...
...
@@ -38,14 +38,27 @@
* - ItemPointerGetBlockNumber(&itup->t_tid) contains block number of
* root of posting tree
* - ItemPointerGetOffsetNumber(&itup->t_tid) contains magic number GIN_TREE_POSTING
*
* Storage of attributes of tuple are different for single and multicolumn index.
* For single-column index tuple stores only value to be indexed and for
* multicolumn variant it stores two attributes: column number of value and value.
*/
IndexTuple
GinFormTuple
(
GinState
*
ginstate
,
Datum
key
,
ItemPointerData
*
ipd
,
uint32
nipd
)
GinFormTuple
(
GinState
*
ginstate
,
OffsetNumber
attnum
,
Datum
key
,
ItemPointerData
*
ipd
,
uint32
nipd
)
{
bool
isnull
=
FALSE
;
bool
isnull
[
2
]
=
{
FALSE
,
FALSE
}
;
IndexTuple
itup
;
itup
=
index_form_tuple
(
ginstate
->
tupdesc
,
&
key
,
&
isnull
);
if
(
ginstate
->
oneCol
)
itup
=
index_form_tuple
(
ginstate
->
origTupdesc
,
&
key
,
isnull
);
else
{
Datum
datums
[
2
];
datums
[
0
]
=
UInt16GetDatum
(
attnum
);
datums
[
1
]
=
key
;
itup
=
index_form_tuple
(
ginstate
->
tupdesc
[
attnum
-
1
],
datums
,
isnull
);
}
GinSetOrigSizePosting
(
itup
,
IndexTupleSize
(
itup
));
...
...
@@ -88,28 +101,20 @@ getRightMostTuple(Page page)
return
(
IndexTuple
)
PageGetItem
(
page
,
PageGetItemId
(
page
,
maxoff
));
}
Datum
ginGetHighKey
(
GinState
*
ginstate
,
Page
page
)
{
IndexTuple
itup
;
bool
isnull
;
itup
=
getRightMostTuple
(
page
);
return
index_getattr
(
itup
,
FirstOffsetNumber
,
ginstate
->
tupdesc
,
&
isnull
);
}
static
bool
entryIsMoveRight
(
GinBtree
btree
,
Page
page
)
{
Datum
highkey
;
IndexTuple
itup
;
if
(
GinPageRightMost
(
page
))
return
FALSE
;
highkey
=
ginGetHighKey
(
btree
->
ginstate
,
page
);
itup
=
getRightMostTuple
(
page
);
if
(
compareEntries
(
btree
->
ginstate
,
btree
->
entryValue
,
highkey
)
>
0
)
if
(
compareAttEntries
(
btree
->
ginstate
,
btree
->
entryAttnum
,
btree
->
entryValue
,
gintuple_get_attrnum
(
btree
->
ginstate
,
itup
),
gin_index_getattr
(
btree
->
ginstate
,
itup
))
>
0
)
return
TRUE
;
return
FALSE
;
...
...
@@ -154,11 +159,11 @@ entryLocateEntry(GinBtree btree, GinBtreeStack *stack)
result
=
-
1
;
else
{
bool
isnull
;
itup
=
(
IndexTuple
)
PageGetItem
(
page
,
PageGetItemId
(
page
,
mid
));
result
=
compareEntries
(
btree
->
ginstate
,
btree
->
entryValue
,
index_getattr
(
itup
,
FirstOffsetNumber
,
btree
->
ginstate
->
tupdesc
,
&
isnull
));
result
=
compareAttEntries
(
btree
->
ginstate
,
btree
->
entryAttnum
,
btree
->
entryValue
,
gintuple_get_attrnum
(
btree
->
ginstate
,
itup
),
gin_index_getattr
(
btree
->
ginstate
,
itup
));
}
if
(
result
==
0
)
...
...
@@ -217,13 +222,13 @@ entryLocateLeafEntry(GinBtree btree, GinBtreeStack *stack)
while
(
high
>
low
)
{
OffsetNumber
mid
=
low
+
((
high
-
low
)
/
2
);
bool
isnull
;
int
result
;
itup
=
(
IndexTuple
)
PageGetItem
(
page
,
PageGetItemId
(
page
,
mid
));
result
=
compareEntries
(
btree
->
ginstate
,
btree
->
entryValue
,
index_getattr
(
itup
,
FirstOffsetNumber
,
btree
->
ginstate
->
tupdesc
,
&
isnull
));
result
=
compareAttEntries
(
btree
->
ginstate
,
btree
->
entryAttnum
,
btree
->
entryValue
,
gintuple_get_attrnum
(
btree
->
ginstate
,
itup
),
gin_index_getattr
(
btree
->
ginstate
,
itup
));
if
(
result
==
0
)
{
stack
->
off
=
mid
;
...
...
@@ -587,7 +592,7 @@ entryFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf)
}
void
prepareEntryScan
(
GinBtree
btree
,
Relation
index
,
Datum
value
,
GinState
*
ginstate
)
prepareEntryScan
(
GinBtree
btree
,
Relation
index
,
OffsetNumber
attnum
,
Datum
value
,
GinState
*
ginstate
)
{
memset
(
btree
,
0
,
sizeof
(
GinBtreeData
));
...
...
@@ -603,6 +608,7 @@ prepareEntryScan(GinBtree btree, Relation index, Datum value, GinState *ginstate
btree
->
index
=
index
;
btree
->
ginstate
=
ginstate
;
btree
->
entryAttnum
=
attnum
;
btree
->
entryValue
=
value
;
btree
->
isDelete
=
FALSE
;
...
...
src/backend/access/gin/ginget.c
View file @
27cb66fd
...
...
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginget.c,v 1.1
7 2008/06/19 00:46:03 alvherre
Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginget.c,v 1.1
8 2008/07/11 21:06:29 tgl
Exp $
*-------------------------------------------------------------------------
*/
...
...
@@ -138,7 +138,6 @@ computePartialMatchList( GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry
Page
page
;
IndexTuple
itup
;
Datum
idatum
;
bool
isnull
;
int32
cmp
;
scanEntry
->
partialMatch
=
tbm_create
(
work_mem
*
1024L
);
...
...
@@ -153,8 +152,15 @@ computePartialMatchList( GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry
page
=
BufferGetPage
(
stack
->
buffer
);
itup
=
(
IndexTuple
)
PageGetItem
(
page
,
PageGetItemId
(
page
,
stack
->
off
));
idatum
=
index_getattr
(
itup
,
1
,
btree
->
ginstate
->
tupdesc
,
&
isnull
);
Assert
(
!
isnull
);
/*
* If tuple stores another attribute then stop scan
*/
if
(
gintuple_get_attrnum
(
btree
->
ginstate
,
itup
)
!=
scanEntry
->
attnum
)
return
true
;
idatum
=
gin_index_getattr
(
btree
->
ginstate
,
itup
);
/*----------
* Check of partial match.
...
...
@@ -163,7 +169,7 @@ computePartialMatchList( GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry
* case cmp < 0 => not match and continue scan
*----------
*/
cmp
=
DatumGetInt32
(
FunctionCall3
(
&
btree
->
ginstate
->
comparePartialFn
,
cmp
=
DatumGetInt32
(
FunctionCall3
(
&
btree
->
ginstate
->
comparePartialFn
[
scanEntry
->
attnum
-
1
]
,
scanEntry
->
entry
,
idatum
,
UInt16GetDatum
(
scanEntry
->
strategy
)));
...
...
@@ -182,8 +188,8 @@ computePartialMatchList( GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry
Datum
newDatum
,
savedDatum
=
datumCopy
(
idatum
,
btree
->
ginstate
->
tupdesc
->
attrs
[
0
]
->
attbyval
,
btree
->
ginstate
->
tupdesc
->
attrs
[
0
]
->
attlen
btree
->
ginstate
->
origTupdesc
->
attrs
[
scanEntry
->
attnum
-
1
]
->
attbyval
,
btree
->
ginstate
->
origTupdesc
->
attrs
[
scanEntry
->
attnum
-
1
]
->
attlen
);
/*
* We should unlock current page (but not unpin) during
...
...
@@ -220,12 +226,15 @@ computePartialMatchList( GinBtreeData *btree, GinBtreeStack *stack, GinScanEntry
page
=
BufferGetPage
(
stack
->
buffer
);
itup
=
(
IndexTuple
)
PageGetItem
(
page
,
PageGetItemId
(
page
,
stack
->
off
));
newDatum
=
index_getattr
(
itup
,
FirstOffsetNumber
,
btree
->
ginstate
->
tupdesc
,
&
isnull
);
newDatum
=
gin_index_getattr
(
btree
->
ginstate
,
itup
);
if
(
gintuple_get_attrnum
(
btree
->
ginstate
,
itup
)
!=
scanEntry
->
attnum
)
elog
(
ERROR
,
"lost saved point in index"
);
/* must not happen !!! */
if
(
compareEntries
(
btree
->
ginstate
,
newDatum
,
savedDatum
)
==
0
)
if
(
compareEntries
(
btree
->
ginstate
,
scanEntry
->
attnum
,
newDatum
,
savedDatum
)
==
0
)
{
/* Found! */
if
(
btree
->
ginstate
->
tupdesc
->
attrs
[
0
]
->
attbyval
==
false
)
if
(
btree
->
ginstate
->
origTupdesc
->
attrs
[
scanEntry
->
attnum
-
1
]
->
attbyval
==
false
)
pfree
(
DatumGetPointer
(
savedDatum
)
);
break
;
}
...
...
@@ -270,7 +279,7 @@ startScanEntry(Relation index, GinState *ginstate, GinScanEntry entry)
* or just store posting list in memory
*/
prepareEntryScan
(
&
btreeEntry
,
index
,
entry
->
entry
,
ginstate
);
prepareEntryScan
(
&
btreeEntry
,
index
,
entry
->
attnum
,
entry
->
entry
,
ginstate
);
btreeEntry
.
searchMode
=
TRUE
;
stackEntry
=
ginFindLeafPage
(
&
btreeEntry
,
NULL
);
page
=
BufferGetPage
(
stackEntry
->
buffer
);
...
...
@@ -705,7 +714,7 @@ keyGetItem(Relation index, GinState *ginstate, MemoryContext tempCtx,
*
keyrecheck
=
true
;
oldCtx
=
MemoryContextSwitchTo
(
tempCtx
);
res
=
DatumGetBool
(
FunctionCall4
(
&
ginstate
->
consistentFn
,
res
=
DatumGetBool
(
FunctionCall4
(
&
ginstate
->
consistentFn
[
key
->
attnum
-
1
]
,
PointerGetDatum
(
key
->
entryRes
),
UInt16GetDatum
(
key
->
strategy
),
key
->
query
,
...
...
src/backend/access/gin/gininsert.c
View file @
27cb66fd
...
...
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.1
3 2008/05/16 01:27:06
tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/gininsert.c,v 1.1
4 2008/07/11 21:06:29
tgl Exp $
*-------------------------------------------------------------------------
*/
...
...
@@ -99,9 +99,9 @@ static IndexTuple
addItemPointersToTuple
(
Relation
index
,
GinState
*
ginstate
,
GinBtreeStack
*
stack
,
IndexTuple
old
,
ItemPointerData
*
items
,
uint32
nitem
,
bool
isBuild
)
{
bool
isnull
;
Datum
key
=
index_getattr
(
old
,
FirstOffsetNumber
,
ginstate
->
tupdesc
,
&
isnull
);
IndexTuple
res
=
GinFormTuple
(
ginstate
,
key
,
NULL
,
nitem
+
GinGetNPosting
(
old
));
Datum
key
=
gin_index_getattr
(
ginstate
,
old
)
;
OffsetNumber
attnum
=
gintuple_get_attrnum
(
ginstate
,
old
);
IndexTuple
res
=
GinFormTuple
(
ginstate
,
attnum
,
key
,
NULL
,
nitem
+
GinGetNPosting
(
old
));
if
(
res
)
{
...
...
@@ -119,7 +119,7 @@ addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack,
GinPostingTreeScan
*
gdi
;
/* posting list becomes big, so we need to make posting's tree */
res
=
GinFormTuple
(
ginstate
,
key
,
NULL
,
0
);
res
=
GinFormTuple
(
ginstate
,
attnum
,
key
,
NULL
,
0
);
postingRoot
=
createPostingTree
(
index
,
GinGetPosting
(
old
),
GinGetNPosting
(
old
));
GinSetPostingTree
(
res
,
postingRoot
);
...
...
@@ -138,14 +138,15 @@ addItemPointersToTuple(Relation index, GinState *ginstate, GinBtreeStack *stack,
* Inserts only one entry to the index, but it can add more than 1 ItemPointer.
*/
static
void
ginEntryInsert
(
Relation
index
,
GinState
*
ginstate
,
Datum
value
,
ItemPointerData
*
items
,
uint32
nitem
,
bool
isBuild
)
ginEntryInsert
(
Relation
index
,
GinState
*
ginstate
,
OffsetNumber
attnum
,
Datum
value
,
ItemPointerData
*
items
,
uint32
nitem
,
bool
isBuild
)
{
GinBtreeData
btree
;
GinBtreeStack
*
stack
;
IndexTuple
itup
;
Page
page
;
prepareEntryScan
(
&
btree
,
index
,
value
,
ginstate
);
prepareEntryScan
(
&
btree
,
index
,
attnum
,
value
,
ginstate
);
stack
=
ginFindLeafPage
(
&
btree
,
NULL
);
page
=
BufferGetPage
(
stack
->
buffer
);
...
...
@@ -180,7 +181,7 @@ ginEntryInsert(Relation index, GinState *ginstate, Datum value, ItemPointerData
else
{
/* We suppose, that tuple can store at list one itempointer */
itup
=
GinFormTuple
(
ginstate
,
value
,
items
,
1
);
itup
=
GinFormTuple
(
ginstate
,
attnum
,
value
,
items
,
1
);
if
(
itup
==
NULL
||
IndexTupleSize
(
itup
)
>=
GinMaxItemSize
)
elog
(
ERROR
,
"huge tuple"
);
...
...
@@ -203,21 +204,21 @@ ginEntryInsert(Relation index, GinState *ginstate, Datum value, ItemPointerData
* Function isn't used during normal insert
*/
static
uint32
ginHeapTupleBulkInsert
(
GinBuildState
*
buildstate
,
Datum
value
,
ItemPointer
heapptr
)
ginHeapTupleBulkInsert
(
GinBuildState
*
buildstate
,
OffsetNumber
attnum
,
Datum
value
,
ItemPointer
heapptr
)
{
Datum
*
entries
;
int32
nentries
;
MemoryContext
oldCtx
;
oldCtx
=
MemoryContextSwitchTo
(
buildstate
->
funcCtx
);
entries
=
extractEntriesSU
(
buildstate
->
accum
.
ginstate
,
value
,
&
nentries
);
entries
=
extractEntriesSU
(
buildstate
->
accum
.
ginstate
,
attnum
,
value
,
&
nentries
);
MemoryContextSwitchTo
(
oldCtx
);
if
(
nentries
==
0
)
/* nothing to insert */
return
0
;
ginInsertRecordBA
(
&
buildstate
->
accum
,
heapptr
,
entries
,
nentries
);
ginInsertRecordBA
(
&
buildstate
->
accum
,
heapptr
,
attnum
,
entries
,
nentries
);
MemoryContextReset
(
buildstate
->
funcCtx
);
...
...
@@ -230,13 +231,15 @@ ginBuildCallback(Relation index, HeapTuple htup, Datum *values,
{
GinBuildState
*
buildstate
=
(
GinBuildState
*
)
state
;
MemoryContext
oldCtx
;
if
(
*
isnull
)
return
;
int
i
;
oldCtx
=
MemoryContextSwitchTo
(
buildstate
->
tmpCtx
);
buildstate
->
indtuples
+=
ginHeapTupleBulkInsert
(
buildstate
,
*
values
,
&
htup
->
t_self
);
for
(
i
=
0
;
i
<
buildstate
->
ginstate
.
origTupdesc
->
natts
;
i
++
)
if
(
!
isnull
[
i
]
)
buildstate
->
indtuples
+=
ginHeapTupleBulkInsert
(
buildstate
,
(
OffsetNumber
)(
i
+
1
),
values
[
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
)
...
...
@@ -244,12 +247,13 @@ ginBuildCallback(Relation index, HeapTuple htup, Datum *values,
ItemPointerData
*
list
;
Datum
entry
;
uint32
nlist
;
OffsetNumber
attnum
;
while
((
list
=
ginGetEntry
(
&
buildstate
->
accum
,
&
entry
,
&
nlist
))
!=
NULL
)
while
((
list
=
ginGetEntry
(
&
buildstate
->
accum
,
&
attnum
,
&
entry
,
&
nlist
))
!=
NULL
)
{
/* there could be many entries, so be willing to abort here */
CHECK_FOR_INTERRUPTS
();
ginEntryInsert
(
index
,
&
buildstate
->
ginstate
,
entry
,
list
,
nlist
,
TRUE
);
ginEntryInsert
(
index
,
&
buildstate
->
ginstate
,
attnum
,
entry
,
list
,
nlist
,
TRUE
);
}
MemoryContextReset
(
buildstate
->
tmpCtx
);
...
...
@@ -273,6 +277,7 @@ ginbuild(PG_FUNCTION_ARGS)
Datum
entry
;
uint32
nlist
;
MemoryContext
oldCtx
;
OffsetNumber
attnum
;
if
(
RelationGetNumberOfBlocks
(
index
)
!=
0
)
elog
(
ERROR
,
"index
\"
%s
\"
already contains data"
,
...
...
@@ -337,11 +342,11 @@ ginbuild(PG_FUNCTION_ARGS)
/* dump remaining entries to the index */
oldCtx
=
MemoryContextSwitchTo
(
buildstate
.
tmpCtx
);
while
((
list
=
ginGetEntry
(
&
buildstate
.
accum
,
&
entry
,
&
nlist
))
!=
NULL
)
while
((
list
=
ginGetEntry
(
&
buildstate
.
accum
,
&
attnum
,
&
entry
,
&
nlist
))
!=
NULL
)
{
/* there could be many entries, so be willing to abort here */
CHECK_FOR_INTERRUPTS
();
ginEntryInsert
(
index
,
&
buildstate
.
ginstate
,
entry
,
list
,
nlist
,
TRUE
);
ginEntryInsert
(
index
,
&
buildstate
.
ginstate
,
attnum
,
entry
,
list
,
nlist
,
TRUE
);
}
MemoryContextSwitchTo
(
oldCtx
);
...
...
@@ -362,20 +367,20 @@ ginbuild(PG_FUNCTION_ARGS)
* Inserts value during normal insertion
*/
static
uint32
ginHeapTupleInsert
(
Relation
index
,
GinState
*
ginstate
,
Datum
value
,
ItemPointer
item
)
ginHeapTupleInsert
(
Relation
index
,
GinState
*
ginstate
,
OffsetNumber
attnum
,
Datum
value
,
ItemPointer
item
)
{
Datum
*
entries
;
int32
i
,
nentries
;
entries
=
extractEntriesSU
(
ginstate
,
value
,
&
nentries
);
entries
=
extractEntriesSU
(
ginstate
,
attnum
,
value
,
&
nentries
);
if
(
nentries
==
0
)
/* nothing to insert */
return
0
;
for
(
i
=
0
;
i
<
nentries
;
i
++
)
ginEntryInsert
(
index
,
ginstate
,
entries
[
i
],
item
,
1
,
FALSE
);
ginEntryInsert
(
index
,
ginstate
,
attnum
,
entries
[
i
],
item
,
1
,
FALSE
);
return
nentries
;
}
...
...
@@ -395,10 +400,8 @@ gininsert(PG_FUNCTION_ARGS)
GinState
ginstate
;
MemoryContext
oldCtx
;
MemoryContext
insertCtx
;
uint32
res
;
if
(
*
isnull
)
PG_RETURN_BOOL
(
false
);
uint32
res
=
0
;
int
i
;
insertCtx
=
AllocSetContextCreate
(
CurrentMemoryContext
,
"Gin insert temporary context"
,
...
...
@@ -410,7 +413,9 @@ gininsert(PG_FUNCTION_ARGS)
initGinState
(
&
ginstate
,
index
);
res
=
ginHeapTupleInsert
(
index
,
&
ginstate
,
*
values
,
ht_ctid
);
for
(
i
=
0
;
i
<
ginstate
.
origTupdesc
->
natts
;
i
++
)
if
(
!
isnull
[
i
]
)
res
+=
ginHeapTupleInsert
(
index
,
&
ginstate
,
(
OffsetNumber
)(
i
+
1
),
values
[
i
],
ht_ctid
);
MemoryContextSwitchTo
(
oldCtx
);
MemoryContextDelete
(
insertCtx
);
...
...
src/backend/access/gin/ginscan.c
View file @
27cb66fd
...
...
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginscan.c,v 1.1
6 2008/07/04 13:21:18 teodor
Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginscan.c,v 1.1
7 2008/07/11 21:06:29 tgl
Exp $
*-------------------------------------------------------------------------
*/
...
...
@@ -36,7 +36,7 @@ ginbeginscan(PG_FUNCTION_ARGS)
}
static
void
fillScanKey
(
GinState
*
ginstate
,
GinScanKey
key
,
Datum
query
,
fillScanKey
(
GinState
*
ginstate
,
GinScanKey
key
,
OffsetNumber
attnum
,
Datum
query
,
Datum
*
entryValues
,
bool
*
partial_matches
,
uint32
nEntryValues
,
StrategyNumber
strategy
)
{
...
...
@@ -47,6 +47,7 @@ fillScanKey(GinState *ginstate, GinScanKey key, Datum query,
key
->
entryRes
=
(
bool
*
)
palloc0
(
sizeof
(
bool
)
*
nEntryValues
);
key
->
scanEntry
=
(
GinScanEntry
)
palloc
(
sizeof
(
GinScanEntryData
)
*
nEntryValues
);
key
->
strategy
=
strategy
;
key
->
attnum
=
attnum
;
key
->
query
=
query
;
key
->
firstCall
=
TRUE
;
ItemPointerSet
(
&
(
key
->
curItem
),
InvalidBlockNumber
,
InvalidOffsetNumber
);
...
...
@@ -55,19 +56,20 @@ fillScanKey(GinState *ginstate, GinScanKey key, Datum query,
{
key
->
scanEntry
[
i
].
pval
=
key
->
entryRes
+
i
;
key
->
scanEntry
[
i
].
entry
=
entryValues
[
i
];
key
->
scanEntry
[
i
].
attnum
=
attnum
;
ItemPointerSet
(
&
(
key
->
scanEntry
[
i
].
curItem
),
InvalidBlockNumber
,
InvalidOffsetNumber
);
key
->
scanEntry
[
i
].
offset
=
InvalidOffsetNumber
;
key
->
scanEntry
[
i
].
buffer
=
InvalidBuffer
;
key
->
scanEntry
[
i
].
partialMatch
=
NULL
;
key
->
scanEntry
[
i
].
list
=
NULL
;
key
->
scanEntry
[
i
].
nlist
=
0
;
key
->
scanEntry
[
i
].
isPartialMatch
=
(
ginstate
->
canPartialMatch
&&
partial_matches
)
key
->
scanEntry
[
i
].
isPartialMatch
=
(
ginstate
->
canPartialMatch
[
attnum
-
1
]
&&
partial_matches
)
?
partial_matches
[
i
]
:
false
;
/* link to the equals entry in current scan key */
key
->
scanEntry
[
i
].
master
=
NULL
;
for
(
j
=
0
;
j
<
i
;
j
++
)
if
(
compareEntries
(
ginstate
,
entryValues
[
i
],
entryValues
[
j
])
==
0
)
if
(
compareEntries
(
ginstate
,
attnum
,
entryValues
[
i
],
entryValues
[
j
])
==
0
)
{
key
->
scanEntry
[
i
].
master
=
key
->
scanEntry
+
j
;
break
;
...
...
@@ -164,19 +166,17 @@ newScanKey(IndexScanDesc scan)
int32
nEntryValues
;
bool
*
partial_matches
=
NULL
;
Assert
(
scankey
[
i
].
sk_attno
==
1
);
/* XXX can't we treat nulls by just setting isVoidRes? */
/* This would amount to assuming that all GIN operators are strict */
if
(
scankey
[
i
].
sk_flags
&
SK_ISNULL
)
elog
(
ERROR
,
"GIN doesn't support NULL as scan key"
);
entryValues
=
(
Datum
*
)
DatumGetPointer
(
FunctionCall4
(
&
so
->
ginstate
.
extractQueryFn
,
scankey
[
i
].
sk_argument
,
PointerGetDatum
(
&
nEntryValues
),
UInt16GetDatum
(
scankey
[
i
].
sk_strategy
),
PointerGetDatum
(
&
partial_matches
)));
&
so
->
ginstate
.
extractQueryFn
[
scankey
[
i
].
sk_attno
-
1
]
,
scankey
[
i
].
sk_argument
,
PointerGetDatum
(
&
nEntryValues
),
UInt16GetDatum
(
scankey
[
i
].
sk_strategy
),
PointerGetDatum
(
&
partial_matches
)));
if
(
nEntryValues
<
0
)
{
/*
...
...
@@ -194,7 +194,7 @@ newScanKey(IndexScanDesc scan)
/* full scan... */
continue
;
fillScanKey
(
&
so
->
ginstate
,
&
(
so
->
keys
[
nkeys
]),
scankey
[
i
].
sk_argument
,
fillScanKey
(
&
so
->
ginstate
,
&
(
so
->
keys
[
nkeys
]),
scankey
[
i
].
sk_a
ttno
,
scankey
[
i
].
sk_a
rgument
,
entryValues
,
partial_matches
,
nEntryValues
,
scankey
[
i
].
sk_strategy
);
nkeys
++
;
}
...
...
src/backend/access/gin/ginutil.c
View file @
27cb66fd
...
...
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.1
5 2008/05/16 16:31:01
tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.1
6 2008/07/11 21:06:29
tgl Exp $
*-------------------------------------------------------------------------
*/
...
...
@@ -16,6 +16,7 @@
#include "access/genam.h"
#include "access/gin.h"
#include "access/reloptions.h"
#include "catalog/pg_type.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/lmgr.h"
...
...
@@ -23,40 +24,116 @@
void
initGinState
(
GinState
*
state
,
Relation
index
)
{
if
(
index
->
rd_att
->
natts
!=
1
)
elog
(
ERROR
,
"numberOfAttributes %d != 1"
,
index
->
rd_att
->
natts
);
state
->
tupdesc
=
index
->
rd_att
;
fmgr_info_copy
(
&
(
state
->
compareFn
),
index_getprocinfo
(
index
,
1
,
GIN_COMPARE_PROC
),
CurrentMemoryContext
);
fmgr_info_copy
(
&
(
state
->
extractValueFn
),
index_getprocinfo
(
index
,
1
,
GIN_EXTRACTVALUE_PROC
),
CurrentMemoryContext
);
fmgr_info_copy
(
&
(
state
->
extractQueryFn
),
index_getprocinfo
(
index
,
1
,
GIN_EXTRACTQUERY_PROC
),
CurrentMemoryContext
);
fmgr_info_copy
(
&
(
state
->
consistentFn
),
index_getprocinfo
(
index
,
1
,
GIN_CONSISTENT_PROC
),
CurrentMemoryContext
);
/*
* Check opclass capability to do partial match.
*/
if
(
index_getprocid
(
index
,
1
,
GIN_COMPARE_PARTIAL_PROC
)
!=
InvalidOid
)
int
i
;
state
->
origTupdesc
=
index
->
rd_att
;
state
->
oneCol
=
(
index
->
rd_att
->
natts
==
1
)
?
true
:
false
;
for
(
i
=
0
;
i
<
index
->
rd_att
->
natts
;
i
++
)
{
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
);
fmgr_info_copy
(
&
(
state
->
compareFn
[
i
]),
index_getprocinfo
(
index
,
i
+
1
,
GIN_COMPARE_PROC
),
CurrentMemoryContext
);
fmgr_info_copy
(
&
(
state
->
extractValueFn
[
i
]),
index_getprocinfo
(
index
,
i
+
1
,
GIN_EXTRACTVALUE_PROC
),
CurrentMemoryContext
);
fmgr_info_copy
(
&
(
state
->
extractQueryFn
[
i
]),
index_getprocinfo
(
index
,
i
+
1
,
GIN_EXTRACTQUERY_PROC
),
CurrentMemoryContext
);
fmgr_info_copy
(
&
(
state
->
consistentFn
[
i
]),
index_getprocinfo
(
index
,
i
+
1
,
GIN_CONSISTENT_PROC
),
CurrentMemoryContext
);
/*
* Check opclass capability to do partial match.
*/
if
(
index_getprocid
(
index
,
i
+
1
,
GIN_COMPARE_PARTIAL_PROC
)
!=
InvalidOid
)
{
fmgr_info_copy
(
&
(
state
->
comparePartialFn
[
i
]),
index_getprocinfo
(
index
,
i
+
1
,
GIN_COMPARE_PARTIAL_PROC
),
CurrentMemoryContext
);
state
->
canPartialMatch
[
i
]
=
true
;
}
else
{
state
->
canPartialMatch
[
i
]
=
false
;
}
}
}
/*
* Extract attribute (column) number of stored entry from GIN tuple
*/
OffsetNumber
gintuple_get_attrnum
(
GinState
*
ginstate
,
IndexTuple
tuple
)
{
OffsetNumber
colN
=
FirstOffsetNumber
;
if
(
!
ginstate
->
oneCol
)
{
fmgr_info_copy
(
&
(
state
->
comparePartialFn
),
index_getprocinfo
(
index
,
1
,
GIN_COMPARE_PARTIAL_PROC
),
CurrentMemoryContext
);
Datum
res
;
bool
isnull
;
/*
* First attribute is always int16, so we can safely use any
* tuple descriptor to obtain first attribute of tuple
*/
res
=
index_getattr
(
tuple
,
FirstOffsetNumber
,
ginstate
->
tupdesc
[
0
],
&
isnull
);
Assert
(
!
isnull
);
state
->
canPartialMatch
=
true
;
colN
=
DatumGetUInt16
(
res
);
Assert
(
colN
>=
FirstOffsetNumber
&&
colN
<=
ginstate
->
origTupdesc
->
natts
);
}
return
colN
;
}
/*
* Extract stored datum from GIN tuple
*/
Datum
gin_index_getattr
(
GinState
*
ginstate
,
IndexTuple
tuple
)
{
bool
isnull
;
Datum
res
;
if
(
ginstate
->
oneCol
)
{
/*
* Single column index doesn't store attribute numbers in tuples
*/
res
=
index_getattr
(
tuple
,
FirstOffsetNumber
,
ginstate
->
origTupdesc
,
&
isnull
);
}
else
{
state
->
canPartialMatch
=
false
;
/*
* Since the datum type depends on which index column it's from,
* we must be careful to use the right tuple descriptor here.
*/
OffsetNumber
colN
=
gintuple_get_attrnum
(
ginstate
,
tuple
);
res
=
index_getattr
(
tuple
,
OffsetNumberNext
(
FirstOffsetNumber
),
ginstate
->
tupdesc
[
colN
-
1
],
&
isnull
);
}
Assert
(
!
isnull
);
return
res
;
}
/*
...
...
@@ -136,16 +213,26 @@ GinInitBuffer(Buffer b, uint32 f)
}
int
compareEntries
(
GinState
*
ginstate
,
Datum
a
,
Datum
b
)
compareEntries
(
GinState
*
ginstate
,
OffsetNumber
attnum
,
Datum
a
,
Datum
b
)
{
return
DatumGetInt32
(
FunctionCall2
(
&
ginstate
->
compareFn
,
&
ginstate
->
compareFn
[
attnum
-
1
]
,
a
,
b
)
);
}
int
compareAttEntries
(
GinState
*
ginstate
,
OffsetNumber
attnum_a
,
Datum
a
,
OffsetNumber
attnum_b
,
Datum
b
)
{
if
(
attnum_a
==
attnum_b
)
return
compareEntries
(
ginstate
,
attnum_a
,
a
,
b
);
return
(
attnum_a
<
attnum_b
)
?
-
1
:
1
;
}
typedef
struct
{
FmgrInfo
*
cmpDatumFunc
;
...
...
@@ -165,13 +252,13 @@ cmpEntries(const Datum *a, const Datum *b, cmpEntriesData *arg)
}
Datum
*
extractEntriesS
(
GinState
*
ginstate
,
Datum
value
,
int32
*
nentries
,
extractEntriesS
(
GinState
*
ginstate
,
OffsetNumber
attnum
,
Datum
value
,
int32
*
nentries
,
bool
*
needUnique
)
{
Datum
*
entries
;
entries
=
(
Datum
*
)
DatumGetPointer
(
FunctionCall2
(
&
ginstate
->
extractValueFn
,
&
ginstate
->
extractValueFn
[
attnum
-
1
]
,
value
,
PointerGetDatum
(
nentries
)
));
...
...
@@ -184,7 +271,7 @@ extractEntriesS(GinState *ginstate, Datum value, int32 *nentries,
{
cmpEntriesData
arg
;
arg
.
cmpDatumFunc
=
&
ginstate
->
compareFn
;
arg
.
cmpDatumFunc
=
&
ginstate
->
compareFn
[
attnum
-
1
]
;
arg
.
needUnique
=
needUnique
;
qsort_arg
(
entries
,
*
nentries
,
sizeof
(
Datum
),
(
qsort_arg_comparator
)
cmpEntries
,
(
void
*
)
&
arg
);
...
...
@@ -195,10 +282,10 @@ extractEntriesS(GinState *ginstate, Datum value, int32 *nentries,
Datum
*
extractEntriesSU
(
GinState
*
ginstate
,
Datum
value
,
int32
*
nentries
)
extractEntriesSU
(
GinState
*
ginstate
,
OffsetNumber
attnum
,
Datum
value
,
int32
*
nentries
)
{
bool
needUnique
;
Datum
*
entries
=
extractEntriesS
(
ginstate
,
value
,
nentries
,
Datum
*
entries
=
extractEntriesS
(
ginstate
,
attnum
,
value
,
nentries
,
&
needUnique
);
if
(
needUnique
)
...
...
@@ -210,7 +297,7 @@ extractEntriesSU(GinState *ginstate, Datum value, int32 *nentries)
while
(
ptr
-
entries
<
*
nentries
)
{
if
(
compareEntries
(
ginstate
,
*
ptr
,
*
res
)
!=
0
)
if
(
compareEntries
(
ginstate
,
attnum
,
*
ptr
,
*
res
)
!=
0
)
*
(
++
res
)
=
*
ptr
++
;
else
ptr
++
;
...
...
src/backend/access/gin/ginvacuum.c
View file @
27cb66fd
...
...
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.2
0 2008/05/12 00:00:44 alvherre
Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.2
1 2008/07/11 21:06:29 tgl
Exp $
*-------------------------------------------------------------------------
*/
...
...
@@ -515,8 +515,8 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
if
(
GinGetNPosting
(
itup
)
!=
newN
)
{
bool
isnull
;
Datum
value
;
Datum
value
;
OffsetNumber
attnum
;
/*
* Some ItemPointers was deleted, so we should remake our
...
...
@@ -544,8 +544,9 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
itup
=
(
IndexTuple
)
PageGetItem
(
tmppage
,
PageGetItemId
(
tmppage
,
i
));
}
value
=
index_getattr
(
itup
,
FirstOffsetNumber
,
gvs
->
ginstate
.
tupdesc
,
&
isnull
);
itup
=
GinFormTuple
(
&
gvs
->
ginstate
,
value
,
GinGetPosting
(
itup
),
newN
);
value
=
gin_index_getattr
(
&
gvs
->
ginstate
,
itup
);
attnum
=
gintuple_get_attrnum
(
&
gvs
->
ginstate
,
itup
);
itup
=
GinFormTuple
(
&
gvs
->
ginstate
,
attnum
,
value
,
GinGetPosting
(
itup
),
newN
);
PageIndexTupleDelete
(
tmppage
,
i
);
if
(
PageAddItem
(
tmppage
,
(
Item
)
itup
,
IndexTupleSize
(
itup
),
i
,
false
,
false
)
!=
i
)
...
...
src/backend/access/gin/ginxlog.c
View file @
27cb66fd
...
...
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gin/ginxlog.c,v 1.1
4 2008/06/12 09:12:29 heikki
Exp $
* $PostgreSQL: pgsql/src/backend/access/gin/ginxlog.c,v 1.1
5 2008/07/11 21:06:29 tgl
Exp $
*-------------------------------------------------------------------------
*/
#include "postgres.h"
...
...
@@ -549,7 +549,7 @@ ginContinueSplit(ginIncompleteSplit *split)
if
(
split
->
rootBlkno
==
GIN_ROOT_BLKNO
)
{
prepareEntryScan
(
&
btree
,
reln
,
(
Datum
)
0
,
NULL
);
prepareEntryScan
(
&
btree
,
reln
,
InvalidOffsetNumber
,
(
Datum
)
0
,
NULL
);
btree
.
entry
=
ginPageGetLinkItup
(
buffer
);
}
else
...
...
src/include/access/gin.h
View file @
27cb66fd
...
...
@@ -4,7 +4,7 @@
*
* Copyright (c) 2006-2008, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/include/access/gin.h,v 1.2
2 2008/06/19 00:46:05 alvherre
Exp $
* $PostgreSQL: pgsql/src/include/access/gin.h,v 1.2
3 2008/07/11 21:06:29 tgl
Exp $
*--------------------------------------------------------------------------
*/
...
...
@@ -140,15 +140,18 @@ typedef struct
typedef
struct
GinState
{
FmgrInfo
compareFn
;
FmgrInfo
extractValueFn
;
FmgrInfo
extractQueryFn
;
FmgrInfo
consistentFn
;
FmgrInfo
comparePartialFn
;
/* optional method */
bool
canPartialMatch
;
/* can opclass perform partial
* match (prefix search)? */
TupleDesc
tupdesc
;
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 */
...
...
@@ -235,12 +238,16 @@ 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
int
compareEntries
(
GinState
*
ginstate
,
Datum
a
,
Datum
b
);
extern
Datum
*
extractEntriesS
(
GinState
*
ginstate
,
Datum
value
,
extern
int
compareEntries
(
GinState
*
ginstate
,
OffsetNumber
attnum
,
Datum
a
,
Datum
b
);
extern
int
compareAttEntries
(
GinState
*
ginstate
,
OffsetNumber
attnum_a
,
Datum
a
,
OffsetNumber
attnum_b
,
Datum
b
);
extern
Datum
*
extractEntriesS
(
GinState
*
ginstate
,
OffsetNumber
attnum
,
Datum
value
,
int32
*
nentries
,
bool
*
needUnique
);
extern
Datum
*
extractEntriesSU
(
GinState
*
ginstate
,
Datum
value
,
int32
*
nentries
);
extern
Datum
*
extractEntriesSU
(
GinState
*
ginstate
,
OffsetNumber
attnum
,
Datum
value
,
int32
*
nentries
);
extern
Page
GinPageGetCopyPage
(
Page
page
);
extern
Datum
gin_index_getattr
(
GinState
*
ginstate
,
IndexTuple
tuple
);
extern
OffsetNumber
gintuple_get_attrnum
(
GinState
*
ginstate
,
IndexTuple
tuple
);
/* gininsert.c */
extern
Datum
ginbuild
(
PG_FUNCTION_ARGS
);
extern
Datum
gininsert
(
PG_FUNCTION_ARGS
);
...
...
@@ -291,6 +298,7 @@ typedef struct GinBtreeData
BlockNumber
rightblkno
;
/* Entry options */
OffsetNumber
entryAttnum
;
Datum
entryValue
;
IndexTuple
entry
;
bool
isDelete
;
...
...
@@ -310,9 +318,10 @@ extern void ginInsertValue(GinBtree btree, GinBtreeStack *stack);
extern
void
findParents
(
GinBtree
btree
,
GinBtreeStack
*
stack
,
BlockNumber
rootBlkno
);
/* ginentrypage.c */
extern
IndexTuple
GinFormTuple
(
GinState
*
ginstate
,
Datum
key
,
ItemPointerData
*
ipd
,
uint32
nipd
);
extern
Datum
ginGetHighKey
(
GinState
*
ginstate
,
Page
page
);
extern
void
prepareEntryScan
(
GinBtree
btree
,
Relation
index
,
Datum
value
,
GinState
*
ginstate
);
extern
IndexTuple
GinFormTuple
(
GinState
*
ginstate
,
OffsetNumber
attnum
,
Datum
key
,
ItemPointerData
*
ipd
,
uint32
nipd
);
extern
void
prepareEntryScan
(
GinBtree
btree
,
Relation
index
,
OffsetNumber
attnum
,
Datum
value
,
GinState
*
ginstate
);
extern
void
entryFillRoot
(
GinBtree
btree
,
Buffer
root
,
Buffer
lbuf
,
Buffer
rbuf
);
extern
IndexTuple
ginPageGetLinkItup
(
Buffer
buf
);
...
...
@@ -359,6 +368,7 @@ typedef struct GinScanEntryData
/* entry, got from extractQueryFn */
Datum
entry
;
OffsetNumber
attnum
;
/* Current page in posting tree */
Buffer
buffer
;
...
...
@@ -396,6 +406,7 @@ typedef struct GinScanKeyData
/* for calling consistentFn(GinScanKey->entryRes, strategy, query) */
StrategyNumber
strategy
;
Datum
query
;
OffsetNumber
attnum
;
ItemPointerData
curItem
;
bool
firstCall
;
...
...
@@ -450,11 +461,12 @@ extern Datum ginarrayconsistent(PG_FUNCTION_ARGS);
/* ginbulk.c */
typedef
struct
EntryAccumulator
{
Datum
value
;
uint32
length
;
uint32
number
;
OffsetNumber
attnum
;
Datum
value
;
uint32
length
;
uint32
number
;
ItemPointerData
*
list
;
bool
shouldSort
;
bool
shouldSort
;
struct
EntryAccumulator
*
left
;
struct
EntryAccumulator
*
right
;
}
EntryAccumulator
;
...
...
@@ -474,7 +486,8 @@ typedef struct
extern
void
ginInitBA
(
BuildAccumulator
*
accum
);
extern
void
ginInsertRecordBA
(
BuildAccumulator
*
accum
,
ItemPointer
heapptr
,
Datum
*
entries
,
int32
nentry
);
extern
ItemPointerData
*
ginGetEntry
(
BuildAccumulator
*
accum
,
Datum
*
entry
,
uint32
*
n
);
ItemPointer
heapptr
,
OffsetNumber
attnum
,
Datum
*
entries
,
int32
nentry
);
extern
ItemPointerData
*
ginGetEntry
(
BuildAccumulator
*
accum
,
OffsetNumber
*
attnum
,
Datum
*
entry
,
uint32
*
n
);
#endif
src/include/catalog/catversion.h
View file @
27cb66fd
...
...
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.46
5 2008/07/03 20:58:46
tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.46
6 2008/07/11 21:06:29
tgl Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200807
03
1
#define CATALOG_VERSION_NO 200807
11
1
#endif
src/include/catalog/pg_am.h
View file @
27cb66fd
...
...
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.5
6 2008/05/16 16:31:01
tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.5
7 2008/07/11 21:06:29
tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
...
...
@@ -114,7 +114,7 @@ DESCR("hash index access method");
DATA
(
insert
OID
=
783
(
gist
0
7
f
f
t
t
t
t
t
t
gistinsert
gistbeginscan
gistgettuple
gistgetbitmap
gistrescan
gistendscan
gistmarkpos
gistrestrpos
gistbuild
gistbulkdelete
gistvacuumcleanup
gistcostestimate
gistoptions
));
DESCR
(
"GiST index access method"
);
#define GIST_AM_OID 783
DATA
(
insert
OID
=
2742
(
gin
0
5
f
f
f
f
f
f
t
f
gininsert
ginbeginscan
gingettuple
gingetbitmap
ginrescan
ginendscan
ginmarkpos
ginrestrpos
ginbuild
ginbulkdelete
ginvacuumcleanup
gincostestimate
ginoptions
));
DATA
(
insert
OID
=
2742
(
gin
0
5
f
f
t
t
f
f
t
f
gininsert
ginbeginscan
gingettuple
gingetbitmap
ginrescan
ginendscan
ginmarkpos
ginrestrpos
ginbuild
ginbulkdelete
ginvacuumcleanup
gincostestimate
ginoptions
));
DESCR
(
"GIN index access method"
);
#define GIN_AM_OID 2742
...
...
src/test/regress/expected/create_index.out
View file @
27cb66fd
This diff is collapsed.
Click to expand it.
src/test/regress/sql/create_index.sql
View file @
27cb66fd
...
...
@@ -138,7 +138,7 @@ RESET enable_indexscan;
RESET
enable_bitmapscan
;
--
-- GIN over int[]
-- GIN over int[]
and text[]
--
SET
enable_seqscan
=
OFF
;
...
...
@@ -180,6 +180,33 @@ 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
;
-- 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
;
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
;
RESET
enable_seqscan
;
RESET
enable_indexscan
;
RESET
enable_bitmapscan
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment