Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1XceOM-0007jm-CY for pgsql-performance@arkaria.postgresql.org; Fri, 10 Oct 2014 17:54:14 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.80) (envelope-from ) id 1XceOK-0001sd-Kh for pgsql-performance@arkaria.postgresql.org; Fri, 10 Oct 2014 17:54:12 +0000 Received: from makus.postgresql.org ([2001:4800:1501:1::229]) by malur.postgresql.org with esmtps (TLS1.2:DHE_RSA_AES_256_CBC_SHA256:256) (Exim 4.80) (envelope-from ) id 1XceNy-0001IC-44 for pgsql-performance@postgresql.org; Fri, 10 Oct 2014 17:53:50 +0000 Received: from smtp-out2.gransy.com ([77.78.126.49]) by makus.postgresql.org with esmtp (Exim 4.80) (envelope-from ) id 1XceNp-0000SD-H4 for pgsql-performance@postgresql.org; Fri, 10 Oct 2014 17:53:44 +0000 Received: from smtp-out.gransy.com (localhost [127.0.0.1]) by smtp-out2.gransy.com (Postfix) with ESMTP id 9EC803F0128 for ; Fri, 10 Oct 2014 19:53:40 +0200 (CEST) Received: from smtp.gransy.com (smtp.simplia.cz [77.78.126.20]) by smtp-out2.gransy.com (Postfix) with ESMTP id 36B5A3F0126 for ; Fri, 10 Oct 2014 19:53:40 +0200 (CEST) Received: from [192.168.1.163] (ip-78-45-139-138.net.upcbroadband.cz [78.45.139.138]) (Authenticated sender: tv@fuzzy.cz) by smtp.gransy.com (Postfix) with ESMTPSA id 151A919A035E for ; Fri, 10 Oct 2014 19:53:32 +0200 (CEST) Message-ID: <54381D20.9010501@fuzzy.cz> Date: Fri, 10 Oct 2014 19:53:36 +0200 From: Tomas Vondra User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.0 MIME-Version: 1.0 To: pgsql-performance@postgresql.org Subject: Re: Yet another abort-early plan disaster on 9.3 References: <541A2335.3060100@agliodbs.com> <5429D517.3090900@agliodbs.com> <542C4E60.3030505@agliodbs.com> <542DADEB.2080103@agliodbs.com> In-Reply-To: Content-Type: multipart/mixed; boundary="------------090805090609080408090308" X-Pg-Spam-Score: -1.9 (-) List-Archive: List-Help: List-ID: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: X-Mailing-List: pgsql-performance Precedence: bulk Sender: pgsql-performance-owner@postgresql.org This is a multi-part message in MIME format. --------------090805090609080408090308 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit On 10.10.2014 14:10, Tomas Vondra wrote: > Dne 10 Říjen 2014, 13:16, Greg Stark napsal(a): >> On Thu, Oct 2, 2014 at 8:56 PM, Josh Berkus wrote: >>> Yes, it's only intractable if you're wedded to the idea of a tiny, >>> fixed-size sample. If we're allowed to sample, say, 1% of the table, we >>> can get a MUCH more accurate n_distinct estimate using multiple >>> algorithms, of which HLL is one. While n_distinct will still have some >>> variance, it'll be over a much smaller range. >> >> I've gone looking for papers on this topic but from what I read this >> isn't so. To get any noticeable improvement you need to read 10-50% of >> the table and that's effectively the same as reading the entire table >> -- and it still had pretty poor results. All the research I could find >> went into how to analyze the whole table while using a reasonable >> amount of scratch space and how to do it incrementally. > > I think it's really difficult to discuss the estimation without some basic > agreement on what are the goals. Naturally, we can't get a perfect > estimator with small samples (especially when the sample size is fixed and > not scaling with the table). But maybe we can improve the estimates > without scanning most of the table? > > FWIW I've been playing with the adaptive estimator described in [1] and > the results looks really interesting, IMHO. So far I was testing it on > synthetic datasets outside the database, but I plan to use it instead of > our estimator, and do some more tests. Attached is an experimental patch implementing the adaptive estimator. It was fairly simple (although it's a bit messy). It only computes the estimates for the "scalar" case (i.e. data types that we can sort). Implementing this for the "minimal" case is possible, but requires a bit more work. It only computes the estimate and prints a WARNING with both the current and new estimate, but the old estimate is stored. I also attach a few synthetic examples of synthetic datasets with distributions stored in various ways, that I used for testing. In all cases there's a single table with 10M rows and a single INT column. There are three kinds of skew: 1) smooth skew - N distinct values (100, 10.000 and 100.000 values) - average moves to 0 as 'k' increases ('k' between 1 and 9) - smooth distribution of frequencies INSERT INTO test SELECT pow(random(),k) * 10000 FROM generate_series(1,10000000); 2) step skew - a few very frequent values, many rare values - for example this generates 5 very frequent and ~10k rare values INSERT INTO test SELECT (CASE WHEN (v < 90000) THEN MOD(v,5) ELSE v END) FROM ( SELECT (random()*100000)::int AS v FROM generate_series(1,10000000) ) foo; Results ======= I tested this with various statistics target settings (10, 100, 1000), which translates to different sample sizes. statistics target 100 (default, 30k rows, 0.3% sample) ====================================================== a) smooth skew, 101 values, different skew ('k') k current adaptive ------------------------- 1 101 102 3 101 102 5 101 102 7 101 102 9 101 102 b) smooth skew, 10.001 values, different skew ('k') k current adaptive ------------------------- 1 9986 10542 3 8902 10883 5 7579 10824 7 6639 10188 9 5947 10013 c) step skew (different numbers of values) values current adaptive ------------------------------ 106 106 107 106 35 104 1006 259 1262 10006 2823 11047 statistics target 10 (3k rows, 0.03% sample) ============================================ a) smooth skew, 101 values, different skew ('k') k current adaptive ------------------------- 1 101 102 3 101 102 5 101 102 7 101 102 9 101 102 b) smooth skew, 10.001 values, different skew ('k') k current adaptive ------------------------- 1 9846 10014 3 4399 7190 5 2532 5477 7 1938 4932 9 1623 1623 c) step skew (different numbers of values) values current adaptive ------------------------------ 106 100 114 106 5 5 1006 37 532 10006 323 20970 statistics target 1000 (300k rows, 3% sample) ============================================= k current adaptive ------------------------- 1 101 102 3 101 102 5 101 102 7 101 102 9 101 102 b) smooth skew, 10.001 values, different skew ('k') k current adaptive ------------------------- 1 10001 10002 3 10000 10000 5 9998 10011 7 9973 10045 9 9939 10114 c) step skew (different numbers of values) values current adaptive ------------------------------ 106 106 107 106 101 107 1006 957 1096 10006 9551 10550 Summary ======= I'm yet to see an example where the adaptive estimator produces worse results than the current estimator, irrespectedly of the distribution and sample size / statistics target. What really matters is the sample size, with respect to the table size, so I'll use the 0.03%, 0.3%, and 3% instead of the statistics target. For the large sample (3%) both estimators produce reasonably accurate results. This however won't work as the tables grow, because the number of rows we sample is static (does not grow with the table). As the sample decreases, the adaptive estimator starts winning. For the 0.3% sample the difference is easily 3x for the high-skew cases. E.g. for one of the "step skew" distributions the actual ndistinct value is 10006, current estimator gives 2823 while adaptive gives 11047. That's ratio error ~3.5 vs. 1.1x. For the tiny 0.03% sample the difference gets even more siginficant, especially for the step-skew cases, where the improvement is often an order of magnitude. Proposal ======== I think the adaptive estimator works very well, and I plan to submit it to the next commitfest after a bit of polishing. Examples of distributions that break it are welcome of course. Also, I think it's clear it'd be useful to be able to scale the sample proportionally to the table (instead of only allowing the current statistics target approach). I understand it may result in scanning large part of the table, but I don't see a problem in that's not a default behavior (and clearly documented). I don't see a way around that - small samples simply result in poor estimates. I was thinking that this could be done using statistics target, but it's already used for other things (e.g. size of MCV list, histogram) and we don't want to break that by adding yet another function. Ideas? regards Tomas --------------090805090609080408090308 Content-Type: text/x-diff; name="ndistinct-estimator.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="ndistinct-estimator.patch" diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index c09ca7e..7018987 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -110,6 +110,7 @@ static void update_attstats(Oid relid, bool inh, static Datum std_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull); static Datum ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull); +static double optimize_estimate(int total_rows, int d, int *f, int r); /* * analyze_rel() -- analyze one relation @@ -2360,6 +2361,11 @@ compute_scalar_stats(VacAttrStatsP stats, int slot_idx = 0; CompareScalarsContext cxt; + /* f values for the estimator - messy and we likely need much + * less memory, but who cares */ + int f_max = 0; /* max number of duplicates */ + int *f_count = (int*)palloc0(sizeof(int)*values_cnt); + /* Sort the collected values */ cxt.ssup = &ssup; cxt.tupnoLink = tupnoLink; @@ -2401,6 +2407,7 @@ compute_scalar_stats(VacAttrStatsP stats, ndistinct++; if (dups_cnt > 1) { + nmultiple++; if (track_cnt < num_mcv || dups_cnt > track[track_cnt - 1].count) @@ -2426,6 +2433,12 @@ compute_scalar_stats(VacAttrStatsP stats, track[j].first = i + 1 - dups_cnt; } } + + /* increment the number of values with this number of + * repetitions and the largest number of repetitions */ + f_count[dups_cnt] += 1; + f_max = (f_max < dups_cnt) ? dups_cnt : f_max; + dups_cnt = 0; } } @@ -2472,6 +2485,7 @@ compute_scalar_stats(VacAttrStatsP stats, double numer, denom, stadistinct; + double adaptdistinct; /* adaptive estimate */ numer = (double) samplerows *(double) d; @@ -2485,6 +2499,12 @@ compute_scalar_stats(VacAttrStatsP stats, if (stadistinct > totalrows) stadistinct = totalrows; stats->stadistinct = floor(stadistinct + 0.5); + + /* compute the adaptive estimate */ + adaptdistinct = optimize_estimate(totalrows, d, f_count, f_max); + + elog(WARNING, "ndistinct estimate current=%.2f adaptive=%.2f", stadistinct, adaptdistinct); + } /* @@ -2810,3 +2830,84 @@ compare_mcvs(const void *a, const void *b) return da - db; } + + +/* + * We need to minimize this equality (find "m" solving it) + * + * m - f1 - f2 = f1 * (A + A(m)) / (B + B(m)) + * + * where A, B are effectively constants (not depending on m), and A(m) + * and B(m) are functions. This is equal to solving + * + * 0 = f1 * (A + A(m)) / (B + B(m)) - (m - f1 - f2) + * + * Instead of looking for the exact solution to this equation (which + * might be fractional), we'll look for a natural number minimizing + * the absolute difference. Number of (distinct) elements is a natural + * number, and we don't mind if the number os slightly wrong. It's + * just an estimate, after all. The error from sampling will be much + * worse in most cases. + * + * We know the acceptable values of 'm' are [d,N] where 'd' is the number + * of distinct elements in the sample, and N is the number of rows in + * the table (not just the sample). For large tables (billions of rows) + * that'd be quite time-consuming to compute, so we'll approximate the + * solution by gradually increasing the step to ~1% of the current value + * of 'm'. This will make it much faster and yet very accurate. + * + * All this of course assumes the function behaves reasonably (not + * oscillating etc.), but that's a safe assumption as the estimator + * would perform terribly otherwise. + */ +static double +optimize_estimate(int total_rows, int d, int *f, int r) +{ + int i, m; + double A = 0.0, B = 0.0; + int opt_m = 0; + double opt_diff = total_rows; + int step = 1; + int ndistinct; + + /* compute the 'constant' parts of the equality (A, B) */ + for (i = 3; i <= r; i++) + { + double p = i / (double)r; + A += pow((1 - p), r ) * f[i]; + B += i * pow((1 - p), r-1) * f[i]; + } + + /* find the 'm' value minimizing the difference */ + for (m = 1; m <= total_rows; m += step) + { + int k = (f[1] + 2*f[2]); + double q = k / ((double)(r * 2 * m)); + + double A_m = A + m * pow((1 - q), r ); + double B_m = B + k * pow((1 - q), r-1); + + double diff = fabs(f[1] * (A_m / B_m) - (m - f[1] - f[2])); + + /* if this is a better solution */ + if (diff < opt_diff) + { + opt_diff = diff; + opt_m = m; + } + + /* tweak the step to 1% to make it faster */ + step = ((int)(0.01 * m) > 1) ? (int)(0.01 * m) : 1; + } + + /* compute the final estimate */ + ndistinct = d + opt_m - f[1] - f[2]; + + /* sanity checks that the estimate is within [d,total_rows] */ + if (ndistinct < d) + ndistinct = d; + else if (ndistinct > total_rows) + ndistinct = total_rows; + + return ndistinct; +} --------------090805090609080408090308 Content-Type: application/sql; name="ndistinct.sql" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="ndistinct.sql" Q1JFQVRFIFRBQkxFIHRlc3QgKGEgaW50KTsKCi0tIHNtb290aCB3aXRoIH4x MDAwMCB2YWx1ZXMKVFJVTkNBVEUgdGVzdDsKSU5TRVJUIElOVE8gdGVzdCBT RUxFQ1QgcmFuZG9tKCkgKiAxMDAwMCBGUk9NIGdlbmVyYXRlX3Nlcmllcygx LDEwMDAwMDAwKTsKQU5BTFlaRSB0ZXN0OwpTRUxFQ1QgQ09VTlQoRElTVElO Q1QgYSkgRlJPTSB0ZXN0OwoKVFJVTkNBVEUgdGVzdDsKSU5TRVJUIElOVE8g dGVzdCBTRUxFQ1QgcG93KHJhbmRvbSgpLDIpICogMTAwMDAgRlJPTSBnZW5l cmF0ZV9zZXJpZXMoMSwxMDAwMDAwMCk7CkFOQUxZWkUgdGVzdDsKU0VMRUNU IENPVU5UKERJU1RJTkNUIGEpIEZST00gdGVzdDsKClRSVU5DQVRFIHRlc3Q7 CklOU0VSVCBJTlRPIHRlc3QgU0VMRUNUIHBvdyhyYW5kb20oKSwzKSAqIDEw MDAwIEZST00gZ2VuZXJhdGVfc2VyaWVzKDEsMTAwMDAwMDApOwpBTkFMWVpF IHRlc3Q7ClNFTEVDVCBDT1VOVChESVNUSU5DVCBhKSBGUk9NIHRlc3Q7CgpU UlVOQ0FURSB0ZXN0OwpJTlNFUlQgSU5UTyB0ZXN0IFNFTEVDVCBwb3cocmFu ZG9tKCksNCkgKiAxMDAwMCBGUk9NIGdlbmVyYXRlX3NlcmllcygxLDEwMDAw MDAwKTsKQU5BTFlaRSB0ZXN0OwpTRUxFQ1QgQ09VTlQoRElTVElOQ1QgYSkg RlJPTSB0ZXN0OwoKVFJVTkNBVEUgdGVzdDsKSU5TRVJUIElOVE8gdGVzdCBT RUxFQ1QgcG93KHJhbmRvbSgpLDUpICogMTAwMDAgRlJPTSBnZW5lcmF0ZV9z ZXJpZXMoMSwxMDAwMDAwMCk7CkFOQUxZWkUgdGVzdDsKU0VMRUNUIENPVU5U KERJU1RJTkNUIGEpIEZST00gdGVzdDsKClRSVU5DQVRFIHRlc3Q7CklOU0VS VCBJTlRPIHRlc3QgU0VMRUNUIHBvdyhyYW5kb20oKSw2KSAqIDEwMDAwIEZS T00gZ2VuZXJhdGVfc2VyaWVzKDEsMTAwMDAwMDApOwpBTkFMWVpFIHRlc3Q7 ClNFTEVDVCBDT1VOVChESVNUSU5DVCBhKSBGUk9NIHRlc3Q7CgpUUlVOQ0FU RSB0ZXN0OwpJTlNFUlQgSU5UTyB0ZXN0IFNFTEVDVCBwb3cocmFuZG9tKCks NykgKiAxMDAwMCBGUk9NIGdlbmVyYXRlX3NlcmllcygxLDEwMDAwMDAwKTsK QU5BTFlaRSB0ZXN0OwpTRUxFQ1QgQ09VTlQoRElTVElOQ1QgYSkgRlJPTSB0 ZXN0OwoKVFJVTkNBVEUgdGVzdDsKSU5TRVJUIElOVE8gdGVzdCBTRUxFQ1Qg cG93KHJhbmRvbSgpLDgpICogMTAwMDAgRlJPTSBnZW5lcmF0ZV9zZXJpZXMo MSwxMDAwMDAwMCk7CkFOQUxZWkUgdGVzdDsKU0VMRUNUIENPVU5UKERJU1RJ TkNUIGEpIEZST00gdGVzdDsKClRSVU5DQVRFIHRlc3Q7CklOU0VSVCBJTlRP IHRlc3QgU0VMRUNUIHBvdyhyYW5kb20oKSw5KSAqIDEwMDAwIEZST00gZ2Vu ZXJhdGVfc2VyaWVzKDEsMTAwMDAwMDApOwpBTkFMWVpFIHRlc3Q7ClNFTEVD VCBDT1VOVChESVNUSU5DVCBhKSBGUk9NIHRlc3Q7CgotLSBzbW9vdGggd2l0 aCB+MTAwIHZhbHVlcwpUUlVOQ0FURSB0ZXN0OwpJTlNFUlQgSU5UTyB0ZXN0 IFNFTEVDVCByYW5kb20oKSAqIDEwMCBGUk9NIGdlbmVyYXRlX3Nlcmllcygx LDEwMDAwMDAwKTsKQU5BTFlaRSB0ZXN0OwpTRUxFQ1QgQ09VTlQoRElTVElO Q1QgYSkgRlJPTSB0ZXN0OwoKVFJVTkNBVEUgdGVzdDsKSU5TRVJUIElOVE8g dGVzdCBTRUxFQ1QgcG93KHJhbmRvbSgpLDIpICogMTAwIEZST00gZ2VuZXJh dGVfc2VyaWVzKDEsMTAwMDAwMDApOwpBTkFMWVpFIHRlc3Q7ClNFTEVDVCBD T1VOVChESVNUSU5DVCBhKSBGUk9NIHRlc3Q7CgpUUlVOQ0FURSB0ZXN0OwpJ TlNFUlQgSU5UTyB0ZXN0IFNFTEVDVCBwb3cocmFuZG9tKCksMykgKiAxMDAg RlJPTSBnZW5lcmF0ZV9zZXJpZXMoMSwxMDAwMDAwMCk7CkFOQUxZWkUgdGVz dDsKU0VMRUNUIENPVU5UKERJU1RJTkNUIGEpIEZST00gdGVzdDsKClRSVU5D QVRFIHRlc3Q7CklOU0VSVCBJTlRPIHRlc3QgU0VMRUNUIHBvdyhyYW5kb20o KSw0KSAqIDEwMCBGUk9NIGdlbmVyYXRlX3NlcmllcygxLDEwMDAwMDAwKTsK QU5BTFlaRSB0ZXN0OwpTRUxFQ1QgQ09VTlQoRElTVElOQ1QgYSkgRlJPTSB0 ZXN0OwoKVFJVTkNBVEUgdGVzdDsKSU5TRVJUIElOVE8gdGVzdCBTRUxFQ1Qg cG93KHJhbmRvbSgpLDUpICogMTAwIEZST00gZ2VuZXJhdGVfc2VyaWVzKDEs MTAwMDAwMDApOwpBTkFMWVpFIHRlc3Q7ClNFTEVDVCBDT1VOVChESVNUSU5D VCBhKSBGUk9NIHRlc3Q7CgpUUlVOQ0FURSB0ZXN0OwpJTlNFUlQgSU5UTyB0 ZXN0IFNFTEVDVCBwb3cocmFuZG9tKCksNikgKiAxMDAgRlJPTSBnZW5lcmF0 ZV9zZXJpZXMoMSwxMDAwMDAwMCk7CkFOQUxZWkUgdGVzdDsKU0VMRUNUIENP VU5UKERJU1RJTkNUIGEpIEZST00gdGVzdDsKClRSVU5DQVRFIHRlc3Q7CklO U0VSVCBJTlRPIHRlc3QgU0VMRUNUIHBvdyhyYW5kb20oKSw3KSAqIDEwMCBG Uk9NIGdlbmVyYXRlX3NlcmllcygxLDEwMDAwMDAwKTsKQU5BTFlaRSB0ZXN0 OwpTRUxFQ1QgQ09VTlQoRElTVElOQ1QgYSkgRlJPTSB0ZXN0OwoKVFJVTkNB VEUgdGVzdDsKSU5TRVJUIElOVE8gdGVzdCBTRUxFQ1QgcG93KHJhbmRvbSgp LDgpICogMTAwIEZST00gZ2VuZXJhdGVfc2VyaWVzKDEsMTAwMDAwMDApOwpB TkFMWVpFIHRlc3Q7ClNFTEVDVCBDT1VOVChESVNUSU5DVCBhKSBGUk9NIHRl c3Q7CgpUUlVOQ0FURSB0ZXN0OwpJTlNFUlQgSU5UTyB0ZXN0IFNFTEVDVCBw b3cocmFuZG9tKCksOSkgKiAxMDAgRlJPTSBnZW5lcmF0ZV9zZXJpZXMoMSwx MDAwMDAwMCk7CkFOQUxZWkUgdGVzdDsKU0VMRUNUIENPVU5UKERJU1RJTkNU IGEpIEZST00gdGVzdDsKCi0tIHN0ZXAgd2l0aCA1IGZyZXF1ZW50IHZhbHVl cywgfjEwMCBsZXNzIGZyZXF1ZW50IHZhbHVlcwpUUlVOQ0FURSB0ZXN0OwpJ TlNFUlQgSU5UTyB0ZXN0IFNFTEVDVCAoQ0FTRSBXSEVOICh2IDwgOTAwKSBU SEVOIE1PRCh2LDUpIEVMU0UgdiBFTkQpIEZST00gKFNFTEVDVCAocmFuZG9t KCkqMTAwMCk6OmludCBBUyB2IEZST00gZ2VuZXJhdGVfc2VyaWVzKDEsMTAw MDAwMDApKSBmb287CkFOQUxZWkUgdGVzdDsKU0VMRUNUIENPVU5UKERJU1RJ TkNUIGEpIEZST00gdGVzdDsKCi0tIHN0ZXAgd2l0aCA1IGZyZXF1ZW50IHZh bHVlcywgfjEwMCByYXJlIHZhbHVlcwpUUlVOQ0FURSB0ZXN0OwpJTlNFUlQg SU5UTyB0ZXN0IFNFTEVDVCAoQ0FTRSBXSEVOICh2IDwgOTk5MDApIFRIRU4g TU9EKHYsNSkgRUxTRSB2IEVORCkgRlJPTSAoU0VMRUNUIChyYW5kb20oKSox MDAwMDApOjppbnQgQVMgdiBGUk9NIGdlbmVyYXRlX3NlcmllcygxLDEwMDAw MDAwKSkgZm9vOwpBTkFMWVpFIHRlc3Q7ClNFTEVDVCBDT1VOVChESVNUSU5D VCBhKSBGUk9NIHRlc3Q7CgotLSBzdGVwIHdpdGggNSBmcmVxdWVudCB2YWx1 ZXMsIH4xMDAwIHJhcmUgdmFsdWVzClRSVU5DQVRFIHRlc3Q7CklOU0VSVCBJ TlRPIHRlc3QgU0VMRUNUIChDQVNFIFdIRU4gKHYgPCA5OTAwMCkgVEhFTiBN T0Qodiw1KSBFTFNFIHYgRU5EKSBGUk9NIChTRUxFQ1QgKHJhbmRvbSgpKjEw MDAwMCk6OmludCBBUyB2IEZST00gZ2VuZXJhdGVfc2VyaWVzKDEsMTAwMDAw MDApKSBmb287CkFOQUxZWkUgdGVzdDsKU0VMRUNUIENPVU5UKERJU1RJTkNU IGEpIEZST00gdGVzdDsKCi0tIHN0ZXAgd2l0aCA1IGZyZXF1ZW50IHZhbHVl cywgfjEwMDAwIHJhcmUgdmFsdWVzClRSVU5DQVRFIHRlc3Q7CklOU0VSVCBJ TlRPIHRlc3QgU0VMRUNUIChDQVNFIFdIRU4gKHYgPCA5MDAwMCkgVEhFTiBN T0Qodiw1KSBFTFNFIHYgRU5EKSBGUk9NIChTRUxFQ1QgKHJhbmRvbSgpKjEw MDAwMCk6OmludCBBUyB2IEZST00gZ2VuZXJhdGVfc2VyaWVzKDEsMTAwMDAw MDApKSBmb287CkFOQUxZWkUgdGVzdDsKU0VMRUNUIENPVU5UKERJU1RJTkNU IGEpIEZST00gdGVzdDsKCg== --------------090805090609080408090308 Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-performance mailing list (pgsql-performance@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-performance --------------090805090609080408090308-- --boundary-377585--