0007-Implement-get_partitions_for_keys-v11.patch
text/plain
Filename: 0007-Implement-get_partitions_for_keys-v11.patch
Type: text/plain
Part: 6
Patch
Format: format-patch
Series: patch v11-0007
Subject: Implement get_partitions_for_keys
| File | + | − |
|---|---|---|
| src/backend/catalog/partition.c | 353 | 2 |
| src/test/regress/expected/inherit.out | 1 | 3 |
| src/test/regress/expected/partition.out | 37 | 84 |
From 6b04f3560b673005fa8b9a81262754227349af89 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Wed, 18 Oct 2017 17:14:53 +0900
Subject: [PATCH 7/7] Implement get_partitions_for_keys
Disable constraint_exclusion using internal partition constraints.
---
src/backend/catalog/partition.c | 355 +++++++++++++++++++++++++++++++-
src/test/regress/expected/inherit.out | 4 +-
src/test/regress/expected/partition.out | 121 ++++-------
3 files changed, 391 insertions(+), 89 deletions(-)
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index fb973ef20c..c58c7354c6 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -2409,10 +2409,361 @@ partition_cmp_args(Oid partopfamily, Oid partopcintype,
static Bitmapset *
get_partitions_for_keys(Relation rel, PartScanKeyInfo *keys)
{
- PartitionDesc partdesc = RelationGetPartitionDesc(rel);
+ PartitionKey partkey = RelationGetPartitionKey(rel);
+ PartitionDesc partdesc = RelationGetPartitionDesc(rel);
+ PartitionBoundInfo boundinfo = partdesc->boundinfo;
Bitmapset *result = NULL;
+ PartitionBoundCmpArg arg;
+ int i,
+ eqoff,
+ minoff,
+ maxoff;
+ bool is_equal;
+
+ /* Return an empty set if no partitions to see. */
+ if (partdesc->nparts == 0)
+ return NULL;
+
+ /*
+ * Check if any of the scan keys are null. If so, return the only
+ * null-accepting partition if boundinfo says there is one.
+ */
+ for (i = 0; i < partkey->partnatts; i++)
+ {
+ if (keys->keynullness[i] == IS_NULL)
+ {
+ int other_idx = -1;
+
+ /*
+ * Note that only one of the null-accepting partition and the
+ * default partition can be holding null values at any given
+ * time.
+ */
+ if (partition_bound_accepts_nulls(boundinfo)||
+ partition_bound_has_default(boundinfo))
+ other_idx = partition_bound_accepts_nulls(boundinfo)
+ ? boundinfo->null_index
+ : boundinfo->default_index;
+ if (other_idx >= 0)
+ result = bms_make_singleton(other_idx);
+
+ return result;
+ }
+ }
+
+ /*
+ * If there are no datums to compare keys with, but there exists a
+ * partition, the latter must be a partition that accepts only nulls
+ * or a default partition. If it is the former and we didn't already
+ * return it as the only scannable partition, that means the query
+ * doesn't want null values in its output. So, all of what the query
+ * wants instead must be in the default partition.
+ */
+ if (boundinfo->ndatums == 0)
+ {
+ if (partition_bound_has_default(boundinfo))
+ result = bms_make_singleton(boundinfo->default_index);
+ return result;
+ }
+ /* No bounding keys, so just return all partitions. */
+ else if (keys->n_eqkeys + keys->n_minkeys + keys->n_maxkeys == 0)
+ {
+ result = bms_add_range(result, 0, partdesc->nparts - 1);
+ return result;
+ }
+
+ /* Valid keys->eqkeys must provide all partition keys. */
+ Assert(keys->n_eqkeys == 0 || keys->n_eqkeys == partkey->partnatts);
+ if (keys->n_eqkeys > 0)
+ {
+ memset(&arg, 0, sizeof(PartitionBoundCmpArg));
+ arg.datums = keys->eqkeys;
+ arg.ndatums = keys->n_eqkeys;
+ eqoff = partition_bound_bsearch(partkey, boundinfo, &arg, &is_equal);
+
+ if (eqoff >= 0)
+ {
+ switch (partkey->strategy)
+ {
+ case PARTITION_STRATEGY_LIST:
+ /* For list partition, must exactly match the datum. */
+ if (!is_equal)
+ eqoff = -1;
+ break;
+
+ case PARTITION_STRATEGY_RANGE:
+ /*
+ * eqoff is gives us the bound that is known to be <=
+ * eqkeys given how partition_bound_bsearch works. The
+ * bound at eqoff + 1, then, would be the upper bound of
+ * the only partition that needs to be scanned.
+ */
+ if (partkey->strategy == PARTITION_STRATEGY_RANGE)
+ eqoff += 1;
+ }
+ }
- result = bms_add_range(result, 0, partdesc->nparts - 1);
+ /*
+ * Ask later code to include the default partition, because eqkeys
+ * didn't identify a specific partition or identified a range
+ * of unassigned values.
+ */
+ if (eqoff >= 0 && boundinfo->indexes[eqoff] >= 0)
+ result = bms_make_singleton(boundinfo->indexes[eqoff]);
+ else if (partition_bound_has_default(boundinfo))
+ result = bms_make_singleton(boundinfo->default_index);
+
+ /* There are no minkeys and maxkeys when eqkeys is valid. */
+ return result;
+ }
+
+ /*
+ * Find the leftmost bound that satisfies the query, i.e., one that
+ * satisfies minkeys.
+ */
+ minoff = 0;
+ if (keys->n_minkeys > 0)
+ {
+ memset(&arg, 0, sizeof(PartitionBoundCmpArg));
+ arg.datums = keys->minkeys;
+ arg.ndatums = keys->n_minkeys;
+ minoff = partition_bound_bsearch(partkey, boundinfo, &arg, &is_equal);
+
+ switch (partkey->strategy)
+ {
+ case PARTITION_STRATEGY_LIST:
+
+ /*
+ * minoff set to -1 means all datums are greater than
+ * minkeys, which means all partitions satisfy minkeys.
+ */
+ if (minoff == -1)
+ minoff = 0;
+
+ /*
+ * minkeys matched one of the datums (because, is_equal), but
+ * the query may have asked to exclude that value. If so,
+ * move to the bound on the right, which doesn't necessarily
+ * mean we're excluding the list partition containing that
+ * value, because there very well might be values in the range
+ * thus selected that belong to the partition to which the
+ * matched value (minkeys) also belongs.
+ */
+ if (is_equal && !keys->min_incl)
+ minoff++;
+ break;
+
+ case PARTITION_STRATEGY_RANGE:
+ /*
+ * If only a prefix of the whole partition key is provided,
+ * there will be multiple partitions whose bound share the
+ * same prefix. If minkey is inclusive, we must make minoff
+ * point to the leftmost such bound, making the result contain
+ * all such partitions. If it is exclusive, we must move
+ * minoff to the right such that minoff points to the first
+ * partition whose bound is greater than this prefix, thus
+ * excluding all aforementioned partitions from appearing in
+ * the result.
+ */
+ if (is_equal && arg.ndatums < partkey->partnatts)
+ {
+ int32 cmpval;
+
+ is_equal = false;
+ do
+ {
+ if (keys->min_incl)
+ minoff -= 1;
+ else
+ minoff += 1;
+ if (minoff < 0 || minoff >= boundinfo->ndatums)
+ break;
+ cmpval = partition_bound_cmp(partkey, boundinfo,
+ minoff, &arg);
+ } while (cmpval == 0);
+
+ /* Back up if went too far. */
+ if (!keys->min_incl)
+ minoff -= 1;
+ }
+
+ /*
+ * At this point, minoff gives us the leftmost bound that is
+ * known to be <= query's minkey. The bound at minoff + 1 (if
+ * there is one), then, would be the upper bound of the
+ * leftmost partition that needs to be scanned.
+ */
+ if (minoff < boundinfo->ndatums - 1)
+ minoff += 1;
+ break;
+ }
+ }
+
+ /*
+ * Find the rightmost bound that satisfies the query, i.e., one that
+ * satisfies maxkeys.
+ */
+ if (partkey->strategy == PARTITION_STRATEGY_RANGE)
+ /* 1 more index than datums in this case */
+ maxoff = boundinfo->ndatums;
+ else
+ maxoff = boundinfo->ndatums - 1;
+ if (keys->n_maxkeys > 0)
+ {
+ memset(&arg, 0, sizeof(PartitionBoundCmpArg));
+ arg.datums = keys->maxkeys;
+ arg.ndatums = keys->n_maxkeys;
+ maxoff = partition_bound_bsearch(partkey, boundinfo, &arg, &is_equal);
+
+ switch (partkey->strategy)
+ {
+ case PARTITION_STRATEGY_LIST:
+ /* See the comment above for minkeys. */
+ if (is_equal && !keys->max_incl)
+ maxoff--;
+ break;
+
+ case PARTITION_STRATEGY_RANGE:
+ /* See the comment above for minkeys. */
+ if (is_equal && arg.ndatums < partkey->partnatts)
+ {
+ int32 cmpval;
+
+ is_equal = false;
+ do
+ {
+ if (keys->max_incl)
+ maxoff += 1;
+ else
+ maxoff -= 1;
+ if (maxoff < 0 || maxoff >= boundinfo->ndatums)
+ break;
+ cmpval = partition_bound_cmp(partkey, boundinfo,
+ maxoff, &arg);
+ } while (cmpval == 0);
+
+ /* Back up if went too far. */
+ if (keys->max_incl)
+ maxoff -= 1;
+ }
+
+ /*
+ * At this point, maxoff gives us the rightmost bound that is
+ * known to be <= query's maxkey. The bound at maxoff + 1,
+ * then, would be the upper bound of the rightmost partition
+ * that needs to be scanned. Although, if the bound is equal
+ * to maxkeys and the latter is not inclusive, then the bound
+ * at maxoff itself is the upper bound of the rightmost
+ * partition that needs to be scanned.
+ */
+ if (!is_equal || keys->max_incl)
+ maxoff += 1;
+
+ break;
+ }
+ }
+
+ /*
+ * minoff or maxoff set to -1 means none of the datums in
+ * PartitionBoundInfo satisfies both minkeys and maxkeys. If both are set
+ * to a valid datum offset, that means there exists at least some
+ * datums (and hence partitions) satisfying both minkeys and maxkeys.
+ */
+ if (minoff >= 0 && maxoff >= 0)
+ {
+ bool list_include_def = false,
+ range_include_def = false;
+
+ switch (partkey->strategy)
+ {
+ case PARTITION_STRATEGY_LIST:
+ /*
+ * Add to the other_parts, list partition indexes are not
+ * monotonously increasing like range partitions' are.
+ */
+ for (i = minoff; i <= maxoff; i++)
+ result = bms_add_member(result, boundinfo->indexes[i]);
+
+ /*
+ * For range queries, always include the default list
+ * partition. Because list partitions divide the key space
+ * in a discontinuous manner, not all values in the given
+ * range will have a partition assigned.
+ */
+ list_include_def = true;
+ break;
+
+ case PARTITION_STRATEGY_RANGE:
+ /*
+ * If the bound at minoff or maxoff looks like it's an upper
+ * bound of an unassigned range of values, move to the
+ * adjacent bound which must be the upper bound of the
+ * leftmost or rightmost partition, respectively, that needs
+ * to be scanned.
+ *
+ * By doing that, we skip over a portion of values that do
+ * indeed satisfy the query, but don't have a valid partition
+ * assigned. The default partition would've been included to
+ * cover those values. Although, if the original bound in
+ * question is an infinite value, there would not be any
+ * unassigned range to speak of, because the range is unbounded
+ * in that direction by definition, so no need to include the
+ * default.
+ */
+ if (boundinfo->indexes[minoff] < 0)
+ {
+ int lastkey = partkey->partnatts - 1;
+
+ if (keys->n_minkeys > 0)
+ lastkey = keys->n_minkeys - 1;
+ if (boundinfo->kind[minoff][lastkey] ==
+ PARTITION_RANGE_DATUM_VALUE)
+ {
+ range_include_def = true;
+ }
+ minoff += 1;
+ }
+
+ if (maxoff >= 1 && boundinfo->indexes[maxoff] < 0)
+ {
+ int lastkey = partkey->partnatts - 1;
+
+ if (keys->n_maxkeys > 0)
+ lastkey = keys->n_maxkeys - 1;
+ if (boundinfo->kind[maxoff - 1][lastkey] ==
+ PARTITION_RANGE_DATUM_VALUE)
+ {
+ range_include_def = true;
+ }
+ maxoff -= 1;
+ }
+
+ if (minoff <= maxoff)
+ result = bms_add_range(result,
+ boundinfo->indexes[minoff],
+ boundinfo->indexes[maxoff]);
+ /*
+ * There might exist a range of values unassigned to any
+ * non-default range partition between the datums at
+ * minoff and maxoff.
+ */
+ for (i = minoff; i <= maxoff; i++)
+ {
+ if (boundinfo->indexes[i] < 0)
+ {
+ range_include_def = true;
+ break;
+ }
+ }
+ break;
+ }
+
+ if ((list_include_def || range_include_def) &&
+ partition_bound_has_default(boundinfo))
+ result = bms_add_member(result, boundinfo->default_index);
+ }
+ else if (partition_bound_has_default(boundinfo))
+ result = bms_add_member(result, boundinfo->default_index);
return result;
}
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index c698faff2f..661f137122 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1713,11 +1713,9 @@ explain (costs off) select * from list_parted where a = 'ab' or a in (null, 'cd'
Append
-> Seq Scan on part_ab_cd
Filter: (((a)::text = 'ab'::text) OR ((a)::text = ANY ('{NULL,cd}'::text[])))
- -> Seq Scan on part_ef_gh
- Filter: (((a)::text = 'ab'::text) OR ((a)::text = ANY ('{NULL,cd}'::text[])))
-> Seq Scan on part_null_xy
Filter: (((a)::text = 'ab'::text) OR ((a)::text = ANY ('{NULL,cd}'::text[])))
-(7 rows)
+(5 rows)
explain (costs off) select * from list_parted where a = 'ab';
QUERY PLAN
diff --git a/src/test/regress/expected/partition.out b/src/test/regress/expected/partition.out
index 484c6fe750..206c6de5b7 100644
--- a/src/test/regress/expected/partition.out
+++ b/src/test/regress/expected/partition.out
@@ -208,16 +208,14 @@ explain (costs off) select * from rlp where 1 > a; /* commutates */
(3 rows)
explain (costs off) select * from rlp where a <= 1;
- QUERY PLAN
----------------------------------------
+ QUERY PLAN
+--------------------------
Append
-> Seq Scan on rlp1
Filter: (a <= 1)
-> Seq Scan on rlp2
Filter: (a <= 1)
- -> Seq Scan on rlp_default_default
- Filter: (a <= 1)
-(7 rows)
+(5 rows)
explain (costs off) select * from rlp where a = 1;
QUERY PLAN
@@ -519,15 +517,13 @@ explain (costs off) select * from rlp where a <= 31;
Filter: (a <= 31)
-> Seq Scan on rlp5_1
Filter: (a <= 31)
- -> Seq Scan on rlp5_default
- Filter: (a <= 31)
-> Seq Scan on rlp_default_10
Filter: (a <= 31)
-> Seq Scan on rlp_default_30
Filter: (a <= 31)
-> Seq Scan on rlp_default_default
Filter: (a <= 31)
-(29 rows)
+(27 rows)
explain (costs off) select * from rlp where a = 1 or a = 7;
QUERY PLAN
@@ -575,9 +571,7 @@ explain (costs off) select * from rlp where a > 20 and a < 27;
Filter: ((a > 20) AND (a < 27))
-> Seq Scan on rlp4_2
Filter: ((a > 20) AND (a < 27))
- -> Seq Scan on rlp4_default
- Filter: ((a > 20) AND (a < 27))
-(7 rows)
+(5 rows)
explain (costs off) select * from rlp where a = 29;
QUERY PLAN
@@ -651,8 +645,6 @@ explain (costs off) select * from rlp where (a = 1 and a = 3) or (a > 1 and a =
QUERY PLAN
-------------------------------------------------------------------
Append
- -> Seq Scan on rlp2
- Filter: (((a = 1) AND (a = 3)) OR ((a > 1) AND (a = 15)))
-> Seq Scan on rlp3abcd
Filter: (((a = 1) AND (a = 3)) OR ((a > 1) AND (a = 15)))
-> Seq Scan on rlp3efgh
@@ -661,7 +653,7 @@ explain (costs off) select * from rlp where (a = 1 and a = 3) or (a > 1 and a =
Filter: (((a = 1) AND (a = 3)) OR ((a > 1) AND (a = 15)))
-> Seq Scan on rlp3_default
Filter: (((a = 1) AND (a = 3)) OR ((a > 1) AND (a = 15)))
-(11 rows)
+(9 rows)
-- multi-column keys
create table mc3p (a int, b int, c int) partition by range (a, abs(b), c);
@@ -675,16 +667,14 @@ create table mc3p5 partition of mc3p for values from (11, 1, 1) to (20, 10, 10);
create table mc3p6 partition of mc3p for values from (20, 10, 10) to (20, 20, 20);
create table mc3p7 partition of mc3p for values from (20, 20, 20) to (maxvalue, maxvalue, maxvalue);
explain (costs off) select * from mc3p where a = 1;
- QUERY PLAN
---------------------------------
+ QUERY PLAN
+-------------------------
Append
-> Seq Scan on mc3p0
Filter: (a = 1)
-> Seq Scan on mc3p1
Filter: (a = 1)
- -> Seq Scan on mc3p_default
- Filter: (a = 1)
-(7 rows)
+(5 rows)
explain (costs off) select * from mc3p where a = 1 and abs(b) < 1;
QUERY PLAN
@@ -702,9 +692,7 @@ explain (costs off) select * from mc3p where a = 1 and abs(b) = 1;
Filter: ((a = 1) AND (abs(b) = 1))
-> Seq Scan on mc3p1
Filter: ((a = 1) AND (abs(b) = 1))
- -> Seq Scan on mc3p_default
- Filter: ((a = 1) AND (abs(b) = 1))
-(7 rows)
+(5 rows)
explain (costs off) select * from mc3p where a = 1 and abs(b) = 1 and c < 8;
QUERY PLAN
@@ -714,9 +702,7 @@ explain (costs off) select * from mc3p where a = 1 and abs(b) = 1 and c < 8;
Filter: ((c < 8) AND (a = 1) AND (abs(b) = 1))
-> Seq Scan on mc3p1
Filter: ((c < 8) AND (a = 1) AND (abs(b) = 1))
- -> Seq Scan on mc3p_default
- Filter: ((c < 8) AND (a = 1) AND (abs(b) = 1))
-(7 rows)
+(5 rows)
explain (costs off) select * from mc3p where a = 10 and abs(b) between 5 and 35;
QUERY PLAN
@@ -730,9 +716,7 @@ explain (costs off) select * from mc3p where a = 10 and abs(b) between 5 and 35;
Filter: ((a = 10) AND (abs(b) >= 5) AND (abs(b) <= 35))
-> Seq Scan on mc3p4
Filter: ((a = 10) AND (abs(b) >= 5) AND (abs(b) <= 35))
- -> Seq Scan on mc3p_default
- Filter: ((a = 10) AND (abs(b) >= 5) AND (abs(b) <= 35))
-(11 rows)
+(9 rows)
explain (costs off) select * from mc3p where a > 10;
QUERY PLAN
@@ -771,16 +755,14 @@ explain (costs off) select * from mc3p where a >= 10;
(17 rows)
explain (costs off) select * from mc3p where a < 10;
- QUERY PLAN
---------------------------------
+ QUERY PLAN
+--------------------------
Append
-> Seq Scan on mc3p0
Filter: (a < 10)
-> Seq Scan on mc3p1
Filter: (a < 10)
- -> Seq Scan on mc3p_default
- Filter: (a < 10)
-(7 rows)
+(5 rows)
explain (costs off) select * from mc3p where a <= 10 and abs(b) < 10;
QUERY PLAN
@@ -792,9 +774,7 @@ explain (costs off) select * from mc3p where a <= 10 and abs(b) < 10;
Filter: ((a <= 10) AND (abs(b) < 10))
-> Seq Scan on mc3p2
Filter: ((a <= 10) AND (abs(b) < 10))
- -> Seq Scan on mc3p_default
- Filter: ((a <= 10) AND (abs(b) < 10))
-(9 rows)
+(7 rows)
explain (costs off) select * from mc3p where a = 11 and abs(b) = 0; /* empty */
QUERY PLAN
@@ -821,8 +801,8 @@ explain (costs off) select * from mc3p where a > 20;
(3 rows)
explain (costs off) select * from mc3p where a >= 20;
- QUERY PLAN
---------------------------------
+ QUERY PLAN
+---------------------------
Append
-> Seq Scan on mc3p5
Filter: (a >= 20)
@@ -830,9 +810,7 @@ explain (costs off) select * from mc3p where a >= 20;
Filter: (a >= 20)
-> Seq Scan on mc3p7
Filter: (a >= 20)
- -> Seq Scan on mc3p_default
- Filter: (a >= 20)
-(9 rows)
+(7 rows)
explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1 and c = 1) or (a = 10 and abs(b) = 5 and c = 10) or (a > 11 and a < 20);
QUERY PLAN
@@ -872,9 +850,7 @@ explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1 and c = 1) or
Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1) OR (a = 1))
-> Seq Scan on mc3p5
Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1) OR (a = 1))
- -> Seq Scan on mc3p_default
- Filter: (((a = 1) AND (abs(b) = 1) AND (c = 1)) OR ((a = 10) AND (abs(b) = 5) AND (c = 10)) OR ((a > 11) AND (a < 20)) OR (a < 1) OR (a = 1))
-(11 rows)
+(9 rows)
explain (costs off) select * from mc3p where a = 1 or abs(b) = 1 or c = 1;
QUERY PLAN
@@ -912,9 +888,7 @@ explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1) or (a = 10 a
Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10)))
-> Seq Scan on mc3p4
Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10)))
- -> Seq Scan on mc3p_default
- Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 10)))
-(13 rows)
+(11 rows)
explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1) or (a = 10 and abs(b) = 9);
QUERY PLAN
@@ -926,9 +900,7 @@ explain (costs off) select * from mc3p where (a = 1 and abs(b) = 1) or (a = 10 a
Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 9)))
-> Seq Scan on mc3p2
Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 9)))
- -> Seq Scan on mc3p_default
- Filter: (((a = 1) AND (abs(b) = 1)) OR ((a = 10) AND (abs(b) = 9)))
-(9 rows)
+(7 rows)
-- a simpler multi-column keys case
create table mc2p (a int, b int) partition by range (a, b);
@@ -999,28 +971,20 @@ explain (costs off) select * from boolpart where a in (true, false);
(5 rows)
explain (costs off) select * from boolpart where a = false;
- QUERY PLAN
-------------------------------------
+ QUERY PLAN
+------------------------------
Append
-> Seq Scan on boolpart_f
Filter: (NOT a)
- -> Seq Scan on boolpart_t
- Filter: (NOT a)
- -> Seq Scan on boolpart_default
- Filter: (NOT a)
-(7 rows)
+(3 rows)
explain (costs off) select * from boolpart where not a = false;
- QUERY PLAN
-------------------------------------
+ QUERY PLAN
+------------------------------
Append
- -> Seq Scan on boolpart_f
- Filter: a
-> Seq Scan on boolpart_t
Filter: a
- -> Seq Scan on boolpart_default
- Filter: a
-(7 rows)
+(3 rows)
explain (costs off) select * from boolpart where a is true or a is not true;
QUERY PLAN
@@ -1030,33 +994,22 @@ explain (costs off) select * from boolpart where a is true or a is not true;
Filter: ((a IS TRUE) OR (a IS NOT TRUE))
-> Seq Scan on boolpart_t
Filter: ((a IS TRUE) OR (a IS NOT TRUE))
- -> Seq Scan on boolpart_default
- Filter: ((a IS TRUE) OR (a IS NOT TRUE))
-(7 rows)
+(5 rows)
explain (costs off) select * from boolpart where a is not true;
- QUERY PLAN
-------------------------------------
+ QUERY PLAN
+---------------------------------
Append
-> Seq Scan on boolpart_f
Filter: (a IS NOT TRUE)
- -> Seq Scan on boolpart_t
- Filter: (a IS NOT TRUE)
- -> Seq Scan on boolpart_default
- Filter: (a IS NOT TRUE)
-(7 rows)
+(3 rows)
explain (costs off) select * from boolpart where a is not true and a is not false;
- QUERY PLAN
---------------------------------------------------------
- Append
- -> Seq Scan on boolpart_f
- Filter: ((a IS NOT TRUE) AND (a IS NOT FALSE))
- -> Seq Scan on boolpart_t
- Filter: ((a IS NOT TRUE) AND (a IS NOT FALSE))
- -> Seq Scan on boolpart_default
- Filter: ((a IS NOT TRUE) AND (a IS NOT FALSE))
-(7 rows)
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
explain (costs off) select * from boolpart where a is unknown;
QUERY PLAN
--
2.11.0