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
f02a82b6
Commit
f02a82b6
authored
Apr 06, 2007
by
Tom Lane
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make 'col IS NULL' clauses be indexable conditions.
Teodor Sigaev, with some kibitzing from Tom Lane.
parent
146c83c0
Changes
19
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
433 additions
and
111 deletions
+433
-111
doc/src/sgml/catalogs.sgml
doc/src/sgml/catalogs.sgml
+8
-1
doc/src/sgml/indexam.sgml
doc/src/sgml/indexam.sgml
+5
-2
doc/src/sgml/indices.sgml
doc/src/sgml/indices.sgml
+9
-5
doc/src/sgml/ref/create_index.sgml
doc/src/sgml/ref/create_index.sgml
+1
-7
src/backend/access/common/scankey.c
src/backend/access/common/scankey.c
+10
-3
src/backend/access/gist/gistget.c
src/backend/access/gist/gistget.c
+33
-25
src/backend/access/nbtree/nbtsearch.c
src/backend/access/nbtree/nbtsearch.c
+1
-3
src/backend/access/nbtree/nbtutils.c
src/backend/access/nbtree/nbtutils.c
+77
-16
src/backend/executor/nodeIndexscan.c
src/backend/executor/nodeIndexscan.c
+36
-2
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/indxpath.c
+23
-3
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/createplan.c
+35
-11
src/backend/optimizer/util/plancat.c
src/backend/optimizer/util/plancat.c
+2
-1
src/backend/utils/adt/selfuncs.c
src/backend/utils/adt/selfuncs.c
+32
-7
src/include/access/skey.h
src/include/access/skey.h
+8
-1
src/include/catalog/catversion.h
src/include/catalog/catversion.h
+2
-2
src/include/catalog/pg_am.h
src/include/catalog/pg_am.h
+23
-21
src/include/nodes/relation.h
src/include/nodes/relation.h
+2
-1
src/test/regress/expected/create_index.out
src/test/regress/expected/create_index.out
+80
-0
src/test/regress/sql/create_index.sql
src/test/regress/sql/create_index.sql
+46
-0
No files found.
doc/src/sgml/catalogs.sgml
View file @
f02a82b6
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.1
49 2007/04/02 03:49:36
tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.1
50 2007/04/06 22:33:41
tgl Exp $ -->
<!--
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
Documentation of the system catalogs, directed toward PostgreSQL developers
-->
-->
...
@@ -405,6 +405,13 @@
...
@@ -405,6 +405,13 @@
<entry>Does the access method support null index entries?</entry>
<entry>Does the access method support null index entries?</entry>
</row>
</row>
<row>
<entry><structfield>amsearchnulls</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
<entry>Does the access method support IS NULL searches?</entry>
</row>
<row>
<row>
<entry><structfield>amstorage</structfield></entry>
<entry><structfield>amstorage</structfield></entry>
<entry><type>bool</type></entry>
<entry><type>bool</type></entry>
...
...
doc/src/sgml/indexam.sgml
View file @
f02a82b6
<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.2
2 2007/02/22 22:00:22
tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.2
3 2007/04/06 22:33:41
tgl Exp $ -->
<chapter id="indexam">
<chapter id="indexam">
<title>Index Access Method Interface Definition</title>
<title>Index Access Method Interface Definition</title>
...
@@ -129,7 +129,10 @@
...
@@ -129,7 +129,10 @@
It is, however, OK to omit rows where the first indexed column is null.
It is, however, OK to omit rows where the first indexed column is null.
Thus, <structfield>amindexnulls</structfield> should be set true only if the
Thus, <structfield>amindexnulls</structfield> should be set true only if the
index access method indexes all rows, including arbitrary combinations of
index access method indexes all rows, including arbitrary combinations of
null values.
null values. An index access method that sets
<structfield>amindexnulls</structfield> may also set
<structfield>amsearchnulls</structfield>, indicating that it supports
<literal>IS NULL</> clauses as search conditions.
</para>
</para>
</sect1>
</sect1>
...
...
doc/src/sgml/indices.sgml
View file @
f02a82b6
<!-- $PostgreSQL: pgsql/doc/src/sgml/indices.sgml,v 1.7
0 2007/02/14 20:47:15
tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/indices.sgml,v 1.7
1 2007/04/06 22:33:41
tgl Exp $ -->
<chapter id="indexes">
<chapter id="indexes">
<title id="indexes-title">Indexes</title>
<title id="indexes-title">Indexes</title>
...
@@ -147,8 +147,8 @@ CREATE INDEX test1_id_index ON test1 (id);
...
@@ -147,8 +147,8 @@ CREATE INDEX test1_id_index ON test1 (id);
Constructs equivalent to combinations of these operators, such as
Constructs equivalent to combinations of these operators, such as
<literal>BETWEEN</> and <literal>IN</>, can also be implemented with
<literal>BETWEEN</> and <literal>IN</>, can also be implemented with
a B-tree index search.
(But note that <literal>IS NULL</> is not
a B-tree index search.
Also, an <literal>IS NULL</> condition on
equivalent to <literal>=</> and is not indexable.)
an index column can be used with a B-tree index.
</para>
</para>
<para>
<para>
...
@@ -180,8 +180,9 @@ CREATE INDEX test1_id_index ON test1 (id);
...
@@ -180,8 +180,9 @@ CREATE INDEX test1_id_index ON test1 (id);
Hash indexes can only handle simple equality comparisons.
Hash indexes can only handle simple equality comparisons.
The query planner will consider using a hash index whenever an
The query planner will consider using a hash index whenever an
indexed column is involved in a comparison using the
indexed column is involved in a comparison using the
<literal>=</literal> operator. The following command is used to
<literal>=</literal> operator. (But hash indexes do not support
create a hash index:
<literal>IS NULL</> searches.)
The following command is used to create a hash index:
<synopsis>
<synopsis>
CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING hash (<replaceable>column</replaceable>);
CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> USING hash (<replaceable>column</replaceable>);
</synopsis>
</synopsis>
...
@@ -234,6 +235,8 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
...
@@ -234,6 +235,8 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
(See <xref linkend="functions-geometry"> for the meaning of
(See <xref linkend="functions-geometry"> for the meaning of
these operators.)
these operators.)
Also, an <literal>IS NULL</> condition on
an index column can be used with a GiST index.
Many other GiST operator
Many other GiST operator
classes are available in the <literal>contrib</> collection or as separate
classes are available in the <literal>contrib</> collection or as separate
projects. For more information see <xref linkend="GiST">.
projects. For more information see <xref linkend="GiST">.
...
@@ -266,6 +269,7 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
...
@@ -266,6 +269,7 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable>
(See <xref linkend="functions-array"> for the meaning of
(See <xref linkend="functions-array"> for the meaning of
these operators.)
these operators.)
GIN indexes cannot use <literal>IS NULL</> as a search condition.
Other GIN operator classes are available in the <literal>contrib</>
Other GIN operator classes are available in the <literal>contrib</>
<literal>tsearch2</literal> and <literal>intarray</literal> modules.
<literal>tsearch2</literal> and <literal>intarray</literal> modules.
For more information see <xref linkend="GIN">.
For more information see <xref linkend="GIN">.
...
...
doc/src/sgml/ref/create_index.sgml
View file @
f02a82b6
<!--
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_index.sgml,v 1.6
1 2007/04/03 22:38:35 momjian
Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/create_index.sgml,v 1.6
2 2007/04/06 22:33:41 tgl
Exp $
PostgreSQL documentation
PostgreSQL documentation
-->
-->
...
@@ -434,12 +434,6 @@ Indexes:
...
@@ -434,12 +434,6 @@ Indexes:
to remove an index.
to remove an index.
</para>
</para>
<para>
Indexes are not used for <literal>IS NULL</> clauses by default.
The best way to use indexes in such cases is to create a partial index
using an <literal>IS NULL</> predicate.
</para>
<para>
<para>
Prior releases of <productname>PostgreSQL</productname> also had an
Prior releases of <productname>PostgreSQL</productname> also had an
R-tree index method. This method has been removed because
R-tree index method. This method has been removed because
...
...
src/backend/access/common/scankey.c
View file @
f02a82b6
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
*
*
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/common/scankey.c,v 1.
29 2007/01/05 22:19:21 momjian
Exp $
* $PostgreSQL: pgsql/src/backend/access/common/scankey.c,v 1.
30 2007/04/06 22:33:41 tgl
Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -20,7 +20,8 @@
...
@@ -20,7 +20,8 @@
/*
/*
* ScanKeyEntryInitialize
* ScanKeyEntryInitialize
* Initializes a scan key entry given all the field values.
* Initializes a scan key entry given all the field values.
* The target procedure is specified by OID.
* The target procedure is specified by OID (but can be invalid
* if SK_SEARCHNULL is set).
*
*
* Note: CurrentMemoryContext at call should be as long-lived as the ScanKey
* Note: CurrentMemoryContext at call should be as long-lived as the ScanKey
* itself, because that's what will be used for any subsidiary info attached
* itself, because that's what will be used for any subsidiary info attached
...
@@ -40,7 +41,13 @@ ScanKeyEntryInitialize(ScanKey entry,
...
@@ -40,7 +41,13 @@ ScanKeyEntryInitialize(ScanKey entry,
entry
->
sk_strategy
=
strategy
;
entry
->
sk_strategy
=
strategy
;
entry
->
sk_subtype
=
subtype
;
entry
->
sk_subtype
=
subtype
;
entry
->
sk_argument
=
argument
;
entry
->
sk_argument
=
argument
;
fmgr_info
(
procedure
,
&
entry
->
sk_func
);
if
(
RegProcedureIsValid
(
procedure
))
fmgr_info
(
procedure
,
&
entry
->
sk_func
);
else
{
Assert
(
flags
&
SK_SEARCHNULL
);
MemSet
(
&
entry
->
sk_func
,
0
,
sizeof
(
entry
->
sk_func
));
}
}
}
/*
/*
...
...
src/backend/access/gist/gistget.c
View file @
f02a82b6
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gistget.c,v 1.6
4 2007/01/20 18:43:35 neilc
Exp $
* $PostgreSQL: pgsql/src/backend/access/gist/gistget.c,v 1.6
5 2007/04/06 22:33:41 tgl
Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -381,37 +381,45 @@ gistindex_keytest(IndexTuple tuple,
...
@@ -381,37 +381,45 @@ gistindex_keytest(IndexTuple tuple,
if
(
key
->
sk_flags
&
SK_ISNULL
)
if
(
key
->
sk_flags
&
SK_ISNULL
)
{
{
/*
/*
* is the compared-to datum NULL? on non-leaf page it's possible
* On non-leaf page we can't conclude that child hasn't NULL
* to have nulls in childs :(
* values because of assumption in GiST: uinon (VAL, NULL) is VAL
* But if on non-leaf page key IS NULL then all childs
* has NULL.
*/
*/
if
(
isNull
||
!
GistPageIsLeaf
(
p
))
Assert
(
key
->
sk_flags
&
SK_SEARCHNULL
);
return
true
;
return
false
;
if
(
GistPageIsLeaf
(
p
)
&&
!
isNull
)
return
false
;
}
}
else
if
(
isNull
)
else
if
(
isNull
)
{
return
false
;
return
false
;
}
else
{
gistdentryinit
(
giststate
,
key
->
sk_attno
-
1
,
&
de
,
gistdentryinit
(
giststate
,
key
->
sk_attno
-
1
,
&
de
,
datum
,
r
,
p
,
offset
,
datum
,
r
,
p
,
offset
,
FALSE
,
isNull
);
FALSE
,
isNull
);
/*
/*
* Call the Consistent function to evaluate the test. The arguments
* Call the Consistent function to evaluate the test. The arguments
* are the index datum (as a GISTENTRY*), the comparison datum, and
* are the index datum (as a GISTENTRY*), the comparison datum, and
* the comparison operator's strategy number and subtype from pg_amop.
* the comparison operator's strategy number and subtype from pg_amop.
*
*
* (Presently there's no need to pass the subtype since it'll always
* (Presently there's no need to pass the subtype since it'll always
* be zero, but might as well pass it for possible future use.)
* be zero, but might as well pass it for possible future use.)
*/
*/
test
=
FunctionCall4
(
&
key
->
sk_func
,
test
=
FunctionCall4
(
&
key
->
sk_func
,
PointerGetDatum
(
&
de
),
PointerGetDatum
(
&
de
),
key
->
sk_argument
,
key
->
sk_argument
,
Int32GetDatum
(
key
->
sk_strategy
),
Int32GetDatum
(
key
->
sk_strategy
),
ObjectIdGetDatum
(
key
->
sk_subtype
));
ObjectIdGetDatum
(
key
->
sk_subtype
));
if
(
!
DatumGetBool
(
test
))
if
(
!
DatumGetBool
(
test
))
return
false
;
return
false
;
}
keySize
--
;
keySize
--
;
key
++
;
key
++
;
...
...
src/backend/access/nbtree/nbtsearch.c
View file @
f02a82b6
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.11
1 2007/01/09 02:14:10
tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.11
2 2007/04/06 22:33:42
tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -746,8 +746,6 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
...
@@ -746,8 +746,6 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
*
*
* If goback = true, we will then step back one item, while if
* If goback = true, we will then step back one item, while if
* goback = false, we will start the scan on the located item.
* goback = false, we will start the scan on the located item.
*
* it's yet other place to add some code later for is(not)null ...
*----------
*----------
*/
*/
switch
(
strat_total
)
switch
(
strat_total
)
...
...
src/backend/access/nbtree/nbtutils.c
View file @
f02a82b6
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
*
*
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.8
3 2007/03/30 00:12:59
tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.8
4 2007/04/06 22:33:42
tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -264,12 +264,27 @@ _bt_preprocess_keys(IndexScanDesc scan)
...
@@ -264,12 +264,27 @@ _bt_preprocess_keys(IndexScanDesc scan)
if
(
numberOfKeys
==
1
)
if
(
numberOfKeys
==
1
)
{
{
/*
/*
* We don't use indices for 'A is null' and 'A is not null' currently
* We treat all btree operators as strict (even if they're not so
* and 'A < = > <> NULL' will always fail - so qual is not OK if
* marked in pg_proc). This means that it is impossible for an
* comparison value is NULL. - vadim 03/21/97
* operator condition with a NULL comparison constant to succeed,
* and we can reject it right away.
*
* However, we now also support "x IS NULL" clauses as search
* conditions, so in that case keep going. The planner has not
* filled in any particular strategy in this case, so set it to
* BTEqualStrategyNumber --- we can treat IS NULL as an equality
* operator for purposes of search strategy.
*/
*/
if
(
cur
->
sk_flags
&
SK_ISNULL
)
if
(
cur
->
sk_flags
&
SK_ISNULL
)
so
->
qual_ok
=
false
;
{
if
(
cur
->
sk_flags
&
SK_SEARCHNULL
)
{
cur
->
sk_strategy
=
BTEqualStrategyNumber
;
cur
->
sk_subtype
=
InvalidOid
;
}
else
so
->
qual_ok
=
false
;
}
_bt_mark_scankey_with_indoption
(
cur
,
indoption
);
_bt_mark_scankey_with_indoption
(
cur
,
indoption
);
memcpy
(
outkeys
,
cur
,
sizeof
(
ScanKeyData
));
memcpy
(
outkeys
,
cur
,
sizeof
(
ScanKeyData
));
so
->
numberOfKeys
=
1
;
so
->
numberOfKeys
=
1
;
...
@@ -303,17 +318,20 @@ _bt_preprocess_keys(IndexScanDesc scan)
...
@@ -303,17 +318,20 @@ _bt_preprocess_keys(IndexScanDesc scan)
{
{
if
(
i
<
numberOfKeys
)
if
(
i
<
numberOfKeys
)
{
{
/* See comments above
: any NULL implies cannot match qual
*/
/* See comments above
about NULLs and IS NULL handling.
*/
/* Note: we assume SK_ISNULL is never set in a row header key */
/* Note: we assume SK_ISNULL is never set in a row header key */
if
(
cur
->
sk_flags
&
SK_ISNULL
)
if
(
cur
->
sk_flags
&
SK_ISNULL
)
{
{
so
->
qual_ok
=
false
;
if
(
cur
->
sk_flags
&
SK_SEARCHNULL
)
{
/*
cur
->
sk_strategy
=
BTEqualStrategyNumber
;
* Quit processing so we don't try to invoke comparison
cur
->
sk_subtype
=
InvalidOid
;
* routines on NULLs.
}
*/
else
return
;
{
so
->
qual_ok
=
false
;
return
;
}
}
}
}
}
...
@@ -344,6 +362,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
...
@@ -344,6 +362,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
if
(
!
chk
||
j
==
(
BTEqualStrategyNumber
-
1
))
if
(
!
chk
||
j
==
(
BTEqualStrategyNumber
-
1
))
continue
;
continue
;
/* IS NULL together with any other predicate must fail */
if
(
eq
->
sk_flags
&
SK_SEARCHNULL
)
{
so
->
qual_ok
=
false
;
return
;
}
if
(
_bt_compare_scankey_args
(
scan
,
chk
,
eq
,
chk
,
if
(
_bt_compare_scankey_args
(
scan
,
chk
,
eq
,
chk
,
&
test_result
))
&
test_result
))
{
{
...
@@ -455,6 +481,23 @@ _bt_preprocess_keys(IndexScanDesc scan)
...
@@ -455,6 +481,23 @@ _bt_preprocess_keys(IndexScanDesc scan)
else
else
{
{
/* yup, keep only the more restrictive key */
/* yup, keep only the more restrictive key */
/* if either arg is NULL, don't try to compare */
if
((
cur
->
sk_flags
|
xform
[
j
]
->
sk_flags
)
&
SK_ISNULL
)
{
/* at least one of them must be an IS NULL clause */
Assert
(
j
==
(
BTEqualStrategyNumber
-
1
));
Assert
((
cur
->
sk_flags
|
xform
[
j
]
->
sk_flags
)
&
SK_SEARCHNULL
);
/* if one is and one isn't, the search must fail */
if
((
cur
->
sk_flags
^
xform
[
j
]
->
sk_flags
)
&
SK_SEARCHNULL
)
{
so
->
qual_ok
=
false
;
return
;
}
/* we have duplicate IS NULL clauses, ignore the newer one */
continue
;
}
if
(
_bt_compare_scankey_args
(
scan
,
cur
,
cur
,
xform
[
j
],
if
(
_bt_compare_scankey_args
(
scan
,
cur
,
cur
,
xform
[
j
],
&
test_result
))
&
test_result
))
{
{
...
@@ -798,11 +841,29 @@ _bt_checkkeys(IndexScanDesc scan,
...
@@ -798,11 +841,29 @@ _bt_checkkeys(IndexScanDesc scan,
tupdesc
,
tupdesc
,
&
isNull
);
&
isNull
);
/* btree doesn't support 'A is null' clauses, yet */
if
(
key
->
sk_flags
&
SK_ISNULL
)
if
(
key
->
sk_flags
&
SK_ISNULL
)
{
{
/* we shouldn't get here, really; see _bt_preprocess_keys() */
/* Handle IS NULL tests */
*
continuescan
=
false
;
Assert
(
key
->
sk_flags
&
SK_SEARCHNULL
);
if
(
isNull
)
continue
;
/* tuple satisfies this qual */
/*
* Tuple fails this qual. If it's a required qual for the current
* scan direction, then we can conclude no further tuples will
* pass, either.
*/
if
((
key
->
sk_flags
&
SK_BT_REQFWD
)
&&
ScanDirectionIsForward
(
dir
))
*
continuescan
=
false
;
else
if
((
key
->
sk_flags
&
SK_BT_REQBKWD
)
&&
ScanDirectionIsBackward
(
dir
))
*
continuescan
=
false
;
/*
* In any case, this indextuple doesn't match the qual.
*/
return
false
;
return
false
;
}
}
...
...
src/backend/executor/nodeIndexscan.c
View file @
f02a82b6
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
*
*
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.12
0 2007/01/05 22:19:28 momjian
Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeIndexscan.c,v 1.12
1 2007/04/06 22:33:42 tgl
Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -599,7 +599,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
...
@@ -599,7 +599,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
* The index quals are passed to the index AM in the form of a ScanKey array.
* The index quals are passed to the index AM in the form of a ScanKey array.
* This routine sets up the ScanKeys, fills in all constant fields of the
* This routine sets up the ScanKeys, fills in all constant fields of the
* ScanKeys, and prepares information about the keys that have non-constant
* ScanKeys, and prepares information about the keys that have non-constant
* comparison values. We divide index qual expressions into f
our
types:
* comparison values. We divide index qual expressions into f
ive
types:
*
*
* 1. Simple operator with constant comparison value ("indexkey op constant").
* 1. Simple operator with constant comparison value ("indexkey op constant").
* For these, we just fill in a ScanKey containing the constant value.
* For these, we just fill in a ScanKey containing the constant value.
...
@@ -620,6 +620,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
...
@@ -620,6 +620,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
* (Note that we treat all array-expressions as requiring runtime evaluation,
* (Note that we treat all array-expressions as requiring runtime evaluation,
* even if they happen to be constants.)
* even if they happen to be constants.)
*
*
* 5. NullTest ("indexkey IS NULL"). We just fill in the ScanKey properly.
*
* Input params are:
* Input params are:
*
*
* planstate: executor state node we are working for
* planstate: executor state node we are working for
...
@@ -956,6 +958,38 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
...
@@ -956,6 +958,38 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
opfuncid
,
/* reg proc to use */
opfuncid
,
/* reg proc to use */
(
Datum
)
0
);
/* constant */
(
Datum
)
0
);
/* constant */
}
}
else
if
(
IsA
(
clause
,
NullTest
))
{
/* indexkey IS NULL */
Assert
(((
NullTest
*
)
clause
)
->
nulltesttype
==
IS_NULL
);
/*
* argument should be the index key Var, possibly relabeled
*/
leftop
=
((
NullTest
*
)
clause
)
->
arg
;
if
(
leftop
&&
IsA
(
leftop
,
RelabelType
))
leftop
=
((
RelabelType
*
)
leftop
)
->
arg
;
Assert
(
leftop
!=
NULL
);
if
(
!
(
IsA
(
leftop
,
Var
)
&&
var_is_rel
((
Var
*
)
leftop
)))
elog
(
ERROR
,
"NullTest indexqual has wrong key"
);
varattno
=
((
Var
*
)
leftop
)
->
varattno
;
/*
* initialize the scan key's fields appropriately
*/
ScanKeyEntryInitialize
(
this_scan_key
,
SK_ISNULL
|
SK_SEARCHNULL
,
varattno
,
/* attribute number to scan */
strategy
,
/* op's strategy */
subtype
,
/* strategy subtype */
InvalidOid
,
/* no reg proc for this */
(
Datum
)
0
);
/* constant */
}
else
else
elog
(
ERROR
,
"unsupported indexqual type: %d"
,
elog
(
ERROR
,
"unsupported indexqual type: %d"
,
(
int
)
nodeTag
(
clause
));
(
int
)
nodeTag
(
clause
));
...
...
src/backend/optimizer/path/indxpath.c
View file @
f02a82b6
...
@@ -9,7 +9,7 @@
...
@@ -9,7 +9,7 @@
*
*
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.21
8 2007/03/21 22:18:1
2 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.21
9 2007/04/06 22:33:4
2 tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -1050,6 +1050,7 @@ match_clause_to_indexcol(IndexOptInfo *index,
...
@@ -1050,6 +1050,7 @@ match_clause_to_indexcol(IndexOptInfo *index,
* Clause must be a binary opclause, or possibly a ScalarArrayOpExpr
* Clause must be a binary opclause, or possibly a ScalarArrayOpExpr
* (which is always binary, by definition). Or it could be a
* (which is always binary, by definition). Or it could be a
* RowCompareExpr, which we pass off to match_rowcompare_to_indexcol().
* RowCompareExpr, which we pass off to match_rowcompare_to_indexcol().
* Or, if the index supports it, we can handle IS NULL clauses.
*/
*/
if
(
is_opclause
(
clause
))
if
(
is_opclause
(
clause
))
{
{
...
@@ -1083,6 +1084,15 @@ match_clause_to_indexcol(IndexOptInfo *index,
...
@@ -1083,6 +1084,15 @@ match_clause_to_indexcol(IndexOptInfo *index,
(
RowCompareExpr
*
)
clause
,
(
RowCompareExpr
*
)
clause
,
outer_relids
);
outer_relids
);
}
}
else
if
(
index
->
amsearchnulls
&&
IsA
(
clause
,
NullTest
))
{
NullTest
*
nt
=
(
NullTest
*
)
clause
;
if
(
nt
->
nulltesttype
==
IS_NULL
&&
match_index_to_operand
((
Node
*
)
nt
->
arg
,
indexcol
,
index
))
return
true
;
return
false
;
}
else
else
return
false
;
return
false
;
...
@@ -2102,8 +2112,8 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
...
@@ -2102,8 +2112,8 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
}
}
/*
/*
* Else it must be an opclause (usual case), ScalarArrayOp,
or
* Else it must be an opclause (usual case), ScalarArrayOp,
* RowCompare
* RowCompare
, or NullTest
*/
*/
if
(
is_opclause
(
clause
))
if
(
is_opclause
(
clause
))
{
{
...
@@ -2123,6 +2133,16 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
...
@@ -2123,6 +2133,16 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
index
,
index
,
indexcol
));
indexcol
));
}
}
else
if
(
IsA
(
clause
,
NullTest
))
{
Assert
(
index
->
amsearchnulls
);
resultquals
=
lappend
(
resultquals
,
make_restrictinfo
(
clause
,
true
,
false
,
false
,
NULL
));
}
else
else
elog
(
ERROR
,
"unsupported indexqual type: %d"
,
elog
(
ERROR
,
"unsupported indexqual type: %d"
,
(
int
)
nodeTag
(
clause
));
(
int
)
nodeTag
(
clause
));
...
...
src/backend/optimizer/plan/createplan.c
View file @
f02a82b6
...
@@ -10,7 +10,7 @@
...
@@ -10,7 +10,7 @@
*
*
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.22
7 2007/02/25 17:44:01
tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.22
8 2007/04/06 22:33:42
tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -18,6 +18,7 @@
...
@@ -18,6 +18,7 @@
#include <limits.h>
#include <limits.h>
#include "access/skey.h"
#include "nodes/makefuncs.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/cost.h"
...
@@ -1821,6 +1822,7 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
...
@@ -1821,6 +1822,7 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
Oid
stratlefttype
;
Oid
stratlefttype
;
Oid
stratrighttype
;
Oid
stratrighttype
;
bool
recheck
;
bool
recheck
;
bool
is_null_op
=
false
;
Assert
(
IsA
(
rinfo
,
RestrictInfo
));
Assert
(
IsA
(
rinfo
,
RestrictInfo
));
...
@@ -1907,6 +1909,17 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
...
@@ -1907,6 +1909,17 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
&
opfamily
);
&
opfamily
);
clause_op
=
saop
->
opno
;
clause_op
=
saop
->
opno
;
}
}
else
if
(
IsA
(
clause
,
NullTest
))
{
NullTest
*
nt
=
(
NullTest
*
)
clause
;
Assert
(
nt
->
nulltesttype
==
IS_NULL
);
nt
->
arg
=
(
Expr
*
)
fix_indexqual_operand
((
Node
*
)
nt
->
arg
,
index
,
&
opfamily
);
is_null_op
=
true
;
clause_op
=
InvalidOid
;
/* keep compiler quiet */
}
else
else
{
{
elog
(
ERROR
,
"unsupported indexqual type: %d"
,
elog
(
ERROR
,
"unsupported indexqual type: %d"
,
...
@@ -1916,16 +1929,27 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
...
@@ -1916,16 +1929,27 @@ fix_indexqual_references(List *indexquals, IndexPath *index_path,
*
fixed_indexquals
=
lappend
(
*
fixed_indexquals
,
clause
);
*
fixed_indexquals
=
lappend
(
*
fixed_indexquals
,
clause
);
/*
if
(
is_null_op
)
* Look up the (possibly commuted) operator in the operator family to
{
* get its strategy number and the recheck indicator. This also
/* IS NULL doesn't have a clause_op */
* double-checks that we found an operator matching the index.
stratno
=
InvalidStrategy
;
*/
stratrighttype
=
InvalidOid
;
get_op_opfamily_properties
(
clause_op
,
opfamily
,
/* We assume it's non-lossy ... might need more work someday */
&
stratno
,
recheck
=
false
;
&
stratlefttype
,
}
&
stratrighttype
,
else
&
recheck
);
{
/*
* Look up the (possibly commuted) operator in the operator family
* to get its strategy number and the recheck indicator. This also
* double-checks that we found an operator matching the index.
*/
get_op_opfamily_properties
(
clause_op
,
opfamily
,
&
stratno
,
&
stratlefttype
,
&
stratrighttype
,
&
recheck
);
}
*
indexstrategy
=
lappend_int
(
*
indexstrategy
,
stratno
);
*
indexstrategy
=
lappend_int
(
*
indexstrategy
,
stratno
);
*
indexsubtype
=
lappend_oid
(
*
indexsubtype
,
stratrighttype
);
*
indexsubtype
=
lappend_oid
(
*
indexsubtype
,
stratrighttype
);
...
...
src/backend/optimizer/util/plancat.c
View file @
f02a82b6
...
@@ -9,7 +9,7 @@
...
@@ -9,7 +9,7 @@
*
*
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.13
2 2007/01/20 23:13:01
tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.13
3 2007/04/06 22:33:42
tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -187,6 +187,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
...
@@ -187,6 +187,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
info
->
relam
=
indexRelation
->
rd_rel
->
relam
;
info
->
relam
=
indexRelation
->
rd_rel
->
relam
;
info
->
amcostestimate
=
indexRelation
->
rd_am
->
amcostestimate
;
info
->
amcostestimate
=
indexRelation
->
rd_am
->
amcostestimate
;
info
->
amoptionalkey
=
indexRelation
->
rd_am
->
amoptionalkey
;
info
->
amoptionalkey
=
indexRelation
->
rd_am
->
amoptionalkey
;
info
->
amsearchnulls
=
indexRelation
->
rd_am
->
amsearchnulls
;
/*
/*
* Fetch the ordering operators associated with the index, if any.
* Fetch the ordering operators associated with the index, if any.
...
...
src/backend/utils/adt/selfuncs.c
View file @
f02a82b6
...
@@ -15,7 +15,7 @@
...
@@ -15,7 +15,7 @@
*
*
*
*
* IDENTIFICATION
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.23
1 2007/03/27 23:21:10
tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.23
2 2007/04/06 22:33:42
tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -4992,6 +4992,7 @@ btcostestimate(PG_FUNCTION_ARGS)
...
@@ -4992,6 +4992,7 @@ btcostestimate(PG_FUNCTION_ARGS)
int
indexcol
;
int
indexcol
;
bool
eqQualHere
;
bool
eqQualHere
;
bool
found_saop
;
bool
found_saop
;
bool
found_null_op
;
double
num_sa_scans
;
double
num_sa_scans
;
ListCell
*
l
;
ListCell
*
l
;
...
@@ -5016,6 +5017,7 @@ btcostestimate(PG_FUNCTION_ARGS)
...
@@ -5016,6 +5017,7 @@ btcostestimate(PG_FUNCTION_ARGS)
indexcol
=
0
;
indexcol
=
0
;
eqQualHere
=
false
;
eqQualHere
=
false
;
found_saop
=
false
;
found_saop
=
false
;
found_null_op
=
false
;
num_sa_scans
=
1
;
num_sa_scans
=
1
;
foreach
(
l
,
indexQuals
)
foreach
(
l
,
indexQuals
)
{
{
...
@@ -5025,6 +5027,7 @@ btcostestimate(PG_FUNCTION_ARGS)
...
@@ -5025,6 +5027,7 @@ btcostestimate(PG_FUNCTION_ARGS)
*
rightop
;
*
rightop
;
Oid
clause_op
;
Oid
clause_op
;
int
op_strategy
;
int
op_strategy
;
bool
is_null_op
=
false
;
Assert
(
IsA
(
rinfo
,
RestrictInfo
));
Assert
(
IsA
(
rinfo
,
RestrictInfo
));
clause
=
rinfo
->
clause
;
clause
=
rinfo
->
clause
;
...
@@ -5051,6 +5054,17 @@ btcostestimate(PG_FUNCTION_ARGS)
...
@@ -5051,6 +5054,17 @@ btcostestimate(PG_FUNCTION_ARGS)
clause_op
=
saop
->
opno
;
clause_op
=
saop
->
opno
;
found_saop
=
true
;
found_saop
=
true
;
}
}
else
if
(
IsA
(
clause
,
NullTest
))
{
NullTest
*
nt
=
(
NullTest
*
)
clause
;
Assert
(
nt
->
nulltesttype
==
IS_NULL
);
leftop
=
(
Node
*
)
nt
->
arg
;
rightop
=
NULL
;
clause_op
=
InvalidOid
;
found_null_op
=
true
;
is_null_op
=
true
;
}
else
else
{
{
elog
(
ERROR
,
"unsupported indexqual type: %d"
,
elog
(
ERROR
,
"unsupported indexqual type: %d"
,
...
@@ -5088,11 +5102,20 @@ btcostestimate(PG_FUNCTION_ARGS)
...
@@ -5088,11 +5102,20 @@ btcostestimate(PG_FUNCTION_ARGS)
break
;
break
;
}
}
}
}
op_strategy
=
get_op_opfamily_strategy
(
clause_op
,
/* check for equality operator */
index
->
opfamily
[
indexcol
]);
if
(
is_null_op
)
Assert
(
op_strategy
!=
0
);
/* not a member of opfamily?? */
{
if
(
op_strategy
==
BTEqualStrategyNumber
)
/* IS NULL is like = for purposes of selectivity determination */
eqQualHere
=
true
;
eqQualHere
=
true
;
}
else
{
op_strategy
=
get_op_opfamily_strategy
(
clause_op
,
index
->
opfamily
[
indexcol
]);
Assert
(
op_strategy
!=
0
);
/* not a member of opfamily?? */
if
(
op_strategy
==
BTEqualStrategyNumber
)
eqQualHere
=
true
;
}
/* count up number of SA scans induced by indexBoundQuals only */
/* count up number of SA scans induced by indexBoundQuals only */
if
(
IsA
(
clause
,
ScalarArrayOpExpr
))
if
(
IsA
(
clause
,
ScalarArrayOpExpr
))
{
{
...
@@ -5108,12 +5131,14 @@ btcostestimate(PG_FUNCTION_ARGS)
...
@@ -5108,12 +5131,14 @@ btcostestimate(PG_FUNCTION_ARGS)
/*
/*
* If index is unique and we found an '=' clause for each column, we can
* If index is unique and we found an '=' clause for each column, we can
* just assume numIndexTuples = 1 and skip the expensive
* just assume numIndexTuples = 1 and skip the expensive
* clauselist_selectivity calculations.
* clauselist_selectivity calculations. However, a ScalarArrayOp or
* NullTest invalidates that theory, even though it sets eqQualHere.
*/
*/
if
(
index
->
unique
&&
if
(
index
->
unique
&&
indexcol
==
index
->
ncolumns
-
1
&&
indexcol
==
index
->
ncolumns
-
1
&&
eqQualHere
&&
eqQualHere
&&
!
found_saop
)
!
found_saop
&&
!
found_null_op
)
numIndexTuples
=
1
.
0
;
numIndexTuples
=
1
.
0
;
else
else
{
{
...
...
src/include/access/skey.h
View file @
f02a82b6
...
@@ -7,7 +7,7 @@
...
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* $PostgreSQL: pgsql/src/include/access/skey.h,v 1.3
4 2007/01/05 22:19:51 momjian
Exp $
* $PostgreSQL: pgsql/src/include/access/skey.h,v 1.3
5 2007/04/06 22:33:42 tgl
Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -52,6 +52,12 @@ typedef uint16 StrategyNumber;
...
@@ -52,6 +52,12 @@ typedef uint16 StrategyNumber;
* the operator. When using a ScanKey in a heap scan, these fields are not
* the operator. When using a ScanKey in a heap scan, these fields are not
* used and may be set to InvalidStrategy/InvalidOid.
* used and may be set to InvalidStrategy/InvalidOid.
*
*
* A ScanKey can also represent a condition "column IS NULL"; this is signaled
* by the SK_SEARCHNULL flag bit. In this case the argument is always NULL,
* and the sk_strategy, sk_subtype, and sk_func fields are not used (unless
* set by the index AM). Currently, SK_SEARCHNULL is supported only for
* index scans, not heap scans; and not all index AMs support it.
*
* Note: in some places, ScanKeys are used as a convenient representation
* Note: in some places, ScanKeys are used as a convenient representation
* for the invocation of an access method support procedure. In this case
* for the invocation of an access method support procedure. In this case
* sk_strategy/sk_subtype are not meaningful, and sk_func may refer to a
* sk_strategy/sk_subtype are not meaningful, and sk_func may refer to a
...
@@ -111,6 +117,7 @@ typedef ScanKeyData *ScanKey;
...
@@ -111,6 +117,7 @@ typedef ScanKeyData *ScanKey;
#define SK_ROW_HEADER 0x0004
/* row comparison header (see above) */
#define SK_ROW_HEADER 0x0004
/* row comparison header (see above) */
#define SK_ROW_MEMBER 0x0008
/* row comparison member (see above) */
#define SK_ROW_MEMBER 0x0008
/* row comparison member (see above) */
#define SK_ROW_END 0x0010
/* last row comparison member (see above) */
#define SK_ROW_END 0x0010
/* last row comparison member (see above) */
#define SK_SEARCHNULL 0x0020
/* scankey represents a "col IS NULL" qual */
/*
/*
...
...
src/include/catalog/catversion.h
View file @
f02a82b6
...
@@ -37,7 +37,7 @@
...
@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.40
0 2007/04/06 04:21
:43 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.40
1 2007/04/06 22:33
:43 tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -53,6 +53,6 @@
...
@@ -53,6 +53,6 @@
*/
*/
/* yyyymmddN */
/* yyyymmddN */
#define CATALOG_VERSION_NO 2007040
5
1
#define CATALOG_VERSION_NO 2007040
6
1
#endif
#endif
src/include/catalog/pg_am.h
View file @
f02a82b6
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.5
0 2007/01/20 23:13:01
tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_am.h,v 1.5
1 2007/04/06 22:33:43
tgl Exp $
*
*
* NOTES
* NOTES
* the genbki.sh script reads this file and generates .bki
* the genbki.sh script reads this file and generates .bki
...
@@ -50,6 +50,7 @@ CATALOG(pg_am,2601)
...
@@ -50,6 +50,7 @@ CATALOG(pg_am,2601)
bool
amcanmulticol
;
/* does AM support multi-column indexes? */
bool
amcanmulticol
;
/* does AM support multi-column indexes? */
bool
amoptionalkey
;
/* can query omit key for the first column? */
bool
amoptionalkey
;
/* can query omit key for the first column? */
bool
amindexnulls
;
/* does AM support NULL index entries? */
bool
amindexnulls
;
/* does AM support NULL index entries? */
bool
amsearchnulls
;
/* can AM search for NULL index entries? */
bool
amstorage
;
/* can storage type differ from column type? */
bool
amstorage
;
/* can storage type differ from column type? */
bool
amclusterable
;
/* does AM support cluster command? */
bool
amclusterable
;
/* does AM support cluster command? */
regproc
aminsert
;
/* "insert this tuple" function */
regproc
aminsert
;
/* "insert this tuple" function */
...
@@ -78,7 +79,7 @@ typedef FormData_pg_am *Form_pg_am;
...
@@ -78,7 +79,7 @@ typedef FormData_pg_am *Form_pg_am;
* compiler constants for pg_am
* compiler constants for pg_am
* ----------------
* ----------------
*/
*/
#define Natts_pg_am 2
3
#define Natts_pg_am 2
4
#define Anum_pg_am_amname 1
#define Anum_pg_am_amname 1
#define Anum_pg_am_amstrategies 2
#define Anum_pg_am_amstrategies 2
#define Anum_pg_am_amsupport 3
#define Anum_pg_am_amsupport 3
...
@@ -87,37 +88,38 @@ typedef FormData_pg_am *Form_pg_am;
...
@@ -87,37 +88,38 @@ typedef FormData_pg_am *Form_pg_am;
#define Anum_pg_am_amcanmulticol 6
#define Anum_pg_am_amcanmulticol 6
#define Anum_pg_am_amoptionalkey 7
#define Anum_pg_am_amoptionalkey 7
#define Anum_pg_am_amindexnulls 8
#define Anum_pg_am_amindexnulls 8
#define Anum_pg_am_amstorage 9
#define Anum_pg_am_amsearchnulls 9
#define Anum_pg_am_amclusterable 10
#define Anum_pg_am_amstorage 10
#define Anum_pg_am_aminsert 11
#define Anum_pg_am_amclusterable 11
#define Anum_pg_am_ambeginscan 12
#define Anum_pg_am_aminsert 12
#define Anum_pg_am_amgettuple 13
#define Anum_pg_am_ambeginscan 13
#define Anum_pg_am_amgetmulti 14
#define Anum_pg_am_amgettuple 14
#define Anum_pg_am_amrescan 15
#define Anum_pg_am_amgetmulti 15
#define Anum_pg_am_amendscan 16
#define Anum_pg_am_amrescan 16
#define Anum_pg_am_ammarkpos 17
#define Anum_pg_am_amendscan 17
#define Anum_pg_am_amrestrpos 18
#define Anum_pg_am_ammarkpos 18
#define Anum_pg_am_ambuild 19
#define Anum_pg_am_amrestrpos 19
#define Anum_pg_am_ambulkdelete 20
#define Anum_pg_am_ambuild 20
#define Anum_pg_am_amvacuumcleanup 21
#define Anum_pg_am_ambulkdelete 21
#define Anum_pg_am_amcostestimate 22
#define Anum_pg_am_amvacuumcleanup 22
#define Anum_pg_am_amoptions 23
#define Anum_pg_am_amcostestimate 23
#define Anum_pg_am_amoptions 24
/* ----------------
/* ----------------
* initial contents of pg_am
* initial contents of pg_am
* ----------------
* ----------------
*/
*/
DATA
(
insert
OID
=
403
(
btree
5
1
t
t
t
t
t
f
t
btinsert
btbeginscan
btgettuple
btgetmulti
btrescan
btendscan
btmarkpos
btrestrpos
btbuild
btbulkdelete
btvacuumcleanup
btcostestimate
btoptions
));
DATA
(
insert
OID
=
403
(
btree
5
1
t
t
t
t
t
t
f
t
btinsert
btbeginscan
btgettuple
btgetmulti
btrescan
btendscan
btmarkpos
btrestrpos
btbuild
btbulkdelete
btvacuumcleanup
btcostestimate
btoptions
));
DESCR
(
"b-tree index access method"
);
DESCR
(
"b-tree index access method"
);
#define BTREE_AM_OID 403
#define BTREE_AM_OID 403
DATA
(
insert
OID
=
405
(
hash
1
1
f
f
f
f
f
f
f
hashinsert
hashbeginscan
hashgettuple
hashgetmulti
hashrescan
hashendscan
hashmarkpos
hashrestrpos
hashbuild
hashbulkdelete
hashvacuumcleanup
hashcostestimate
hashoptions
));
DATA
(
insert
OID
=
405
(
hash
1
1
f
f
f
f
f
f
f
f
hashinsert
hashbeginscan
hashgettuple
hashgetmulti
hashrescan
hashendscan
hashmarkpos
hashrestrpos
hashbuild
hashbulkdelete
hashvacuumcleanup
hashcostestimate
hashoptions
));
DESCR
(
"hash index access method"
);
DESCR
(
"hash index access method"
);
#define HASH_AM_OID 405
#define HASH_AM_OID 405
DATA
(
insert
OID
=
783
(
gist
0
7
f
f
t
t
t
t
t
gistinsert
gistbeginscan
gistgettuple
gistgetmulti
gistrescan
gistendscan
gistmarkpos
gistrestrpos
gistbuild
gistbulkdelete
gistvacuumcleanup
gistcostestimate
gistoptions
));
DATA
(
insert
OID
=
783
(
gist
0
7
f
f
t
t
t
t
t
t
gistinsert
gistbeginscan
gistgettuple
gistgetmulti
gistrescan
gistendscan
gistmarkpos
gistrestrpos
gistbuild
gistbulkdelete
gistvacuumcleanup
gistcostestimate
gistoptions
));
DESCR
(
"GiST index access method"
);
DESCR
(
"GiST index access method"
);
#define GIST_AM_OID 783
#define GIST_AM_OID 783
DATA
(
insert
OID
=
2742
(
gin
0
4
f
f
f
f
f
t
f
gininsert
ginbeginscan
gingettuple
gingetmulti
ginrescan
ginendscan
ginmarkpos
ginrestrpos
ginbuild
ginbulkdelete
ginvacuumcleanup
gincostestimate
ginoptions
));
DATA
(
insert
OID
=
2742
(
gin
0
4
f
f
f
f
f
f
t
f
gininsert
ginbeginscan
gingettuple
gingetmulti
ginrescan
ginendscan
ginmarkpos
ginrestrpos
ginbuild
ginbulkdelete
ginvacuumcleanup
gincostestimate
ginoptions
));
DESCR
(
"GIN index access method"
);
DESCR
(
"GIN index access method"
);
#define GIN_AM_OID 2742
#define GIN_AM_OID 2742
...
...
src/include/nodes/relation.h
View file @
f02a82b6
...
@@ -7,7 +7,7 @@
...
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.1
39 2007/02/27 01:11:26
tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.1
40 2007/04/06 22:33:43
tgl Exp $
*
*
*-------------------------------------------------------------------------
*-------------------------------------------------------------------------
*/
*/
...
@@ -397,6 +397,7 @@ typedef struct IndexOptInfo
...
@@ -397,6 +397,7 @@ typedef struct IndexOptInfo
bool
predOK
;
/* true if predicate matches query */
bool
predOK
;
/* true if predicate matches query */
bool
unique
;
/* true if a unique index */
bool
unique
;
/* true if a unique index */
bool
amoptionalkey
;
/* can query omit key for the first column? */
bool
amoptionalkey
;
/* can query omit key for the first column? */
bool
amsearchnulls
;
/* can AM search for NULL index entries? */
}
IndexOptInfo
;
}
IndexOptInfo
;
...
...
src/test/regress/expected/create_index.out
View file @
f02a82b6
...
@@ -75,6 +75,12 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
...
@@ -75,6 +75,12 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
2
2
(1 row)
(1 row)
SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
count
-------
278
(1 row)
SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon
SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon
ORDER BY (poly_center(f1))[0];
ORDER BY (poly_center(f1))[0];
f1
f1
...
@@ -125,6 +131,12 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
...
@@ -125,6 +131,12 @@ SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
2
2
(1 row)
(1 row)
SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
count
-------
278
(1 row)
SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon
SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon
ORDER BY (poly_center(f1))[0];
ORDER BY (poly_center(f1))[0];
f1
f1
...
@@ -410,3 +422,71 @@ Indexes:
...
@@ -410,3 +422,71 @@ Indexes:
"std_index" btree (f2)
"std_index" btree (f2)
DROP TABLE concur_heap;
DROP TABLE concur_heap;
--
-- Tests for IS NULL with b-tree indexes
--
SELECT unique1, unique2 INTO onek_with_null FROM onek;
INSERT INTO onek_with_null (unique1,unique2) VALUES (NULL, -1), (NULL, NULL);
CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2,unique1);
SET enable_seqscan = OFF;
SET enable_indexscan = ON;
SET enable_bitmapscan = ON;
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL;
count
-------
2
(1 row)
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
count
-------
1
(1 row)
DROP INDEX onek_nulltest;
CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 desc,unique1);
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL;
count
-------
2
(1 row)
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
count
-------
1
(1 row)
DROP INDEX onek_nulltest;
CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 desc nulls last,unique1);
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL;
count
-------
2
(1 row)
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
count
-------
1
(1 row)
DROP INDEX onek_nulltest;
CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 nulls first,unique1);
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL;
count
-------
2
(1 row)
SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
count
-------
1
(1 row)
RESET enable_seqscan;
RESET enable_indexscan;
RESET enable_bitmapscan;
DROP TABLE onek_with_null;
src/test/regress/sql/create_index.sql
View file @
f02a82b6
...
@@ -96,6 +96,8 @@ SELECT * FROM fast_emp4000
...
@@ -96,6 +96,8 @@ SELECT * FROM fast_emp4000
SELECT
count
(
*
)
FROM
fast_emp4000
WHERE
home_base
&&
'(1000,1000,0,0)'
::
box
;
SELECT
count
(
*
)
FROM
fast_emp4000
WHERE
home_base
&&
'(1000,1000,0,0)'
::
box
;
SELECT
count
(
*
)
FROM
fast_emp4000
WHERE
home_base
IS
NULL
;
SELECT
*
FROM
polygon_tbl
WHERE
f1
~
'((1,1),(2,2),(2,1))'
::
polygon
SELECT
*
FROM
polygon_tbl
WHERE
f1
~
'((1,1),(2,2),(2,1))'
::
polygon
ORDER
BY
(
poly_center
(
f1
))[
0
];
ORDER
BY
(
poly_center
(
f1
))[
0
];
...
@@ -119,6 +121,8 @@ SELECT * FROM fast_emp4000
...
@@ -119,6 +121,8 @@ SELECT * FROM fast_emp4000
SELECT
count
(
*
)
FROM
fast_emp4000
WHERE
home_base
&&
'(1000,1000,0,0)'
::
box
;
SELECT
count
(
*
)
FROM
fast_emp4000
WHERE
home_base
&&
'(1000,1000,0,0)'
::
box
;
SELECT
count
(
*
)
FROM
fast_emp4000
WHERE
home_base
IS
NULL
;
SELECT
*
FROM
polygon_tbl
WHERE
f1
~
'((1,1),(2,2),(2,1))'
::
polygon
SELECT
*
FROM
polygon_tbl
WHERE
f1
~
'((1,1),(2,2),(2,1))'
::
polygon
ORDER
BY
(
poly_center
(
f1
))[
0
];
ORDER
BY
(
poly_center
(
f1
))[
0
];
...
@@ -259,3 +263,45 @@ COMMIT;
...
@@ -259,3 +263,45 @@ COMMIT;
\
d
concur_heap
\
d
concur_heap
DROP
TABLE
concur_heap
;
DROP
TABLE
concur_heap
;
--
-- Tests for IS NULL with b-tree indexes
--
SELECT
unique1
,
unique2
INTO
onek_with_null
FROM
onek
;
INSERT
INTO
onek_with_null
(
unique1
,
unique2
)
VALUES
(
NULL
,
-
1
),
(
NULL
,
NULL
);
CREATE
UNIQUE
INDEX
onek_nulltest
ON
onek_with_null
(
unique2
,
unique1
);
SET
enable_seqscan
=
OFF
;
SET
enable_indexscan
=
ON
;
SET
enable_bitmapscan
=
ON
;
SELECT
count
(
*
)
FROM
onek_with_null
WHERE
unique1
IS
NULL
;
SELECT
count
(
*
)
FROM
onek_with_null
WHERE
unique1
IS
NULL
AND
unique2
IS
NULL
;
DROP
INDEX
onek_nulltest
;
CREATE
UNIQUE
INDEX
onek_nulltest
ON
onek_with_null
(
unique2
desc
,
unique1
);
SELECT
count
(
*
)
FROM
onek_with_null
WHERE
unique1
IS
NULL
;
SELECT
count
(
*
)
FROM
onek_with_null
WHERE
unique1
IS
NULL
AND
unique2
IS
NULL
;
DROP
INDEX
onek_nulltest
;
CREATE
UNIQUE
INDEX
onek_nulltest
ON
onek_with_null
(
unique2
desc
nulls
last
,
unique1
);
SELECT
count
(
*
)
FROM
onek_with_null
WHERE
unique1
IS
NULL
;
SELECT
count
(
*
)
FROM
onek_with_null
WHERE
unique1
IS
NULL
AND
unique2
IS
NULL
;
DROP
INDEX
onek_nulltest
;
CREATE
UNIQUE
INDEX
onek_nulltest
ON
onek_with_null
(
unique2
nulls
first
,
unique1
);
SELECT
count
(
*
)
FROM
onek_with_null
WHERE
unique1
IS
NULL
;
SELECT
count
(
*
)
FROM
onek_with_null
WHERE
unique1
IS
NULL
AND
unique2
IS
NULL
;
RESET
enable_seqscan
;
RESET
enable_indexscan
;
RESET
enable_bitmapscan
;
DROP
TABLE
onek_with_null
;
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