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 1gjwLA-0005jI-VZ for pgsql-hackers@arkaria.postgresql.org; Thu, 17 Jan 2019 01:19:29 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.89) (envelope-from ) id 1gjwL9-00061l-BG for pgsql-hackers@arkaria.postgresql.org; Thu, 17 Jan 2019 01:19:27 +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 1gjwL8-0005yH-Mg for pgsql-hackers@lists.postgresql.org; Thu, 17 Jan 2019 01:19:27 +0000 Received: from mail-wr1-x432.google.com ([2a00:1450:4864:20::432]) by makus.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1gjwL3-0007XS-Vi for pgsql-hackers@postgresql.org; Thu, 17 Jan 2019 01:19:25 +0000 Received: by mail-wr1-x432.google.com with SMTP id t27so9135551wra.6 for ; Wed, 16 Jan 2019 17:19:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=2ndquadrant-com.20150623.gappssmtp.com; s=20150623; h=subject:to:cc:references:from:message-id:date:user-agent :mime-version:in-reply-to:content-language:content-transfer-encoding; bh=h4NYL4z3nkTFQsVRvv2RXyVcOioEWuItgbnnb+cnSHk=; b=sueZQIaBNYTtGz8YYZlkhgJ7Rk38s/QGlQg7F2//iEi30M7l5oSF9u1YfsKq3ZLDgZ x5jz8mDNFiopEG+x5HA4fr8JApADiR7w97RSEBSgaTmQuh2McRoBn2K4uOGIe9wHN4H3 /o3/VrSDM7MKHhyYujl8Od/K8s9FUOxl7MnQE/ze7Fswd0M2dxwLgmpyl6pWUGgPDoxF r4P0cvrrriAIQvhYF/bO8Vc8boKniwamYt89u+BEZn8xpKYFOmrGLk+PUYcv8nEgDajW f7JK4Sd3e8ektehGDdzYzZiUf78GMD89ahyVY1joVNJ/uOjUlrAbawOgZm0nZ+oaKDQh wqtA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:cc:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=h4NYL4z3nkTFQsVRvv2RXyVcOioEWuItgbnnb+cnSHk=; b=JUceNCWqJj2+QJ1NJXlIEPJKVSBDjpsqdoKyM/dKn8dzPJYD7HcVaMDlbrLTOs09BQ BthFGI6G5M2Snj+WmW1vJJBqlCTImPiwBhXpk/XNUAbVAAdGucNZ43YGNgP3TS9ZM66f 2KN7YgK1JbSmP9IrRbyDD3xNc5vkraaJkYlZ1NVc4P9XX+vCEUxiJV/qYpZLlszqFN19 6037eo2LojiAutWjEZv5lZh+ssQMc7pYNHVfxIr0l1BDRbPY9e1jhXRxpWD3Qi6eDBf2 sJQl9+RZKo+YFVg/v93V+OrQ+xL6Pc8yySOfGuOEG72EbVScYmUanNbCe3zbjues3vgC Un/A== X-Gm-Message-State: AJcUuke0NVOVKA9h63rWNEr8MdcK7vdSHtcp2uvjrXnu03FsjiPLCFKW ufUlHPKn2/zBEGiH9IyV2+2OK+QL7rqYwwH5UQASPmKt8bisMpcxitqivsznNENmfomYIrhBysp 0zRkB4XPYThZo4cECxcWEBf2XN1XojaiaRRiBMHk6fAhO6I9vJFuibKBD97LnNIkJuP9xAW/oSl JFOMRHPH4l48U= X-Google-Smtp-Source: ALg8bN5Gg3N8Zds4joeFCTkvlDq6zNf2CxugvxsI+P2HfkcrMczkRa3tBFp7uqFap9rB47BpPW1fvw== X-Received: by 2002:adf:fdc2:: with SMTP id i2mr10022430wrs.117.1547687958949; Wed, 16 Jan 2019 17:19:18 -0800 (PST) Received: from [10.137.0.20] (ip-86-49-251-50.net.upcbroadband.cz. [86.49.251.50]) by smtp.gmail.com with ESMTPSA id z9sm70168601wrs.63.2019.01.16.17.19.17 (version=TLS1_3 cipher=AEAD-AES128-GCM-SHA256 bits=128/128); Wed, 16 Jan 2019 17:19:18 -0800 (PST) Subject: Re: [HACKERS] PATCH: multivariate histograms and MCV lists To: David Rowley Cc: Dean Rasheed , Thomas Munro , Bruce Momjian , Alvaro Herrera , Andres Freund , Mark Dilger , Pg Hackers References: <49348328-1a02-7e45-e50f-e1d4d0243c5c@2ndquadrant.com> <80bc98b2-1c43-1fe8-b316-d447bb68cff2@2ndquadrant.com> <341ca23f-144d-130d-1b45-ddddf581797e@2ndquadrant.com> <8ac8bd94-478d-215d-e6bd-339f1f20a74c@2ndquadrant.com> <11b0d685-152d-8320-5f49-a7c6ac3ee90a@2ndquadrant.com> <19b77ce3-83ec-c04b-ff7a-02475f044f4d@2ndquadrant.com> <78cc09c8-a9aa-9d7b-06f5-c2f9eba08059@2ndquadrant.com> From: Tomas Vondra Message-ID: <72a97865-9864-9cd7-eedb-e96dae4bde65@2ndquadrant.com> Date: Wed, 16 Jan 2019 20:19:16 -0500 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.3.0 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8; format=flowed Content-Language: en-US Content-Transfer-Encoding: 7bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Precedence: bulk On 1/16/19 7:56 AM, David Rowley wrote:> On Tue, 15 Jan 2019 at 08:21, Tomas Vondra wrote: >> Turns out you were right - the attribute_referenced piece was quite >> unnecessary. So I've removed it. I've also extended the regression tests >> to verify changing type of another column does not reset the stats. > > (Trying to find my feet over here) > > I've read over the entire thread, and apart from missing the last two > emails and therefore the latest patch, I managed to read over most of > the MCV patch. I didn't quite get to reading mcv.c and don't quite > have the energy to take that on now. > Thanks for looking! > At this stage I'm trying to get to know the patch. I read a lot of > discussing between you and Dean ironing out how the stats should be > used to form selectivities. At the time I'd not read the patch yet, > so most of it went over my head. > > I did note down a few things on my read. I've included them below. > Hopefully, they're useful. > > MCV list review > > 1. In mvc.c there's Assert(ndistinct <= UINT16_MAX); This should be > PG_UINT16_MAX > Yep. Will fix. > 2. math.h should be included just after postgres.h > Yep. Will fix. > 3. Copyright is still -2017 in mcv.c. Hopefully, if you change it to > 2019, you'll never have to bump it ever again! :-) > Optimist ;-) > 4. Looking at pg_stats_ext_mcvlist_items() I see you've coded the > string building manually. The way it's coded I'm finding a little > strange. It means the copying becomes quadratic due to > > snprintf(buff, 1024, format, values[1], DatumGetPointer(valout)); > strncpy(values[1], buff, 1023); > > So basically, generally, here you're building a new string with > values[1] followed by a comma, then followed by valout. One the next > line you then copy that new buffer back into values[1]. I understand > this part is likely not performance critical, but I see no reason to > write the code this way. > > Are you limiting the strings to 1024 bytes on purpose? I don't see > any comment mentioning you want to truncate strings. > > Would it not be better to do this part using a > AppendStringInfoString()? and just manually add a '{', ',' or '}' as > and when required? >> DatumGetPointer(valout) should really be using DatumGetCString(valout). > > Likely you can also use heap_form_tuple. This will save you having to > convert ints into strings then only to have BuildTupleFromCStrings() > do the reverse. > I agree. I admit all of this is a residue of an initial hackish version of the function, and should be changed to StringInfo. Will fix. > 5. individiaul -> individual > lists. This allows very accurate estimates for individiaul columns, but > > litst -> lists > > litst on combinations of columns. Similarly to functional dependencies > Will fix. > 6. Worth mentioning planning cycles too? > > "It's advisable to create MCV statistics objects only > on combinations of columns that are actually used in conditions together, > and for which misestimation of the number of groups is resulting in bad > plans. Otherwise, the ANALYZE cycles are just wasted." > Makes sense. Although that's what we say about the existing stats, so perhaps we should tweak that too. > 7. straight-forward -> straightforward > > (most-common values) lists, a straight-forward extension of the per-column > > 8. adresses -> addresses > > statistics adresses the limitation by storing individual values, but it > Will fix. Thanks for proof-reading. > 9. Worth mentioning ANALYZE time? > > This section introduces multivariate variant of MCV > (most-common values) lists, a straight-forward extension of the per-column > statistics described in . This > statistics adresses the limitation by storing individual values, but it > is naturally more expensive, both in terms of storage and planning time. > Yeah. > 10. low -> a low > > with low number of distinct values. Before looking at the second query, > > 11. them -> then > > on items in the MCV list, and them sums the frequencies > Will fix. > 12. Should we be referencing the source from the docs? > > See mcv_clauselist_selectivity > in src/backend/statistics/mcv.c for details. > > hmm. I see it's not the first going by: git grep -E "\w+\.c\<" > gt Hmm, that does not return anything to me - do you actually see any references to .c files in the sgml docs? I agree that probably is not a good idea, so I'll remove that. > 13. Pretty minor, but the following loop in > UpdateStatisticsForTypeChange() could use a break; > > attribute_referenced = false; > for (i = 0; i < staForm->stxkeys.dim1; i++) > if (attnum == staForm->stxkeys.values[i]) > attribute_referenced = true; > > UPDATE: If I'd reviewed the correct patch I'd have seen that you'd > removed this already > ;-) > 14. Again in UpdateStatisticsForTypeChange(), would it not be better > to do the statext_is_kind_built(oldtup, STATS_EXT_MCV) check before > checking if the stats contain this column? This gets rid of your > reset_stats variable. > > I also don't quite understand why there's an additional check for > statext_is_kind_built(oldtup, STATS_EXT_MCV), which if that's false > then why do we do the dummy update on the tuple? > > Have you just coded this so that you can support other stats types > later without too much modification? If so, I'm finding it a bit > confusing to read, so maybe it's worth only coding it that way if > there's more than one stats type to reset for. > > UPDATE: If I'd reviewed the correct patch I'd have seen that you'd > removed this already ;-) > > 15. I see you broke out the remainder of the code from > clauselist_selectivity() into clauselist_selectivity_simple(). The > comment looks like just a copy and paste from the original. That > seems like quite a bit of duplication. Is it better to maybe trim down > the original one? > I'll see what I can do. > 16. I initially didn't see how this code transformed the bms into an array: > > /* > * Transform the bms into an array, to make accessing i-th member easier, > * and then construct a filtered version with only attnums referenced > * by the dependency we validate. > */ > attnums = build_attnums(attrs); > > attnums_dep = (int *)palloc(k * sizeof(int)); > for (i = 0; i < k; i++) > attnums_dep[i] = attnums[dependency[i]]; > > Would it be better to name build_attnums() build_attnums_array() ? > > I think it would also be better to, instead of saying "the bms", just > say "attrs". > Hmmm, maybe. > 17. dependencies_clauselist_selectivity(), in: > > if ((dependency_is_compatible_clause(clause, rel->relid, &attnum)) && > (!bms_is_member(listidx, *estimatedclauses))) > > would it be better to have the bms_is_member() first? > Yes, that might be a tad faster. > 18. In dependencies_clauselist_selectivity() there seem to be a new > bug introduced. We do: > > /* mark this one as done, so we don't touch it again. */ > *estimatedclauses = bms_add_member(*estimatedclauses, listidx); > > but the bms_is_member() check that skipped these has been removed. > > It might be easier to document if we just always do: > > if (bms_is_member(listidx, *estimatedclauses)) > continue; > > at the start of both loops. list_attnums can just be left unset for > the originally already estimatedclauses. > It's probably not as clear as it should be, but if the clause is already estimated (or incompatible), then the list_attnums[] entry will be InvalidAttrNumber. Which is what we check in the second loop. > 19. in extended_stats.c, should build_attnums() be documented that the > Bitmapset members are not offset by > FirstLowInvalidHeapAttributeNumber. I think mostly Bitmapsets of > Attnums are offset by this, so might be worth a mention. > Good point. > 20. I think bms_member_index() needs documentation. I imagine you'll > want to mention that the bitmapset must contain the given varattno, > else surely it'll do the wrong thing if it's not. Perhaps an > Assert(bms_is_member(keys, varattno)); should be added to it. > Agreed. Or maybe make it return -1 in that case? It might even have missing_ok flag or something like that. > 21. Comment does not really explain what the function does or what the > arguments mean: > > /* > * statext_is_compatible_clause_internal > * Does the heavy lifting of actually inspecting the clauses for > * statext_is_compatible_clause. > */ > Will improve. > 22. In statext_is_compatible_clause_internal(): > > /* Var = Const */ > > The above comment seems a bit misplaced. It looks like the code below > it is looking for an OpExpr in the form of "Var Const", or "Const > Var". > Yes, I agree. > 23. statext_is_compatible_clause_internal() you have: > > if ((get_oprrest(expr->opno) != F_EQSEL) && > (get_oprrest(expr->opno) != F_NEQSEL) && > (get_oprrest(expr->opno) != F_SCALARLTSEL) && > (get_oprrest(expr->opno) != F_SCALARLESEL) && > (get_oprrest(expr->opno) != F_SCALARGTSEL) && > (get_oprrest(expr->opno) != F_SCALARGESEL)) > return false; > > 6 calls to get_oprrest(). 1 is enough. > > How does the existing MCV and histogram stats handle these operators? > Does it insist on a btree opfamily, or is it as crude as this too? > It's this crude too, AFAICS. > 24. In statext_is_compatible_clause_internal, you have: > > /* NOT/AND/OR clause */ > if (or_clause(clause) || > and_clause(clause) || > not_clause(clause)) > { > /* > * AND/OR/NOT-clauses are supported if all sub-clauses are supported > > Looks like you were not sure which order to have these, so you just > tried a few variations :-D Maybe just make them all the same? > If you insist ;-) > 25. Does statext_is_compatible_clause_internal)_ need to skip over RelabelTypes? > I believe it does, based on what I've observed during development. Why do you think it's not necessary? > 26. In statext_is_compatible_clause_internal() you mention: /* We only > support plain Vars for now */, but I see nothing that ensures that > only Vars are allowed in the is_opclause() condition. > > /* see if it actually has the right */ > ok = (NumRelids((Node *) expr) == 1) && > (is_pseudo_constant_clause(lsecond(expr->args)) || > (varonleft = false, > is_pseudo_constant_clause(linitial(expr->args)))); > > the above would allow var+var == const through. > But then we call statext_is_compatible_clause_internal on it again, and that only allows Vars and "Var op Const" expressions. Maybe there's a way around that? > The NumRelids seems like it would never have anything > 1 as you have > a BMS_SINGLETON test on the RestrictInfo where you're calling this > function from. I think you likely want just a IsA(... , Var) checks > here, after skipping over RelabelTypes. > > Not sure what "/* see if it actually has the right */" means. > That should have been "right structure" I believe. > 27. Should the function be named something more related to MCV? The > name makes it appear fairly generic to extended stats. > > * statext_is_compatible_clause > * Determines if the clause is compatible with MCV lists. > No, because it's supposed to also handle histograms (and perhaps other stats types) in the future. > 28. This comment seems wrong: > > * Currently we only support Var = Const, or Const = Var. It may be possible > * to expand on this later. > > I see you're allowing IS NULL and IS NOT NULL too. = does not seem to > be required either. > OK, will fix. > 29. The following fragment makes me think we're only processing > clauses to use them with MCV lists, but the comment claims "dependency > selectivity estimations" > > /* we're interested in MCV lists */ > int types = STATS_EXT_MCV; > > /* check if there's any stats that might be useful for us. */ > if (!has_stats_of_kind(rel->statlist, types)) > return (Selectivity) 1.0; > > list_attnums = (Bitmapset **) palloc(sizeof(Bitmapset *) * > list_length(clauses)); > > /* > * Pre-process the clauses list to extract the attnums seen in each item. > * We need to determine if there's any clauses which will be useful for > * dependency selectivity estimations. Along the way we'll record all of > Yeah, that's copy-pasto. > 30. Is it better to do the bms_is_member() first here? > > if ((statext_is_compatible_clause(clause, rel->relid, &attnums)) && > (!bms_is_member(listidx, *estimatedclauses))) > > Likely it'll be cheaper. > Yeah, same as before. > 31. I think this comment should be /* Ensure choose_best_statistics() > didn't mess up */ > > /* We only understand MCV lists for now. */ > Assert(stat->kind == STATS_EXT_MCV); > I'll expand the comment a bit. > 32. What're lags? > > bool *isnull; /* lags of NULL values (up to 32 columns) */ > Should be "flags" I think. > 33. "ndimentions"? There's no field in the struct by that name. I'd > assume it's the same size as the isnull array above it? > > Datum *values; /* variable-length (ndimensions) */ > Yes, that's the case. > 34. README.mcv > > * large -> a large > > For columns with large number of distinct values (e.g. those with continuous > > * Is the following up-to-date? I thought I saw code for NOT too? > > (a) equality clauses WHERE (a = 1) AND (b = 2) > (b) inequality clauses WHERE (a < 1) AND (b >= 2) > (c) NULL clauses WHERE (a IS NULL) AND (b IS NOT NULL) > (d) OR clauses WHERE (a < 1) OR (b >= 2) > > * multi-variate -> multivariate > > are large the list may be quite large. This is especially true for multi-variate > > * a -> an > > TODO Currently there's no logic to consider building only a MCV list (and not > > * I'd have said "an SRF", but git grep "a SRF" disagrees with me. I > guess those people must be pronouncing it, somehow!? surf... serf... ? > > easier, there's a SRF returning detailed information about the MCV lists. > > * Is it better to put a working SQL in here? > > SELECT * FROM pg_mcv_list_items(stxmcv); > > maybe like: > > SELECT s.* FROM pg_statistic_ext, LATERAL pg_mcv_list_items(stxmcv) s; > > Maybe with a WHERE clause? > > * This list seems outdated. > > - item index (0, ..., (nitems-1)) > - values (string array) > - nulls only (boolean array) > - frequency (double precision) > > base_frequency seems to exist now too. > Yeah, those are mostly typos. Will fix. thanks -- Tomas Vondra http://www.2ndQuadrant.com PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services