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 1gubNl-0008IE-WC for pgsql-hackers@arkaria.postgresql.org; Fri, 15 Feb 2019 11:10:14 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1gubNk-0005La-HL for pgsql-hackers@arkaria.postgresql.org; Fri, 15 Feb 2019 11:10:12 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1gubNk-0005LN-6a for pgsql-hackers@lists.postgresql.org; Fri, 15 Feb 2019 11:10:12 +0000 Received: from tama50.ecl.ntt.co.jp ([129.60.39.147]) by makus.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1gubNg-0005RM-2i for pgsql-hackers@postgresql.org; Fri, 15 Feb 2019 11:10:10 +0000 Received: from vc2.ecl.ntt.co.jp (vc2.ecl.ntt.co.jp [129.60.86.154]) by tama50.ecl.ntt.co.jp (8.13.8/8.13.8) with ESMTP id x1FB9mdr017439; Fri, 15 Feb 2019 20:09:48 +0900 Received: from vc2.ecl.ntt.co.jp (localhost [127.0.0.1]) by vc2.ecl.ntt.co.jp (Postfix) with ESMTP id 2426D639149; Fri, 15 Feb 2019 20:09:48 +0900 (JST) Received: from jcms-pop21.ecl.ntt.co.jp (jcms-pop21.ecl.ntt.co.jp [129.60.87.134]) by vc2.ecl.ntt.co.jp (Postfix) with ESMTP id 187AD6390CF; Fri, 15 Feb 2019 20:09:48 +0900 (JST) Received: from [IPv6:::1] (unknown [129.60.241.75]) by jcms-pop21.ecl.ntt.co.jp (Postfix) with ESMTPSA id 0E85840072A; Fri, 15 Feb 2019 20:09:48 +0900 (JST) Message-ID: <5C669DFD.30002@lab.ntt.co.jp> Date: Fri, 15 Feb 2019 20:09:49 +0900 From: Etsuro Fujita User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:11.0) Gecko/20120327 Thunderbird/11.0.1 MIME-Version: 1.0 Subject: Re: Problems with plan estimates in postgres_fdw References: <87pnz1aby9.fsf@news-spur.riddles.org.uk> <1175.1533220908@sss.pgh.pa.us> <5BB739CD.6040002@lab.ntt.co.jp> <5BBC4140.50403@lab.ntt.co.jp> <5C17A00E.5000005@lab.ntt.co.jp> <5C232F39.9060509@lab.ntt.co.jp> <5C25C7B5.2010802@lab.ntt.co.jp> <5C518631.5080401@lab.ntt.co.jp> <10994.1549559088@localhost> <5C5D778F.3080501@lab.ntt.co.jp> <19433.1549962210@localhost> In-Reply-To: <19433.1549962210@localhost> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-CC-Mail-RelayStamp: 1 To: Antonin Houska Cc: Tom Lane , Andrew Gierth , pgsql-hackers@postgresql.org X-TM-AS-MML: disable List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk (2019/02/12 18:03), Antonin Houska wrote: > Etsuro Fujita wrote: >> (2019/02/08 2:04), Antonin Houska wrote: >>> Some comments on coding: >>> >>> 0001-postgres_fdw-Perform-UPPERREL_ORDERED-step-remotely-v3.patch >>> ----------------------------------------------------------------- >>> >>> * I'm not sure if UPPERREL_ORDERD should to deal with LIMIT at all. Note that >>> grouping_planner() does not call create_limit_path() until it's done with >>> create_ordered_paths(), and create_ordered_paths() is where the FDW is >>> requested to add its paths to UPPERREL_ORDERED relation. >> >> Maybe I'm missing something, but the 0001 patch doesn't deal with LIMIT at >> all. I added the parameter limit_tuples to PgFdwPathExtraData so that we can >> pass limit_tuples=-1, which means no LIMIT, to cost_sort(), though. > > Yes, the limit_tuples field made me confused. But if cost_sort() is the only > reason, why not simply pass -1 in the 0001 patch, and use the limit_tuples > argument only in 0002? I see several other calls of cost_sort() in the core > where -1 is hard-coded. Yeah, that would make it easy to review the patch; will do. >>> Both patches: >>> ------------ >>> >>> One thing that makes me confused when I try to understand details is that the >>> new functions add_foreign_ordered_paths() and add_foreign_final_paths() both >>> pass "input_rel" for the "foreignrel" argument of estimate_path_cost_size(): >>> >>> estimate_path_cost_size(root, input_rel, ...) >>> >>> whereas the existing add_foreign_grouping_paths() passes "grouped_rel": >>> >>> estimate_path_cost_size(root, grouped_rel, ...) >>> >>> Can you please make this consistent? If you do, you'll probably need to >>> reconsider your changes to estimate_path_cost_size(). >> >> This is intended and I think I should add more comments on that. Let me >> explain. These patches modified estimate_path_cost_size() so that we can >> estimate the costs of a foreign path for the UPPERREL_ORDERED or >> UPPERREL_FINAL rel, by adding the costs of the upper-relation processing (ie, >> ORDER BY and/or LIMIT/OFFSET, specified by PgFdwPathExtraData) to the costs of >> implementing the *underlying* relation for the upper relation (ie, scan, join >> or grouping rel, specified by the input_rel). So those functions call >> estimate_path_cost_size() with the input_rel, not the ordered_rel/final_rel, >> along with PgFdwPathExtraData. > > I think the same already happens for the UPPERREL_GROUP_AGG relation: > estimate_path_cost_size() > > ... > else if (IS_UPPER_REL(foreignrel)) > { > ... > ofpinfo = (PgFdwRelationInfo *) fpinfo->outerrel->fdw_private; > > Here "outerrel" actually means input relation from the grouping perspective. > The input relation (i.e. result of scan / join) estimates are retrieved from > "ofpinfo" and the costs of aggregation are added. Why should this be different > for other kinds of upper relations? IIUC, I think there is an oversight in that case: the cost estimation doesn't account for evaluating the final scan/join target updated by apply_scanjoin_target_to_paths(), but I think that is another issue, so I'll start a new thread. >>> And maybe related problem: why should FDW care about the effect of >>> apply_scanjoin_target_to_paths() like you claim to do here? >>> >>> /* >>> * If this is UPPERREL_ORDERED and/or UPPERREL_FINAL steps on the >>> * final scan/join relation, the costs obtained from the cache >>> * wouldn't yet contain the eval cost for the final scan/join target, >>> * which would have been updated by apply_scanjoin_target_to_paths(); >>> * add the eval cost now. >>> */ >>> if (fpextra&& !IS_UPPER_REL(foreignrel)) >>> { >>> /* The costs should have been obtained from the cache. */ >>> Assert(fpinfo->rel_startup_cost>= 0&& >>> fpinfo->rel_total_cost>= 0); >>> >>> startup_cost += foreignrel->reltarget->cost.startup; >>> run_cost += foreignrel->reltarget->cost.per_tuple * rows; >>> } >>> >>> I think it should not, whether "foreignrel" means "input_rel" or "output_rel" >>> from the perspective of postgresGetForeignUpperPaths(). >> >> I added this before costing the sort operation below, because 1) the cost of >> evaluating the scan/join target might not be zero (consider the case where >> sort columns are not simple Vars, for example) and 2) the cost of sorting >> takes into account the underlying path's startup/total costs. Maybe I'm >> missing something, though. > > My understanding is that the "input_rel" argument of > postgresGetForeignUpperPaths() should already have been processed by > postgresGetForeignPaths() or postgresGetForeignJoinPaths() (or actually by > postgresGetForeignUpperPaths() called with different "stage" too). Therefore > it should already have the "fdw_private" field initialized. The estimates in > the corresponding PgFdwRelationInfo should already include the reltarget > costs, as set previously by estimate_path_cost_size(). Right, but what I'm saying here is: the costs of evaluating the final scan/join target updated by apply_scanjoin_target_to_paths() wouldn't be yet included in the costs we calculated using local statistics when called from postgresGetForeignPaths() or postgresGetForeignJoinPaths(). Let me explain using an example: SELECT a+b FROM foreign_table LIMIT 10; For this query, the reltarget for the foreign_table would be {a, b} when called from postgresGetForeignPaths() (see build_base_rel_tlists()). The costs of evaluating simple Vars are zeroes, so we wouldn't charge any costs for tlist evaluation when estimating the costs of a basic foreign table scan in that callback routine, but the final scan target, with which the reltarget will be replaced later by apply_scanjoin_target_to_paths(), would be {a+b}, so we need to adjust the costs of the basic foreign table scan, cached in the PgFdwRelationInfo for the input_rel (ie, the foreign_table), so that eval costs for the replaced tlist are included when called from postgresGetForeignUpperPaths() with the UPPERREL_FINAL stage, as the costs of evaluating the expression 'a+b' wouldn't be zeroes. Best regards, Etsuro Fujita