diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c index 21c05be3c1..a7a3d7a12d 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.96 2010/01/02 16:57:35 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.97 2010/01/03 05:39:08 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,7 +32,7 @@ static bool _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op, ScanKey leftarg, ScanKey rightarg, bool *result); -static void _bt_mark_scankey_with_indoption(ScanKey skey, int16 *indoption); +static bool _bt_fix_scankey_strategy(ScanKey skey, int16 *indoption); static void _bt_mark_scankey_required(ScanKey skey); static bool _bt_check_rowcompare(ScanKey skey, IndexTuple tuple, TupleDesc tupdesc, @@ -265,49 +265,9 @@ _bt_preprocess_keys(IndexScanDesc scan) /* We can short-circuit most of the work if there's just one key */ if (numberOfKeys == 1) { - /* - * 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. - * - * Likewise, "x IS NOT NULL" is supported. We treat that as either - * "less than NULL" in a NULLS LAST index, or "greater than NULL" - * in a NULLS FIRST index. However, we have to flip those around in - * a DESC index, to allow for the re-flipping that occurs elsewhere. - */ - if (cur->sk_flags & SK_ISNULL) - { - if (cur->sk_flags & SK_SEARCHNULL) - { - cur->sk_strategy = BTEqualStrategyNumber; - cur->sk_subtype = InvalidOid; - } - else if (cur->sk_flags & SK_SEARCHNOTNULL) - { - switch (indoption[cur->sk_attno - 1] & - (INDOPTION_DESC | INDOPTION_NULLS_FIRST)) - { - case 0: /* ASC / NULLS LAST */ - case INDOPTION_DESC | INDOPTION_NULLS_FIRST: - cur->sk_strategy = BTLessStrategyNumber; - break; - default: - cur->sk_strategy = BTGreaterStrategyNumber; - break; - } - cur->sk_subtype = InvalidOid; - } - else - so->qual_ok = false; - } - _bt_mark_scankey_with_indoption(cur, indoption); + /* Apply indoption to scankey (might change sk_strategy!) */ + if (!_bt_fix_scankey_strategy(cur, indoption)) + so->qual_ok = false; memcpy(outkeys, cur, sizeof(ScanKeyData)); so->numberOfKeys = 1; /* We can mark the qual as required if it's for first index col */ @@ -340,35 +300,12 @@ _bt_preprocess_keys(IndexScanDesc scan) { if (i < numberOfKeys) { - /* See comments above about NULLs and IS NULL/NOT NULL handling */ - /* Note: we assume SK_ISNULL is never set in a row header key */ - if (cur->sk_flags & SK_ISNULL) + /* Apply indoption to scankey (might change sk_strategy!) */ + if (!_bt_fix_scankey_strategy(cur, indoption)) { - if (cur->sk_flags & SK_SEARCHNULL) - { - cur->sk_strategy = BTEqualStrategyNumber; - cur->sk_subtype = InvalidOid; - } - else if (cur->sk_flags & SK_SEARCHNOTNULL) - { - switch (indoption[cur->sk_attno - 1] & - (INDOPTION_DESC | INDOPTION_NULLS_FIRST)) - { - case 0: /* ASC / NULLS LAST */ - case INDOPTION_DESC | INDOPTION_NULLS_FIRST: - cur->sk_strategy = BTLessStrategyNumber; - break; - default: - cur->sk_strategy = BTGreaterStrategyNumber; - break; - } - cur->sk_subtype = InvalidOid; - } - else - { - so->qual_ok = false; - return; - } + /* NULL can't be matched, so give up */ + so->qual_ok = false; + return; } } @@ -480,9 +417,6 @@ _bt_preprocess_keys(IndexScanDesc scan) memset(xform, 0, sizeof(xform)); } - /* apply indoption to scankey (might change sk_strategy!) */ - _bt_mark_scankey_with_indoption(cur, indoption); - /* check strategy this key's operator corresponds to */ j = cur->sk_strategy - 1; @@ -678,7 +612,7 @@ _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op, * indexscan initiated by syscache lookup will use cross-data-type * operators.) * - * If the sk_strategy was flipped by _bt_mark_scankey_with_indoption, we + * If the sk_strategy was flipped by _bt_fix_scankey_strategy, we * have to un-flip it to get the correct opfamily member. */ strat = op->sk_strategy; @@ -708,12 +642,20 @@ _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op, } /* - * Mark a scankey with info from the index's indoption array. + * Adjust a scankey's strategy and flags setting as needed for indoptions. * * We copy the appropriate indoption value into the scankey sk_flags * (shifting to avoid clobbering system-defined flag bits). Also, if * the DESC option is set, commute (flip) the operator strategy number. * + * A secondary purpose is to check for IS NULL/NOT NULL scankeys and set up + * the strategy field correctly for them. + * + * Lastly, for ordinary scankeys (not IS NULL/NOT NULL), we check for a + * NULL comparison value. Since all btree operators are assumed strict, + * a NULL means that the qual cannot be satisfied. We return TRUE if the + * comparison value isn't NULL, or FALSE if the scan should be abandoned. + * * This function is applied to the *input* scankey structure; therefore * on a rescan we will be looking at already-processed scankeys. Hence * we have to be careful not to re-commute the strategy if we already did it. @@ -721,16 +663,67 @@ _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op, * there shouldn't be any problem, since the index's indoptions are certainly * not going to change while the scankey survives. */ -static void -_bt_mark_scankey_with_indoption(ScanKey skey, int16 *indoption) +static bool +_bt_fix_scankey_strategy(ScanKey skey, int16 *indoption) { int addflags; addflags = indoption[skey->sk_attno - 1] << SK_BT_INDOPTION_SHIFT; + + /* + * 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. + * + * Likewise, "x IS NOT NULL" is supported. We treat that as either "less + * than NULL" in a NULLS LAST index, or "greater than NULL" in a NULLS + * FIRST index. + */ + if (skey->sk_flags & SK_ISNULL) + { + /* SK_ISNULL shouldn't be set in a row header scankey */ + Assert(!(skey->sk_flags & SK_ROW_HEADER)); + + /* Set indoption flags in scankey (might be done already) */ + skey->sk_flags |= addflags; + + /* Set correct strategy for IS NULL or NOT NULL search */ + if (skey->sk_flags & SK_SEARCHNULL) + { + skey->sk_strategy = BTEqualStrategyNumber; + skey->sk_subtype = InvalidOid; + } + else if (skey->sk_flags & SK_SEARCHNOTNULL) + { + if (skey->sk_flags & SK_BT_NULLS_FIRST) + skey->sk_strategy = BTGreaterStrategyNumber; + else + skey->sk_strategy = BTLessStrategyNumber; + skey->sk_subtype = InvalidOid; + } + else + { + /* regular qual, so it cannot be satisfied */ + return false; + } + + /* Needn't do the rest */ + return true; + } + + /* Adjust strategy for DESC, if we didn't already */ if ((addflags & SK_BT_DESC) && !(skey->sk_flags & SK_BT_DESC)) skey->sk_strategy = BTCommuteStrategyNumber(skey->sk_strategy); skey->sk_flags |= addflags; + /* If it's a row header, fix row member flags and strategies similarly */ if (skey->sk_flags & SK_ROW_HEADER) { ScanKey subkey = (ScanKey) DatumGetPointer(skey->sk_argument); @@ -747,6 +740,8 @@ _bt_mark_scankey_with_indoption(ScanKey skey, int16 *indoption) subkey++; } } + + return true; } /*