Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1huA1W-0001AC-A8 for pgsql-hackers@arkaria.postgresql.org; Sun, 04 Aug 2019 06:29:42 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1huA1U-0007Jk-6p for pgsql-hackers@arkaria.postgresql.org; Sun, 04 Aug 2019 06:29:40 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1huA1T-0007FU-QO for pgsql-hackers@lists.postgresql.org; Sun, 04 Aug 2019 06:29:39 +0000 Received: from mail-qt1-x844.google.com ([2607:f8b0:4864:20::844]) by magus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1huA1L-0005dQ-Gb for pgsql-hackers@lists.postgresql.org; Sun, 04 Aug 2019 06:29:39 +0000 Received: by mail-qt1-x844.google.com with SMTP id d23so78110110qto.2 for ; Sat, 03 Aug 2019 23:29:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=2ndquadrant-com.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:mime-version:content-disposition :content-transfer-encoding:in-reply-to:user-agent; bh=xcgbQh/5te5CLap8mXGCkq3WoCqMiMxhEDbgGveU+BQ=; b=AKKnFH1pjnwPPwwVCMzIHXphKvh8yh+zQsMvhlL+ID0DzU082rOiH+o4s32JFd+O/C KUmu+/6781EYgpYEuAaYAc0hQbBBbVnnvrXdeJI0h/l97NgqNVuje+xLZsSphiYw+ULn J0l/UTn2sGozw9RyFaeq2F4YX6aSPfWffZ7xXVGsyhur1zvnq9tJIK9G7vdednVQ47kk H3J2+P2nYDv51zGawYV2VWejb0U00q/ES99bzztiupFDc56PQmgE8c6iGqyaYSeRRfHU zy0Rj0bsiQVA7KoyCxyD6CFbsquPnhjeEuPRhGinP4f7xMnKhLs2bQf5kNUix+gSiJXc SE5w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition:content-transfer-encoding:in-reply-to :user-agent; bh=xcgbQh/5te5CLap8mXGCkq3WoCqMiMxhEDbgGveU+BQ=; b=rjwSBkAxNJptQ4307Wp4AjxExUkiifKrRUeuztc8hfC2ViX5FDyglCazDBb4GARO8c h8AKKEXjoZ5PPsbvOrepDSJUMGpWk77e0EWfrDBCrbqPwwTi6C/sWqONfpxBnhJ7b+cq Yxr/EuPX49KUDBcPvWTshSOZl3B5w1rLX3tmknuRTGRHYL4Je5ukpiwftk0zW810I0f2 /W8K5qSBpoJTm+h2M+8SZTOIHX6LWAnVsZzlsy+amYLQbwgxV0jeHe3fCAV6yGotKA0Y NnWDGuLsZ2tR1ucqH68j+IcluyDm8DSJn1TRj7cKaymPxH2mLLzZuXeIQ7XWvaRP8tQK wxmQ== X-Gm-Message-State: APjAAAVjMhMdaLTEEZVFB+5LWvANocOe8M1dcGqP+skA2NGRA8KrK9mF MZK+D3l6XGoTJWCAt5SOLJlMyA== X-Google-Smtp-Source: APXvYqwbRwxgY16WAOuXaSGoVRz+2qRYtKt36CgLfqJ8x/5AmbHuJOGwPB1A1kzY5YCca5krSXQTXQ== X-Received: by 2002:ad4:55a9:: with SMTP id f9mr104686832qvx.133.1564900168834; Sat, 03 Aug 2019 23:29:28 -0700 (PDT) Received: from nimloth.alvh.no-ip.org ([177.154.139.198]) by smtp.gmail.com with ESMTPSA id n27sm27558204qkk.35.2019.08.03.23.29.27 (version=TLS1_3 cipher=AEAD-AES256-GCM-SHA384 bits=256/256); Sat, 03 Aug 2019 23:29:28 -0700 (PDT) Received: by nimloth.alvh.no-ip.org (Postfix, from userid 1000) id 8ACBF12066D; Sun, 4 Aug 2019 02:29:25 -0400 (-04) Date: Sun, 4 Aug 2019 02:29:25 -0400 From: Alvaro Herrera To: Amit Langote Cc: yuzuko , shawn wang , Shawn Wang , PostgreSQL Hackers Subject: Re: Problem with default partition pruning Message-ID: <20190804062925.GA5861@alvherre.pgsql> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="YZ5djTAD1cGYuMQK" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: User-Agent: Mutt/1.9.4 (2018-02-28) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk --YZ5djTAD1cGYuMQK Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit On 2019-Jul-03, Amit Langote wrote: > Hosoya-san, > > Thanks for updating the patches. > > I have no comment in particular about > v4_default_partition_pruning.patch, Cool, thanks. I spent some time reviewing this patch (the first one) and I propose the attached cosmetic changes. Mostly they consist of a few comment rewordings. There is one Assert() that changed in a pretty significant way ... innocent though the change looks. The original (not Hosoya-san's patch's fault) had an assertion which is being changed thus: minoff = 0; maxoff = boundinfo->ndatums; ... if (partindices[minoff] < 0) minoff++; if (partindices[maxoff] < 0) maxoff--; result->scan_default = partition_bound_has_default(boundinfo); - Assert(minoff >= 0 && maxoff >= 0); + Assert(partindices[minoff] >= 0 && + partindices[maxoff] >= 0); Note that the original Assert() here was verifying whether minoff and maxoff are both >= 0. But that seems pretty useless since it seems almost impossible to have them become that given what we do to them. What I think this code *really* wants to check is whether *the partition indexes* that they point to are not negative: the transformation that the two "if" lines do means to ignore bounds that correspond to value ranges uncovered by any partition. And after the incr/decr operators, we expect that the bounds will be those of existing partitions ... so they shouldn't be -1. Other changes are addition of braces to some one-line blocks that had significant comments, and minor code rearrangements to make things look more easily understandable. -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services --YZ5djTAD1cGYuMQK Content-Type: text/x-diff; charset=us-ascii Content-Disposition: attachment; filename="v4-delta.patch" diff --git a/src/backend/partitioning/partprune.c b/src/backend/partitioning/partprune.c index 533818ce34..bfc16409dd 100644 --- a/src/backend/partitioning/partprune.c +++ b/src/backend/partitioning/partprune.c @@ -714,7 +714,7 @@ get_matching_partitions(PartitionPruneContext *context, List *pruning_steps) PruneStepResult **results, *final_result; ListCell *lc; - bool scan_default; + bool scan_default; /* If there are no pruning steps then all partitions match. */ if (num_steps == 0) @@ -771,21 +771,27 @@ get_matching_partitions(PartitionPruneContext *context, List *pruning_steps) { int partindex = context->boundinfo->indexes[i]; - /* - * In range and hash partitioning cases, some index values may be -1, - * indicating that no partition has been defined to accept a given - * range of values (that is, the bound at given offset is the upper - * bound of this unassigned range of values) or for a given remainder, - * respectively. As it's still part of the queried range of values, - * add the default partition, if any. - */ - if (partindex >= 0) - result = bms_add_member(result, partindex); - else + if (partindex < 0) + { + /* + * In range partitioning cases, if a partition index is -1 it + * means that the bound at the offset is the upper bound for a + * range not covered by any partition (other than a possible + * default partition). In hash partitioning, the same means no + * partition has been defined for the corresponding remainder + * value. + * + * In either case, the value is still part of the queried range of + * values, so mark to scan the default partition if one exists. + */ scan_default |= partition_bound_has_default(context->boundinfo); + continue; + } + + result = bms_add_member(result, partindex); } - /* Add the null and/or default partition if needed and if present. */ + /* Add the null and/or default partition if needed and present. */ if (final_result->scan_null) { Assert(context->strategy == PARTITION_STRATEGY_LIST); @@ -2623,17 +2629,13 @@ get_matching_list_bounds(PartitionPruneContext *context, * Each datum whose offset is in result is to be treated as the upper bound of * the partition that will contain the desired values. * - * scan_default will be set in the returned struct, if the default partition - * needs to be scanned, provided one exists at all. Although note that we - * intentionally don't set scan_default in this function if only because the - * matching set of values, found by comparing the input values using the - * specified opstrategy, contains unassigned portions of key space, which - * we normally assume to belong to the default partition. We don't infer - * that until all steps have been executed, including any combination steps, - * because even if such unassinged portion of key space appears to be part of - * the set of queried values based on comparisons in the individual OpExprs, - * the final set of bounds obtained after combining multiple OpExprs may - * exclude it. + * scan_default is set in the returned struct if a default partition exists + * and we're absolutely certain that it needs to be scanned. We do *not* set + * it just because values match portions of the key space uncovered by + * partitions other than default (space which we normally assume to belong to + * the default partition): the final set of bounds obtained after combining + * multiple pruning steps might exclude it, so we infer its inclusion + * elsewhere. * * 'opstrategy' if non-zero must be a btree strategy number. * @@ -2689,13 +2691,15 @@ get_matching_range_bounds(PartitionPruneContext *context, */ if (nvalues == 0) { + /* ignore key space not covered by any partitions */ if (partindices[minoff] < 0) minoff++; if (partindices[maxoff] < 0) maxoff--; result->scan_default = partition_bound_has_default(boundinfo); - Assert(minoff >= 0 && maxoff >= 0); + Assert(partindices[minoff] >= 0 && + partindices[maxoff] >= 0); result->bound_offsets = bms_add_range(NULL, minoff, maxoff); return result; @@ -2896,16 +2900,18 @@ get_matching_range_bounds(PartitionPruneContext *context, minoff = inclusive ? off : off + 1; } - - /* - * lookup value falls in the range between some bounds in - * boundinfo. off would be the offset of the greatest bound - * that is <= lookup value, so add off + 1 to the result - * instead as the offset of the upper bound of the smallest - * partition that may contain the lookup value. - */ else + { + + /* + * lookup value falls in the range between some bounds in + * boundinfo. off would be the offset of the greatest + * bound that is <= lookup value, so add off + 1 to the + * result instead as the offset of the upper bound of the + * smallest partition that may contain the lookup value. + */ minoff = off + 1; + } } break; @@ -2972,11 +2978,13 @@ get_matching_range_bounds(PartitionPruneContext *context, maxoff = off; } else + { /* * 'off' is -1 indicating that all bounds are greater, so just * set the first bound's offset as maxoff. */ maxoff = off + 1; + } break; default: @@ -3265,23 +3273,24 @@ perform_pruning_combine_step(PartitionPruneContext *context, /* * A combine step without any source steps is an indication to not perform - * any partition pruning, we just return all datum offsets. + * any partition pruning. Return all datum indexes in that case. */ result = (PruneStepResult *) palloc0(sizeof(PruneStepResult)); if (list_length(cstep->source_stepids) == 0) { PartitionBoundInfo boundinfo = context->boundinfo; + int rangemax; /* * Add all valid offsets into the boundinfo->indexes array. For range * partitioning, boundinfo->indexes contains (boundinfo->ndatums + 1) - * valid entries. + * valid entries; otherwise there are boundinfo->ndatums. */ - if (context->strategy == PARTITION_STRATEGY_RANGE) - result->bound_offsets = bms_add_range(NULL, 0, boundinfo->ndatums); - else - result->bound_offsets = bms_add_range(NULL, 0, - boundinfo->ndatums - 1); + rangemax = context->strategy == PARTITION_STRATEGY_RANGE ? + boundinfo->ndatums : boundinfo->ndatums - 1; + + result->bound_offsets = + bms_add_range(result->bound_offsets, 0, rangemax); result->scan_default = partition_bound_has_default(boundinfo); result->scan_null = partition_bound_accepts_nulls(boundinfo); return result; diff --git a/src/include/partitioning/partbounds.h b/src/include/partitioning/partbounds.h index c7535e32fc..160b319e0a 100644 --- a/src/include/partitioning/partbounds.h +++ b/src/include/partitioning/partbounds.h @@ -56,7 +56,6 @@ * pointed by remainder produced when hash value of the datum-tuple is divided * by the greatest modulus. */ - typedef struct PartitionBoundInfoData { char strategy; /* hash, list or range? */ --YZ5djTAD1cGYuMQK--