diff options
Diffstat (limited to 'src/backend/access')
-rw-r--r-- | src/backend/access/common/scankey.c | 13 | ||||
-rw-r--r-- | src/backend/access/gist/gistget.c | 58 | ||||
-rw-r--r-- | src/backend/access/nbtree/nbtsearch.c | 4 | ||||
-rw-r--r-- | src/backend/access/nbtree/nbtutils.c | 93 |
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; } |