aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access')
-rw-r--r--src/backend/access/common/scankey.c13
-rw-r--r--src/backend/access/gist/gistget.c58
-rw-r--r--src/backend/access/nbtree/nbtsearch.c4
-rw-r--r--src/backend/access/nbtree/nbtutils.c93
4 files changed, 121 insertions, 47 deletions
diff --git a/src/backend/access/common/scankey.c b/src/backend/access/common/scankey.c
index a93c72e0932..21fccc23a2c 100644
--- a/src/backend/access/common/scankey.c
+++ b/src/backend/access/common/scankey.c
@@ -8,7 +8,7 @@
*
*
* 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 @@
/*
* ScanKeyEntryInitialize
* 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
* itself, because that's what will be used for any subsidiary info attached
@@ -40,7 +41,13 @@ ScanKeyEntryInitialize(ScanKey entry,
entry->sk_strategy = strategy;
entry->sk_subtype = subtype;
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));
+ }
}
/*
diff --git a/src/backend/access/gist/gistget.c b/src/backend/access/gist/gistget.c
index f1d2c777c2f..226812322aa 100644
--- a/src/backend/access/gist/gistget.c
+++ b/src/backend/access/gist/gistget.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/gist/gistget.c,v 1.64 2007/01/20 18:43:35 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/access/gist/gistget.c,v 1.65 2007/04/06 22:33:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -381,37 +381,45 @@ gistindex_keytest(IndexTuple tuple,
if (key->sk_flags & SK_ISNULL)
{
/*
- * is the compared-to datum NULL? on non-leaf page it's possible
- * to have nulls in childs :(
+ * On non-leaf page we can't conclude that child hasn't NULL
+ * 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))
- return true;
- return false;
+ Assert( key->sk_flags & SK_SEARCHNULL );
+
+ if ( GistPageIsLeaf(p) && !isNull )
+ return false;
}
else if (isNull)
+ {
return false;
+ }
+ else
+ {
- gistdentryinit(giststate, key->sk_attno - 1, &de,
- datum, r, p, offset,
- FALSE, isNull);
+ gistdentryinit(giststate, key->sk_attno - 1, &de,
+ datum, r, p, offset,
+ FALSE, isNull);
- /*
- * Call the Consistent function to evaluate the test. The arguments
- * are the index datum (as a GISTENTRY*), the comparison datum, and
- * the comparison operator's strategy number and subtype from pg_amop.
- *
- * (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.)
- */
- test = FunctionCall4(&key->sk_func,
- PointerGetDatum(&de),
- key->sk_argument,
- Int32GetDatum(key->sk_strategy),
- ObjectIdGetDatum(key->sk_subtype));
-
- if (!DatumGetBool(test))
- return false;
+ /*
+ * Call the Consistent function to evaluate the test. The arguments
+ * are the index datum (as a GISTENTRY*), the comparison datum, and
+ * the comparison operator's strategy number and subtype from pg_amop.
+ *
+ * (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.)
+ */
+ test = FunctionCall4(&key->sk_func,
+ PointerGetDatum(&de),
+ key->sk_argument,
+ Int32GetDatum(key->sk_strategy),
+ ObjectIdGetDatum(key->sk_subtype));
+
+ if (!DatumGetBool(test))
+ return false;
+ }
keySize--;
key++;
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index fc8b18a2e90..036a97a8d04 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.111 2007/01/09 02:14:10 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtsearch.c,v 1.112 2007/04/06 22:33:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -746,8 +746,6 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
*
* If goback = true, we will then step back one item, while if
* 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)
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 9c227d7f6c6..b5e7686303e 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.83 2007/03/30 00:12:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.84 2007/04/06 22:33:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -264,12 +264,27 @@ _bt_preprocess_keys(IndexScanDesc scan)
if (numberOfKeys == 1)
{
/*
- * We don't use indices for 'A is null' and 'A is not null' currently
- * and 'A < = > <> NULL' will always fail - so qual is not OK if
- * comparison value is NULL. - vadim 03/21/97
+ * We treat all btree operators as strict (even if they're not so
+ * marked in pg_proc). This means that it is impossible for an
+ * 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)
- 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);
memcpy(outkeys, cur, sizeof(ScanKeyData));
so->numberOfKeys = 1;
@@ -303,17 +318,20 @@ _bt_preprocess_keys(IndexScanDesc scan)
{
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 */
if (cur->sk_flags & SK_ISNULL)
{
- so->qual_ok = false;
-
- /*
- * Quit processing so we don't try to invoke comparison
- * routines on NULLs.
- */
- return;
+ if (cur->sk_flags & SK_SEARCHNULL)
+ {
+ cur->sk_strategy = BTEqualStrategyNumber;
+ cur->sk_subtype = InvalidOid;
+ }
+ else
+ {
+ so->qual_ok = false;
+ return;
+ }
}
}
@@ -344,6 +362,14 @@ _bt_preprocess_keys(IndexScanDesc scan)
if (!chk || j == (BTEqualStrategyNumber - 1))
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,
&test_result))
{
@@ -455,6 +481,23 @@ _bt_preprocess_keys(IndexScanDesc scan)
else
{
/* 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],
&test_result))
{
@@ -798,11 +841,29 @@ _bt_checkkeys(IndexScanDesc scan,
tupdesc,
&isNull);
- /* btree doesn't support 'A is null' clauses, yet */
if (key->sk_flags & SK_ISNULL)
{
- /* we shouldn't get here, really; see _bt_preprocess_keys() */
- *continuescan = false;
+ /* Handle IS NULL tests */
+ 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;
}