PostgreSQL Source Code git master
Loading...
Searching...
No Matches
mcv.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_statistic_ext_data.h"
#include "fmgr.h"
#include "funcapi.h"
#include "nodes/nodeFuncs.h"
#include "statistics/extended_stats_internal.h"
#include "statistics/statistics.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fmgrprotos.h"
#include "utils/lsyscache.h"
#include "utils/selfuncs.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
Include dependency graph for mcv.c:

Go to the source code of this file.

Macros

#define ITEM_SIZE(ndims)    ((ndims) * (sizeof(uint16) + sizeof(bool)) + 2 * sizeof(double))
 
#define MinSizeOfMCVList    (VARHDRSZ + sizeof(uint32) * 3 + sizeof(AttrNumber))
 
#define SizeOfMCVList(ndims, nitems)
 
#define RESULT_MERGE(value, is_or, match)    ((is_or) ? ((value) || (match)) : ((value) && (match)))
 
#define RESULT_IS_FINAL(value, is_or)   ((is_or) ? (value) : (!(value)))
 

Functions

static MultiSortSupport build_mss (StatsBuildData *data)
 
static SortItembuild_distinct_groups (int numrows, SortItem *items, MultiSortSupport mss, int *ndistinct)
 
static SortItem ** build_column_frequencies (SortItem *groups, int ngroups, MultiSortSupport mss, int *ncounts)
 
static int count_distinct_groups (int numrows, SortItem *items, MultiSortSupport mss)
 
static double get_mincount_for_mcv_list (int samplerows, double totalrows)
 
MCVListstatext_mcv_build (StatsBuildData *data, double totalrows, int stattarget)
 
static int compare_sort_item_count (const void *a, const void *b, void *arg)
 
static int sort_item_compare (const void *a, const void *b, void *arg)
 
MCVListstatext_mcv_load (Oid mvoid, bool inh)
 
byteastatext_mcv_serialize (MCVList *mcvlist, VacAttrStats **stats)
 
MCVListstatext_mcv_deserialize (bytea *data)
 
Datum pg_stats_ext_mcvlist_items (PG_FUNCTION_ARGS)
 
Datum pg_mcv_list_in (PG_FUNCTION_ARGS)
 
Datum pg_mcv_list_out (PG_FUNCTION_ARGS)
 
Datum pg_mcv_list_recv (PG_FUNCTION_ARGS)
 
Datum pg_mcv_list_send (PG_FUNCTION_ARGS)
 
static int mcv_match_expression (Node *expr, Bitmapset *keys, List *exprs, Oid *collid)
 
static boolmcv_get_match_bitmap (PlannerInfo *root, List *clauses, Bitmapset *keys, List *exprs, MCVList *mcvlist, bool is_or)
 
Selectivity mcv_combine_selectivities (Selectivity simple_sel, Selectivity mcv_sel, Selectivity mcv_basesel, Selectivity mcv_totalsel)
 
Selectivity mcv_clauselist_selectivity (PlannerInfo *root, StatisticExtInfo *stat, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo, RelOptInfo *rel, Selectivity *basesel, Selectivity *totalsel)
 
Selectivity mcv_clause_selectivity_or (PlannerInfo *root, StatisticExtInfo *stat, MCVList *mcv, Node *clause, bool **or_matches, Selectivity *basesel, Selectivity *overlap_mcvsel, Selectivity *overlap_basesel, Selectivity *totalsel)
 
void statext_mcv_free (MCVList *mcvlist)
 
Datum statext_mcv_import (int elevel, int numattrs, Oid *atttypids, int32 *atttypmods, Oid *atttypcolls, int nitems, Datum *mcv_elems, bool *mcv_nulls, float8 *freqs, float8 *base_freqs)
 

Macro Definition Documentation

◆ ITEM_SIZE

#define ITEM_SIZE (   ndims)     ((ndims) * (sizeof(uint16) + sizeof(bool)) + 2 * sizeof(double))

Definition at line 51 of file mcv.c.

86 : ((value) && (match)))
87
88/*
89 * When processing a list of clauses, the bitmap item may get set to a value
90 * such that additional clauses can't change it. For example, when processing
91 * a list of clauses connected to AND, as soon as the item gets set to 'false'
92 * then it'll remain like that. Similarly clauses connected by OR and 'true'.
93 *
94 * Returns true when the value in the bitmap can't change no matter how the
95 * remaining clauses are evaluated.
96 */
97#define RESULT_IS_FINAL(value, is_or) ((is_or) ? (value) : (!(value)))
98
99/*
100 * get_mincount_for_mcv_list
101 * Determine the minimum number of times a value needs to appear in
102 * the sample for it to be included in the MCV list.
103 *
104 * We want to keep only values that appear sufficiently often in the
105 * sample that it is reasonable to extrapolate their sample frequencies to
106 * the entire table. We do this by placing an upper bound on the relative
107 * standard error of the sample frequency, so that any estimates the
108 * planner generates from the MCV statistics can be expected to be
109 * reasonably accurate.
110 *
111 * Since we are sampling without replacement, the sample frequency of a
112 * particular value is described by a hypergeometric distribution. A
113 * common rule of thumb when estimating errors in this situation is to
114 * require at least 10 instances of the value in the sample, in which case
115 * the distribution can be approximated by a normal distribution, and
116 * standard error analysis techniques can be applied. Given a sample size
117 * of n, a population size of N, and a sample frequency of p=cnt/n, the
118 * standard error of the proportion p is given by
119 * SE = sqrt(p*(1-p)/n) * sqrt((N-n)/(N-1))
120 * where the second term is the finite population correction. To get
121 * reasonably accurate planner estimates, we impose an upper bound on the
122 * relative standard error of 20% -- i.e., SE/p < 0.2. This 20% relative
123 * error bound is fairly arbitrary, but has been found empirically to work
124 * well. Rearranging this formula gives a lower bound on the number of
125 * instances of the value seen:
126 * cnt > n*(N-n) / (N-n+0.04*n*(N-1))
127 * This bound is at most 25, and approaches 0 as n approaches 0 or N. The
128 * case where n approaches 0 cannot happen in practice, since the sample
129 * size is at least 300. The case where n approaches N corresponds to
130 * sampling the whole table, in which case it is reasonable to keep
131 * the whole MCV list (have no lower bound), so it makes sense to apply
132 * this formula for all inputs, even though the above derivation is
133 * technically only valid when the right hand side is at least around 10.
134 *
135 * An alternative way to look at this formula is as follows -- assume that
136 * the number of instances of the value seen scales up to the entire
137 * table, so that the population count is K=N*cnt/n. Then the distribution
138 * in the sample is a hypergeometric distribution parameterised by N, n
139 * and K, and the bound above is mathematically equivalent to demanding
140 * that the standard deviation of that distribution is less than 20% of
141 * its mean. Thus the relative errors in any planner estimates produced
142 * from the MCV statistics are likely to be not too large.
143 */
144static double
145get_mincount_for_mcv_list(int samplerows, double totalrows)
146{
147 double n = samplerows;
148 double N = totalrows;
149 double numer,
150 denom;
151
152 numer = n * (N - n);
153 denom = N - n + 0.04 * n * (N - 1);
154
155 /* Guard against division by zero (possible if n = N = 1) */
156 if (denom == 0.0)
157 return 0.0;
158
159 return numer / denom;
160}
161
162/*
163 * Builds MCV list from the set of sampled rows.
164 *
165 * The algorithm is quite simple:
166 *
167 * (1) sort the data (default collation, '<' for the data type)
168 *
169 * (2) count distinct groups, decide how many to keep
170 *
171 * (3) build the MCV list using the threshold determined in (2)
172 *
173 * (4) remove rows represented by the MCV from the sample
174 *
175 */
176MCVList *
177statext_mcv_build(StatsBuildData *data, double totalrows, int stattarget)
178{
179 int i,
180 numattrs,
181 numrows,
182 ngroups,
183 nitems;
184 double mincount;
189
190 /* comparator for all the columns */
191 mss = build_mss(data);
192
193 /* sort the rows */
195 data->nattnums, data->attnums);
196
197 if (!items)
198 return NULL;
199
200 /* for convenience */
201 numattrs = data->nattnums;
202 numrows = data->numrows;
203
204 /* transform the sorted rows into groups (sorted by frequency) */
206
207 /*
208 * The maximum number of MCV items to store, based on the statistics
209 * target we computed for the statistics object (from the target set for
210 * the object itself, attributes and the system default). In any case, we
211 * can't keep more groups than we have available.
212 */
213 nitems = stattarget;
214 if (nitems > ngroups)
215 nitems = ngroups;
216
217 /*
218 * Decide how many items to keep in the MCV list. We can't use the same
219 * algorithm as per-column MCV lists, because that only considers the
220 * actual group frequency - but we're primarily interested in how the
221 * actual frequency differs from the base frequency (product of simple
222 * per-column frequencies, as if the columns were independent).
223 *
224 * Using the same algorithm might exclude items that are close to the
225 * "average" frequency of the sample. But that does not say whether the
226 * observed frequency is close to the base frequency or not. We also need
227 * to consider unexpectedly uncommon items (again, compared to the base
228 * frequency), and the single-column algorithm does not have to.
229 *
230 * We simply decide how many items to keep by computing the minimum count
231 * using get_mincount_for_mcv_list() and then keep all items that seem to
232 * be more common than that.
233 */
235
236 /*
237 * Walk the groups until we find the first group with a count below the
238 * mincount threshold (the index of that group is the number of groups we
239 * want to keep).
240 */
241 for (i = 0; i < nitems; i++)
242 {
243 if (groups[i].count < mincount)
244 {
245 nitems = i;
246 break;
247 }
248 }
249
250 /*
251 * At this point, we know the number of items for the MCV list. There
252 * might be none (for uniform distribution with many groups), and in that
253 * case, there will be no MCV list. Otherwise, construct the MCV list.
254 */
255 if (nitems > 0)
256 {
257 int j;
260
261 /* frequencies for values in each attribute */
262 SortItem **freqs;
263 int *nfreqs;
264
265 /* used to search values */
267 + sizeof(SortSupportData));
268
269 /* compute frequencies for values in each column */
272
273 /*
274 * Allocate the MCV list structure, set the global parameters.
275 */
277 sizeof(MCVItem) * nitems);
278
279 mcvlist->magic = STATS_MCV_MAGIC;
281 mcvlist->ndimensions = numattrs;
282 mcvlist->nitems = nitems;
283
284 /* store info about data type OIDs */
285 for (i = 0; i < numattrs; i++)
286 mcvlist->types[i] = data->stats[i]->attrtypid;
287
288 /* Copy the first chunk of groups into the result. */
289 for (i = 0; i < nitems; i++)
290 {
291 /* just point to the proper place in the list */
292 MCVItem *item = &mcvlist->items[i];
293
295 item->isnull = palloc_array(bool, numattrs);
296
297 /* copy values for the group */
298 memcpy(item->values, groups[i].values, sizeof(Datum) * numattrs);
299 memcpy(item->isnull, groups[i].isnull, sizeof(bool) * numattrs);
300
301 /* groups should be sorted by frequency in descending order */
302 Assert((i == 0) || (groups[i - 1].count >= groups[i].count));
303
304 /* group frequency */
305 item->frequency = (double) groups[i].count / numrows;
306
307 /* base frequency, if the attributes were independent */
308 item->base_frequency = 1.0;
309 for (j = 0; j < numattrs; j++)
310 {
311 SortItem *freq;
312
313 /* single dimension */
314 tmp->ndims = 1;
315 tmp->ssup[0] = mss->ssup[j];
316
317 /* fill search key */
318 key.values = &groups[i].values[j];
319 key.isnull = &groups[i].isnull[j];
320
321 freq = (SortItem *) bsearch_arg(&key, freqs[j], nfreqs[j],
322 sizeof(SortItem),
323 multi_sort_compare, tmp);
324
325 item->base_frequency *= ((double) freq->count) / numrows;
326 }
327 }
328
329 pfree(nfreqs);
330 pfree(freqs);
331 }
332
333 pfree(items);
334 pfree(groups);
335
336 return mcvlist;
337}
338
339/*
340 * build_mss
341 * Build a MultiSortSupport for the given StatsBuildData.
342 */
343static MultiSortSupport
345{
346 int i;
347 int numattrs = data->nattnums;
348
349 /* Sort by multiple columns (using array of SortSupport) */
351
352 /* prepare the sort functions for all the attributes */
353 for (i = 0; i < numattrs; i++)
354 {
355 VacAttrStats *colstat = data->stats[i];
357
359 if (type->lt_opr == InvalidOid) /* shouldn't happen */
360 elog(ERROR, "cache lookup failed for ordering operator for type %u",
361 colstat->attrtypid);
362
363 multi_sort_add_dimension(mss, i, type->lt_opr, colstat->attrcollid);
364 }
365
366 return mss;
367}
368
369/*
370 * count_distinct_groups
371 * Count distinct combinations of SortItems in the array.
372 *
373 * The array is assumed to be sorted according to the MultiSortSupport.
374 */
375static int
377{
378 int i;
379 int ndistinct;
380
381 ndistinct = 1;
382 for (i = 1; i < numrows; i++)
383 {
384 /* make sure the array really is sorted */
385 Assert(multi_sort_compare(&items[i], &items[i - 1], mss) >= 0);
386
387 if (multi_sort_compare(&items[i], &items[i - 1], mss) != 0)
388 ndistinct += 1;
389 }
390
391 return ndistinct;
392}
393
394/*
395 * compare_sort_item_count
396 * Comparator for sorting items by count (frequencies) in descending
397 * order.
398 */
399static int
400compare_sort_item_count(const void *a, const void *b, void *arg)
401{
402 const SortItem *ia = a;
403 const SortItem *ib = b;
404
405 if (ia->count == ib->count)
406 return 0;
407 else if (ia->count > ib->count)
408 return -1;
409
410 return 1;
411}
412
413/*
414 * build_distinct_groups
415 * Build an array of SortItems for distinct groups and counts matching
416 * items.
417 *
418 * The 'items' array is assumed to be sorted.
419 */
420static SortItem *
422 int *ndistinct)
423{
424 int i,
425 j;
426 int ngroups = count_distinct_groups(numrows, items, mss);
427
428 SortItem *groups = (SortItem *) palloc(ngroups * sizeof(SortItem));
429
430 j = 0;
431 groups[0] = items[0];
432 groups[0].count = 1;
433
434 for (i = 1; i < numrows; i++)
435 {
436 /* Assume sorted in ascending order. */
437 Assert(multi_sort_compare(&items[i], &items[i - 1], mss) >= 0);
438
439 /* New distinct group detected. */
440 if (multi_sort_compare(&items[i], &items[i - 1], mss) != 0)
441 {
442 groups[++j] = items[i];
443 groups[j].count = 0;
444 }
445
446 groups[j].count++;
447 }
448
449 /* ensure we filled the expected number of distinct groups */
450 Assert(j + 1 == ngroups);
451
452 /* Sort the distinct groups by frequency (in descending order). */
455
456 *ndistinct = ngroups;
457 return groups;
458}
459
460/* compare sort items (single dimension) */
461static int
462sort_item_compare(const void *a, const void *b, void *arg)
463{
464 SortSupport ssup = (SortSupport) arg;
465 const SortItem *ia = a;
466 const SortItem *ib = b;
467
468 return ApplySortComparator(ia->values[0], ia->isnull[0],
469 ib->values[0], ib->isnull[0],
470 ssup);
471}
472
473/*
474 * build_column_frequencies
475 * Compute frequencies of values in each column.
476 *
477 * This returns an array of SortItems for each attribute the MCV is built
478 * on, with a frequency (number of occurrences) for each value. This is
479 * then used to compute "base" frequency of MCV items.
480 *
481 * All the memory is allocated in a single chunk, so that a single pfree
482 * is enough to release it. We do not allocate space for values/isnull
483 * arrays in the SortItems, because we can simply point into the input
484 * groups directly.
485 */
486static SortItem **
489{
490 int i,
491 dim;
493 char *ptr;
494
495 Assert(groups);
497
498 /* allocate arrays for all columns as a single chunk */
499 ptr = palloc(MAXALIGN(sizeof(SortItem *) * mss->ndims) +
500 mss->ndims * MAXALIGN(sizeof(SortItem) * ngroups));
501
502 /* initial array of pointers */
503 result = (SortItem **) ptr;
504 ptr += MAXALIGN(sizeof(SortItem *) * mss->ndims);
505
506 for (dim = 0; dim < mss->ndims; dim++)
507 {
508 SortSupport ssup = &mss->ssup[dim];
509
510 /* array of values for a single column */
511 result[dim] = (SortItem *) ptr;
512 ptr += MAXALIGN(sizeof(SortItem) * ngroups);
513
514 /* extract data for the dimension */
515 for (i = 0; i < ngroups; i++)
516 {
517 /* point into the input groups */
518 result[dim][i].values = &groups[i].values[dim];
519 result[dim][i].isnull = &groups[i].isnull[dim];
520 result[dim][i].count = groups[i].count;
521 }
522
523 /* sort the values, deduplicate */
525 sort_item_compare, ssup);
526
527 /*
528 * Identify distinct values, compute frequency (there might be
529 * multiple MCV items containing this value, so we need to sum counts
530 * from all of them.
531 */
532 ncounts[dim] = 1;
533 for (i = 1; i < ngroups; i++)
534 {
535 if (sort_item_compare(&result[dim][i - 1], &result[dim][i], ssup) == 0)
536 {
537 result[dim][ncounts[dim] - 1].count += result[dim][i].count;
538 continue;
539 }
540
541 result[dim][ncounts[dim]] = result[dim][i];
542
543 ncounts[dim]++;
544 }
545 }
546
547 return result;
548}
549
550/*
551 * statext_mcv_load
552 * Load the MCV list for the indicated pg_statistic_ext_data tuple.
553 */
554MCVList *
555statext_mcv_load(Oid mvoid, bool inh)
556{
558 bool isnull;
562
563 if (!HeapTupleIsValid(htup))
564 elog(ERROR, "cache lookup failed for statistics object %u", mvoid);
565
568
569 if (isnull)
570 elog(ERROR,
571 "requested statistics kind \"%c\" is not yet built for statistics object %u",
573
575
576 ReleaseSysCache(htup);
577
578 return result;
579}
580
581
582/*
583 * statext_mcv_serialize
584 * Serialize MCV list into a pg_mcv_list value.
585 *
586 * The MCV items may include values of various data types, and it's reasonable
587 * to expect redundancy (values for a given attribute, repeated for multiple
588 * MCV list items). So we deduplicate the values into arrays, and then replace
589 * the values by indexes into those arrays.
590 *
591 * The overall structure of the serialized representation looks like this:
592 *
593 * +---------------+----------------+---------------------+-------+
594 * | header fields | dimension info | deduplicated values | items |
595 * +---------------+----------------+---------------------+-------+
596 *
597 * Where dimension info stores information about the type of the K-th
598 * attribute (e.g. typlen, typbyval and length of deduplicated values).
599 * Deduplicated values store deduplicated values for each attribute. And
600 * items store the actual MCV list items, with values replaced by indexes into
601 * the arrays.
602 *
603 * When serializing the items, we use uint16 indexes. The number of MCV items
604 * is limited by the statistics target (which is capped to 10k at the moment).
605 * We might increase this to 65k and still fit into uint16, so there's a bit of
606 * slack. Furthermore, this limit is on the number of distinct values per column,
607 * and we usually have few of those (and various combinations of them for the
608 * those MCV list). So uint16 seems fine for now.
609 *
610 * We don't really expect the serialization to save as much space as for
611 * histograms, as we are not doing any bucket splits (which is the source
612 * of high redundancy in histograms).
613 *
614 * TODO: Consider packing boolean flags (NULL) for each item into a single char
615 * (or a longer type) instead of using an array of bool items.
616 */
617bytea *
619{
620 int dim;
621 int ndims = mcvlist->ndimensions;
622
623 SortSupport ssup;
624 DimensionInfo *info;
625
627
628 /* serialized items (indexes into arrays, etc.) */
629 bytea *raw;
630 char *ptr;
631 char *endptr PG_USED_FOR_ASSERTS_ONLY;
632
633 /* values per dimension (and number of non-NULL values) */
634 Datum **values = palloc0_array(Datum *, ndims);
635 int *counts = palloc0_array(int, ndims);
636
637 /*
638 * We'll include some rudimentary information about the attribute types
639 * (length, by-val flag), so that we don't have to look them up while
640 * deserializing the MCV list (we already have the type OID in the
641 * header). This is safe because when changing the type of the attribute
642 * the statistics gets dropped automatically. We need to store the info
643 * about the arrays of deduplicated values anyway.
644 */
645 info = palloc0_array(DimensionInfo, ndims);
646
647 /* sort support data for all attributes included in the MCV list */
648 ssup = palloc0_array(SortSupportData, ndims);
649
650 /* collect and deduplicate values for each dimension (attribute) */
651 for (dim = 0; dim < ndims; dim++)
652 {
653 int ndistinct;
654 TypeCacheEntry *typentry;
655
656 /*
657 * Lookup the LT operator (can't get it from stats extra_data, as we
658 * don't know how to interpret that - scalar vs. array etc.).
659 */
660 typentry = lookup_type_cache(stats[dim]->attrtypid, TYPECACHE_LT_OPR);
661
662 /* copy important info about the data type (length, by-value) */
663 info[dim].typlen = stats[dim]->attrtype->typlen;
664 info[dim].typbyval = stats[dim]->attrtype->typbyval;
665
666 /* allocate space for values in the attribute and collect them */
667 values[dim] = palloc0_array(Datum, mcvlist->nitems);
668
669 for (uint32 i = 0; i < mcvlist->nitems; i++)
670 {
671 /* skip NULL values - we don't need to deduplicate those */
672 if (mcvlist->items[i].isnull[dim])
673 continue;
674
675 /* append the value at the end */
676 values[dim][counts[dim]] = mcvlist->items[i].values[dim];
677 counts[dim] += 1;
678 }
679
680 /* if there are just NULL values in this dimension, we're done */
681 if (counts[dim] == 0)
682 continue;
683
684 /* sort and deduplicate the data */
685 ssup[dim].ssup_cxt = CurrentMemoryContext;
686 ssup[dim].ssup_collation = stats[dim]->attrcollid;
687 ssup[dim].ssup_nulls_first = false;
688
689 PrepareSortSupportFromOrderingOp(typentry->lt_opr, &ssup[dim]);
690
691 qsort_interruptible(values[dim], counts[dim], sizeof(Datum),
692 compare_scalars_simple, &ssup[dim]);
693
694 /*
695 * Walk through the array and eliminate duplicate values, but keep the
696 * ordering (so that we can do a binary search later). We know there's
697 * at least one item as (counts[dim] != 0), so we can skip the first
698 * element.
699 */
700 ndistinct = 1; /* number of distinct values */
701 for (int i = 1; i < counts[dim]; i++)
702 {
703 /* expect sorted array */
704 Assert(compare_datums_simple(values[dim][i - 1], values[dim][i], &ssup[dim]) <= 0);
705
706 /* if the value is the same as the previous one, we can skip it */
707 if (!compare_datums_simple(values[dim][i - 1], values[dim][i], &ssup[dim]))
708 continue;
709
710 values[dim][ndistinct] = values[dim][i];
711 ndistinct += 1;
712 }
713
714 /* we must not exceed PG_UINT16_MAX, as we use uint16 indexes */
715 Assert(ndistinct <= PG_UINT16_MAX);
716
717 /*
718 * Store additional info about the attribute - number of deduplicated
719 * values, and also size of the serialized data. For fixed-length data
720 * types this is trivial to compute, for varwidth types we need to
721 * actually walk the array and sum the sizes.
722 */
723 info[dim].nvalues = ndistinct;
724
725 if (info[dim].typbyval) /* by-value data types */
726 {
727 info[dim].nbytes = info[dim].nvalues * info[dim].typlen;
728
729 /*
730 * We copy the data into the MCV item during deserialization, so
731 * we don't need to allocate any extra space.
732 */
733 info[dim].nbytes_aligned = 0;
734 }
735 else if (info[dim].typlen > 0) /* fixed-length by-ref */
736 {
737 /*
738 * We don't care about alignment in the serialized data, so we
739 * pack the data as much as possible. But we also track how much
740 * data will be needed after deserialization, and in that case we
741 * need to account for alignment of each item.
742 *
743 * Note: As the items are fixed-length, we could easily compute
744 * this during deserialization, but we do it here anyway.
745 */
746 info[dim].nbytes = info[dim].nvalues * info[dim].typlen;
747 info[dim].nbytes_aligned = info[dim].nvalues * MAXALIGN(info[dim].typlen);
748 }
749 else if (info[dim].typlen == -1) /* varlena */
750 {
751 info[dim].nbytes = 0;
752 info[dim].nbytes_aligned = 0;
753 for (int i = 0; i < info[dim].nvalues; i++)
754 {
755 Size len;
756
757 /*
758 * For varlena values, we detoast the values and store the
759 * length and data separately. We don't bother with alignment
760 * here, which means that during deserialization we need to
761 * copy the fields and only access the copies.
762 */
764
765 /* serialized length (uint32 length + data) */
767 info[dim].nbytes += sizeof(uint32); /* length */
768 info[dim].nbytes += len; /* value (no header) */
769
770 /*
771 * During deserialization we'll build regular varlena values
772 * with full headers, and we need to align them properly.
773 */
774 info[dim].nbytes_aligned += MAXALIGN(VARHDRSZ + len);
775 }
776 }
777 else if (info[dim].typlen == -2) /* cstring */
778 {
779 info[dim].nbytes = 0;
780 info[dim].nbytes_aligned = 0;
781 for (int i = 0; i < info[dim].nvalues; i++)
782 {
783 Size len;
784
785 /*
786 * cstring is handled similar to varlena - first we store the
787 * length as uint32 and then the data. We don't care about
788 * alignment, which means that during deserialization we need
789 * to copy the fields and only access the copies.
790 */
791
792 /* c-strings include terminator, so +1 byte */
793 len = strlen(DatumGetCString(values[dim][i])) + 1;
794 info[dim].nbytes += sizeof(uint32); /* length */
795 info[dim].nbytes += len; /* value */
796
797 /* space needed for properly aligned deserialized copies */
798 info[dim].nbytes_aligned += MAXALIGN(len);
799 }
800 }
801
802 /* we know (count>0) so there must be some data */
803 Assert(info[dim].nbytes > 0);
804 }
805
806 /*
807 * Now we can finally compute how much space we'll actually need for the
808 * whole serialized MCV list (varlena header, MCV header, dimension info
809 * for each attribute, deduplicated values and items).
810 */
811 total_length = (3 * sizeof(uint32)) /* magic + type + nitems */
812 + sizeof(AttrNumber) /* ndimensions */
813 + (ndims * sizeof(Oid)); /* attribute types */
814
815 /* dimension info */
816 total_length += ndims * sizeof(DimensionInfo);
817
818 /* add space for the arrays of deduplicated values */
819 for (int i = 0; i < ndims; i++)
820 total_length += info[i].nbytes;
821
822 /*
823 * And finally account for the items (those are fixed-length, thanks to
824 * replacing values with uint16 indexes into the deduplicated arrays).
825 */
826 total_length += mcvlist->nitems * ITEM_SIZE(dim);
827
828 /*
829 * Allocate space for the whole serialized MCV list (we'll skip bytes, so
830 * we set them to zero to make the result more compressible).
831 */
834
835 ptr = VARDATA(raw);
836 endptr = ptr + total_length;
837
838 /* copy the MCV list header fields, one by one */
839 memcpy(ptr, &mcvlist->magic, sizeof(uint32));
840 ptr += sizeof(uint32);
841
842 memcpy(ptr, &mcvlist->type, sizeof(uint32));
843 ptr += sizeof(uint32);
844
845 memcpy(ptr, &mcvlist->nitems, sizeof(uint32));
846 ptr += sizeof(uint32);
847
848 memcpy(ptr, &mcvlist->ndimensions, sizeof(AttrNumber));
849 ptr += sizeof(AttrNumber);
850
851 memcpy(ptr, mcvlist->types, sizeof(Oid) * ndims);
852 ptr += (sizeof(Oid) * ndims);
853
854 /* store information about the attributes (data amounts, ...) */
855 memcpy(ptr, info, sizeof(DimensionInfo) * ndims);
856 ptr += sizeof(DimensionInfo) * ndims;
857
858 /* Copy the deduplicated values for all attributes to the output. */
859 for (dim = 0; dim < ndims; dim++)
860 {
861 /* remember the starting point for Asserts later */
863
864 for (int i = 0; i < info[dim].nvalues; i++)
865 {
866 Datum value = values[dim][i];
867
868 if (info[dim].typbyval) /* passed by value */
869 {
870 Datum tmp;
871
872 /*
873 * For byval types, we need to copy just the significant bytes
874 * - we can't use memcpy directly, as that assumes
875 * little-endian behavior. store_att_byval does almost what
876 * we need, but it requires a properly aligned buffer - the
877 * output buffer does not guarantee that. So we simply use a
878 * local Datum variable (which guarantees proper alignment),
879 * and then copy the value from it.
880 */
881 store_att_byval(&tmp, value, info[dim].typlen);
882
883 memcpy(ptr, &tmp, info[dim].typlen);
884 ptr += info[dim].typlen;
885 }
886 else if (info[dim].typlen > 0) /* passed by reference */
887 {
888 /* no special alignment needed, treated as char array */
889 memcpy(ptr, DatumGetPointer(value), info[dim].typlen);
890 ptr += info[dim].typlen;
891 }
892 else if (info[dim].typlen == -1) /* varlena */
893 {
895
896 /* copy the length */
897 memcpy(ptr, &len, sizeof(uint32));
898 ptr += sizeof(uint32);
899
900 /* data from the varlena value (without the header) */
902 ptr += len;
903 }
904 else if (info[dim].typlen == -2) /* cstring */
905 {
907
908 /* copy the length */
909 memcpy(ptr, &len, sizeof(uint32));
910 ptr += sizeof(uint32);
911
912 /* value */
914 ptr += len;
915 }
916
917 /* no underflows or overflows */
918 Assert((ptr > start) && ((ptr - start) <= info[dim].nbytes));
919 }
920
921 /* we should get exactly nbytes of data for this dimension */
922 Assert((ptr - start) == info[dim].nbytes);
923 }
924
925 /* Serialize the items, with uint16 indexes instead of the values. */
926 for (uint32 i = 0; i < mcvlist->nitems; i++)
927 {
928 MCVItem *mcvitem = &mcvlist->items[i];
929
930 /* don't write beyond the allocated space */
931 Assert(ptr <= (endptr - ITEM_SIZE(dim)));
932
933 /* copy NULL and frequency flags into the serialized MCV */
934 memcpy(ptr, mcvitem->isnull, sizeof(bool) * ndims);
935 ptr += sizeof(bool) * ndims;
936
937 memcpy(ptr, &mcvitem->frequency, sizeof(double));
938 ptr += sizeof(double);
939
940 memcpy(ptr, &mcvitem->base_frequency, sizeof(double));
941 ptr += sizeof(double);
942
943 /* store the indexes last */
944 for (dim = 0; dim < ndims; dim++)
945 {
946 uint16 index = 0;
947 Datum *value;
948
949 /* do the lookup only for non-NULL values */
950 if (!mcvitem->isnull[dim])
951 {
952 value = (Datum *) bsearch_arg(&mcvitem->values[dim], values[dim],
953 info[dim].nvalues, sizeof(Datum),
954 compare_scalars_simple, &ssup[dim]);
955
956 Assert(value != NULL); /* serialization or deduplication
957 * error */
958
959 /* compute index within the deduplicated array */
960 index = (uint16) (value - values[dim]);
961
962 /* check the index is within expected bounds */
963 Assert(index < info[dim].nvalues);
964 }
965
966 /* copy the index into the serialized MCV */
967 memcpy(ptr, &index, sizeof(uint16));
968 ptr += sizeof(uint16);
969 }
970
971 /* make sure we don't overflow the allocated value */
972 Assert(ptr <= endptr);
973 }
974
975 /* at this point we expect to match the total_length exactly */
976 Assert(ptr == endptr);
977
978 pfree(values);
979 pfree(counts);
980
981 return raw;
982}
983
984/*
985 * statext_mcv_deserialize
986 * Reads serialized MCV list into MCVList structure.
987 *
988 * All the memory needed by the MCV list is allocated as a single chunk, so
989 * it's possible to simply pfree() it at once.
990 */
991MCVList *
993{
994 int dim,
995 i;
998 char *raw;
999 char *ptr;
1000 char *endptr PG_USED_FOR_ASSERTS_ONLY;
1001
1002 int ndims,
1003 nitems;
1004 DimensionInfo *info = NULL;
1005
1006 /* local allocation buffer (used only for deserialization) */
1007 Datum **map = NULL;
1008
1009 /* MCV list */
1010 Size mcvlen;
1011
1012 /* buffer used for the result */
1013 Size datalen;
1014 char *dataptr;
1015 char *valuesptr;
1016 char *isnullptr;
1017
1018 if (data == NULL)
1019 return NULL;
1020
1021 /*
1022 * We can't possibly deserialize a MCV list if there's not even a complete
1023 * header. We need an explicit formula here, because we serialize the
1024 * header fields one by one, so we need to ignore struct alignment.
1025 */
1027 elog(ERROR, "invalid MCV size %zu (expected at least %zu)",
1029
1030 /* read the MCV list header */
1032
1033 /* pointer to the data part (skip the varlena header) */
1034 raw = (char *) data;
1035 ptr = VARDATA_ANY(raw);
1036 endptr = raw + VARSIZE_ANY(data);
1037
1038 /* get the header and perform further sanity checks */
1039 memcpy(&mcvlist->magic, ptr, sizeof(uint32));
1040 ptr += sizeof(uint32);
1041
1042 memcpy(&mcvlist->type, ptr, sizeof(uint32));
1043 ptr += sizeof(uint32);
1044
1045 memcpy(&mcvlist->nitems, ptr, sizeof(uint32));
1046 ptr += sizeof(uint32);
1047
1048 memcpy(&mcvlist->ndimensions, ptr, sizeof(AttrNumber));
1049 ptr += sizeof(AttrNumber);
1050
1051 if (mcvlist->magic != STATS_MCV_MAGIC)
1052 elog(ERROR, "invalid MCV magic %u (expected %u)",
1053 mcvlist->magic, STATS_MCV_MAGIC);
1054
1055 if (mcvlist->type != STATS_MCV_TYPE_BASIC)
1056 elog(ERROR, "invalid MCV type %u (expected %u)",
1058
1059 if (mcvlist->ndimensions == 0)
1060 elog(ERROR, "invalid zero-length dimension array in MCVList");
1061 else if ((mcvlist->ndimensions > STATS_MAX_DIMENSIONS) ||
1062 (mcvlist->ndimensions < 0))
1063 elog(ERROR, "invalid length (%d) dimension array in MCVList",
1064 mcvlist->ndimensions);
1065
1066 if (mcvlist->nitems == 0)
1067 elog(ERROR, "invalid zero-length item array in MCVList");
1068 else if (mcvlist->nitems > STATS_MCVLIST_MAX_ITEMS)
1069 elog(ERROR, "invalid length (%u) item array in MCVList",
1070 mcvlist->nitems);
1071
1072 nitems = mcvlist->nitems;
1073 ndims = mcvlist->ndimensions;
1074
1075 /*
1076 * Check amount of data including DimensionInfo for all dimensions and
1077 * also the serialized items (including uint16 indexes). Also, walk
1078 * through the dimension information and add it to the sum.
1079 */
1081
1082 /*
1083 * Check that we have at least the dimension and info records, along with
1084 * the items. We don't know the size of the serialized values yet. We need
1085 * to do this check first, before accessing the dimension info.
1086 */
1088 elog(ERROR, "invalid MCV size %zu (expected %zu)",
1090
1091 /* Now copy the array of type Oids. */
1092 memcpy(mcvlist->types, ptr, sizeof(Oid) * ndims);
1093 ptr += (sizeof(Oid) * ndims);
1094
1095 /* Now it's safe to access the dimension info. */
1096 info = palloc(ndims * sizeof(DimensionInfo));
1097
1098 memcpy(info, ptr, ndims * sizeof(DimensionInfo));
1099 ptr += (ndims * sizeof(DimensionInfo));
1100
1101 /* account for the value arrays */
1102 for (dim = 0; dim < ndims; dim++)
1103 {
1104 /*
1105 * XXX I wonder if we can/should rely on asserts here. Maybe those
1106 * checks should be done every time?
1107 */
1108 Assert(info[dim].nvalues >= 0);
1109 Assert(info[dim].nbytes >= 0);
1110
1111 expected_size += info[dim].nbytes;
1112 }
1113
1114 /*
1115 * Now we know the total expected MCV size, including all the pieces
1116 * (header, dimension info. items and deduplicated data). So do the final
1117 * check on size.
1118 */
1120 elog(ERROR, "invalid MCV size %zu (expected %zu)",
1122
1123 /*
1124 * We need an array of Datum values for each dimension, so that we can
1125 * easily translate the uint16 indexes later. We also need a top-level
1126 * array of pointers to those per-dimension arrays.
1127 *
1128 * While allocating the arrays for dimensions, compute how much space we
1129 * need for a copy of the by-ref data, as we can't simply point to the
1130 * original values (it might go away).
1131 */
1132 datalen = 0; /* space for by-ref data */
1133 map = palloc_array(Datum *, ndims);
1134
1135 for (dim = 0; dim < ndims; dim++)
1136 {
1137 map[dim] = palloc_array(Datum, info[dim].nvalues);
1138
1139 /* space needed for a copy of data for by-ref types */
1140 datalen += info[dim].nbytes_aligned;
1141 }
1142
1143 /*
1144 * Now resize the MCV list so that the allocation includes all the data.
1145 *
1146 * Allocate space for a copy of the data, as we can't simply reference the
1147 * serialized data - it's not aligned properly, and it may disappear while
1148 * we're still using the MCV list, e.g. due to catcache release.
1149 *
1150 * We do care about alignment here, because we will allocate all the
1151 * pieces at once, but then use pointers to different parts.
1152 */
1153 mcvlen = MAXALIGN(offsetof(MCVList, items) + (sizeof(MCVItem) * nitems));
1154
1155 /* arrays of values and isnull flags for all MCV items */
1156 mcvlen += nitems * MAXALIGN(sizeof(Datum) * ndims);
1157 mcvlen += nitems * MAXALIGN(sizeof(bool) * ndims);
1158
1159 /* we don't quite need to align this, but it makes some asserts easier */
1160 mcvlen += MAXALIGN(datalen);
1161
1162 /* now resize the deserialized MCV list, and compute pointers to parts */
1164
1165 /* pointer to the beginning of values/isnull arrays */
1166 valuesptr = (char *) mcvlist
1167 + MAXALIGN(offsetof(MCVList, items) + (sizeof(MCVItem) * nitems));
1168
1169 isnullptr = valuesptr + (nitems * MAXALIGN(sizeof(Datum) * ndims));
1170
1171 dataptr = isnullptr + (nitems * MAXALIGN(sizeof(bool) * ndims));
1172
1173 /*
1174 * Build mapping (index => value) for translating the serialized data into
1175 * the in-memory representation.
1176 */
1177 for (dim = 0; dim < ndims; dim++)
1178 {
1179 /* remember start position in the input array */
1180 char *start PG_USED_FOR_ASSERTS_ONLY = ptr;
1181
1182 if (info[dim].typbyval)
1183 {
1184 /* for by-val types we simply copy data into the mapping */
1185 for (i = 0; i < info[dim].nvalues; i++)
1186 {
1187 Datum v = 0;
1188
1189 memcpy(&v, ptr, info[dim].typlen);
1190 ptr += info[dim].typlen;
1191
1192 map[dim][i] = fetch_att(&v, true, info[dim].typlen);
1193
1194 /* no under/overflow of input array */
1195 Assert(ptr <= (start + info[dim].nbytes));
1196 }
1197 }
1198 else
1199 {
1200 /* for by-ref types we need to also make a copy of the data */
1201
1202 /* passed by reference, but fixed length (name, tid, ...) */
1203 if (info[dim].typlen > 0)
1204 {
1205 for (i = 0; i < info[dim].nvalues; i++)
1206 {
1207 memcpy(dataptr, ptr, info[dim].typlen);
1208 ptr += info[dim].typlen;
1209
1210 /* just point into the array */
1211 map[dim][i] = PointerGetDatum(dataptr);
1212 dataptr += MAXALIGN(info[dim].typlen);
1213 }
1214 }
1215 else if (info[dim].typlen == -1)
1216 {
1217 /* varlena */
1218 for (i = 0; i < info[dim].nvalues; i++)
1219 {
1220 uint32 len;
1221
1222 /* read the uint32 length */
1223 memcpy(&len, ptr, sizeof(uint32));
1224 ptr += sizeof(uint32);
1225
1226 /* the length is data-only */
1227 SET_VARSIZE(dataptr, len + VARHDRSZ);
1228 memcpy(VARDATA(dataptr), ptr, len);
1229 ptr += len;
1230
1231 /* just point into the array */
1232 map[dim][i] = PointerGetDatum(dataptr);
1233
1234 /* skip to place of the next deserialized value */
1235 dataptr += MAXALIGN(len + VARHDRSZ);
1236 }
1237 }
1238 else if (info[dim].typlen == -2)
1239 {
1240 /* cstring */
1241 for (i = 0; i < info[dim].nvalues; i++)
1242 {
1243 uint32 len;
1244
1245 memcpy(&len, ptr, sizeof(uint32));
1246 ptr += sizeof(uint32);
1247
1248 memcpy(dataptr, ptr, len);
1249 ptr += len;
1250
1251 /* just point into the array */
1252 map[dim][i] = PointerGetDatum(dataptr);
1253 dataptr += MAXALIGN(len);
1254 }
1255 }
1256
1257 /* no under/overflow of input array */
1258 Assert(ptr <= (start + info[dim].nbytes));
1259
1260 /* no overflow of the output mcv value */
1261 Assert(dataptr <= ((char *) mcvlist + mcvlen));
1262 }
1263
1264 /* check we consumed input data for this dimension exactly */
1265 Assert(ptr == (start + info[dim].nbytes));
1266 }
1267
1268 /* we should have also filled the MCV list exactly */
1269 Assert(dataptr == ((char *) mcvlist + mcvlen));
1270
1271 /* deserialize the MCV items and translate the indexes to Datums */
1272 for (i = 0; i < nitems; i++)
1273 {
1274 MCVItem *item = &mcvlist->items[i];
1275
1276 item->values = (Datum *) valuesptr;
1277 valuesptr += MAXALIGN(sizeof(Datum) * ndims);
1278
1279 item->isnull = (bool *) isnullptr;
1280 isnullptr += MAXALIGN(sizeof(bool) * ndims);
1281
1282 memcpy(item->isnull, ptr, sizeof(bool) * ndims);
1283 ptr += sizeof(bool) * ndims;
1284
1285 memcpy(&item->frequency, ptr, sizeof(double));
1286 ptr += sizeof(double);
1287
1288 memcpy(&item->base_frequency, ptr, sizeof(double));
1289 ptr += sizeof(double);
1290
1291 /* finally translate the indexes (for non-NULL only) */
1292 for (dim = 0; dim < ndims; dim++)
1293 {
1294 uint16 index;
1295
1296 memcpy(&index, ptr, sizeof(uint16));
1297 ptr += sizeof(uint16);
1298
1299 if (item->isnull[dim])
1300 continue;
1301
1302 item->values[dim] = map[dim][index];
1303 }
1304
1305 /* check we're not overflowing the input */
1306 Assert(ptr <= endptr);
1307 }
1308
1309 /* check that we processed all the data */
1310 Assert(ptr == endptr);
1311
1312 /* release the buffers used for mapping */
1313 for (dim = 0; dim < ndims; dim++)
1314 pfree(map[dim]);
1315
1316 pfree(map);
1317
1318 return mcvlist;
1319}
1320
1321/*
1322 * SRF with details about buckets of a histogram:
1323 *
1324 * - item ID (0...nitems)
1325 * - values (string array)
1326 * - nulls only (boolean array)
1327 * - frequency (double precision)
1328 * - base_frequency (double precision)
1329 *
1330 * The input is the OID of the statistics, and there are no rows returned if
1331 * the statistics contains no histogram.
1332 */
1333Datum
1335{
1337
1338 /* stuff done only on the first call of the function */
1339 if (SRF_IS_FIRSTCALL())
1340 {
1341 MemoryContext oldcontext;
1343 TupleDesc tupdesc;
1344
1345 /* create a function context for cross-call persistence */
1347
1348 /* switch to memory context appropriate for multiple function calls */
1349 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1350
1352
1353 funcctx->user_fctx = mcvlist;
1354
1355 /* total number of tuples to be returned */
1356 funcctx->max_calls = 0;
1357 if (funcctx->user_fctx != NULL)
1358 funcctx->max_calls = mcvlist->nitems;
1359
1360 /* Build a tuple descriptor for our result type */
1361 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1362 ereport(ERROR,
1364 errmsg("function returning record called in context "
1365 "that cannot accept type record")));
1366 tupdesc = BlessTupleDesc(tupdesc);
1367
1368 /*
1369 * generate attribute metadata needed later to produce tuples from raw
1370 * C strings
1371 */
1372 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1373
1374 MemoryContextSwitchTo(oldcontext);
1375 }
1376
1377 /* stuff done on every call of the function */
1379
1380 if (funcctx->call_cntr < funcctx->max_calls) /* do when there is more
1381 * left to send */
1382 {
1383 Datum values[5];
1384 bool nulls[5];
1385 HeapTuple tuple;
1386 Datum result;
1389
1390 int i;
1392 MCVItem *item;
1393
1394 mcvlist = (MCVList *) funcctx->user_fctx;
1395
1396 Assert(funcctx->call_cntr < mcvlist->nitems);
1397
1398 item = &mcvlist->items[funcctx->call_cntr];
1399
1400 for (i = 0; i < mcvlist->ndimensions; i++)
1401 {
1402
1404 BoolGetDatum(item->isnull[i]),
1405 false,
1406 BOOLOID,
1408
1409 if (!item->isnull[i])
1410 {
1411 bool isvarlena;
1412 Oid outfunc;
1414 Datum val;
1415 text *txt;
1416
1417 /* lookup output func for the type */
1420
1421 val = FunctionCall1(&fmgrinfo, item->values[i]);
1423
1426 false,
1427 TEXTOID,
1429 }
1430 else
1432 (Datum) 0,
1433 true,
1434 TEXTOID,
1436 }
1437
1438 values[0] = Int32GetDatum(funcctx->call_cntr);
1441 values[3] = Float8GetDatum(item->frequency);
1443
1444 /* no NULLs in the tuple */
1445 memset(nulls, 0, sizeof(nulls));
1446
1447 /* build a tuple */
1448 tuple = heap_form_tuple(funcctx->attinmeta->tupdesc, values, nulls);
1449
1450 /* make the tuple into a datum */
1451 result = HeapTupleGetDatum(tuple);
1452
1454 }
1455 else /* do when there is no more left */
1456 {
1458 }
1459}
1460
1461/*
1462 * pg_mcv_list_in - input routine for type pg_mcv_list.
1463 *
1464 * pg_mcv_list is real enough to be a table column, but it has no operations
1465 * of its own, and disallows input too
1466 */
1467Datum
1469{
1470 /*
1471 * pg_mcv_list stores the data in binary form and parsing text input is
1472 * not needed, so disallow this.
1473 */
1474 ereport(ERROR,
1476 errmsg("cannot accept a value of type %s", "pg_mcv_list")));
1477
1478 PG_RETURN_VOID(); /* keep compiler quiet */
1479}
1480
1481
1482/*
1483 * pg_mcv_list_out - output routine for type pg_mcv_list.
1484 *
1485 * MCV lists are serialized into a bytea value, so we simply call byteaout()
1486 * to serialize the value into text. But it'd be nice to serialize that into
1487 * a meaningful representation (e.g. for inspection by people).
1488 *
1489 * XXX This should probably return something meaningful, similar to what
1490 * pg_dependencies_out does. Not sure how to deal with the deduplicated
1491 * values, though - do we want to expand that or not?
1492 */
1493Datum
1495{
1496 return byteaout(fcinfo);
1497}
1498
1499/*
1500 * pg_mcv_list_recv - binary input routine for type pg_mcv_list.
1501 */
1502Datum
1504{
1505 ereport(ERROR,
1507 errmsg("cannot accept a value of type %s", "pg_mcv_list")));
1508
1509 PG_RETURN_VOID(); /* keep compiler quiet */
1510}
1511
1512/*
1513 * pg_mcv_list_send - binary output routine for type pg_mcv_list.
1514 *
1515 * MCV lists are serialized in a bytea value (although the type is named
1516 * differently), so let's just send that.
1517 */
1518Datum
1520{
1521 return byteasend(fcinfo);
1522}
1523
1524/*
1525 * match the attribute/expression to a dimension of the statistic
1526 *
1527 * Returns the zero-based index of the matching statistics dimension.
1528 * Optionally determines the collation.
1529 */
1530static int
1531mcv_match_expression(Node *expr, Bitmapset *keys, List *exprs, Oid *collid)
1532{
1533 int idx;
1534
1535 if (IsA(expr, Var))
1536 {
1537 /* simple Var, so just lookup using varattno */
1538 Var *var = (Var *) expr;
1539
1540 if (collid)
1541 *collid = var->varcollid;
1542
1543 idx = bms_member_index(keys, var->varattno);
1544
1545 if (idx < 0)
1546 elog(ERROR, "variable not found in statistics object");
1547 }
1548 else
1549 {
1550 /* expression - lookup in stats expressions */
1551 ListCell *lc;
1552
1553 if (collid)
1554 *collid = exprCollation(expr);
1555
1556 /* expressions are stored after the simple columns */
1557 idx = bms_num_members(keys);
1558 foreach(lc, exprs)
1559 {
1560 Node *stat_expr = (Node *) lfirst(lc);
1561
1562 if (equal(expr, stat_expr))
1563 break;
1564
1565 idx++;
1566 }
1567
1568 if (lc == NULL)
1569 elog(ERROR, "expression not found in statistics object");
1570 }
1571
1572 return idx;
1573}
1574
1575/*
1576 * mcv_get_match_bitmap
1577 * Evaluate clauses using the MCV list, and update the match bitmap.
1578 *
1579 * A match bitmap keeps match/mismatch status for each MCV item, and we
1580 * update it based on additional clauses. We also use it to skip items
1581 * that can't possibly match (e.g. item marked as "mismatch" can't change
1582 * to "match" when evaluating AND clause list).
1583 *
1584 * The function also returns a flag indicating whether there was an
1585 * equality condition for all attributes, the minimum frequency in the MCV
1586 * list, and a total MCV frequency (sum of frequencies for all items).
1587 *
1588 * XXX Currently the match bitmap uses a bool for each MCV item, which is
1589 * somewhat wasteful as we could do with just a single bit, thus reducing
1590 * the size to ~1/8. It would also allow us to combine bitmaps simply using
1591 * & and |, which should be faster than min/max. The bitmaps are fairly
1592 * small, though (thanks to the cap on the MCV list size).
1593 */
1594static bool *
1596 Bitmapset *keys, List *exprs,
1597 MCVList *mcvlist, bool is_or)
1598{
1599 ListCell *l;
1600 bool *matches;
1601
1602 /* The bitmap may be partially built. */
1603 Assert(clauses != NIL);
1604 Assert(mcvlist != NULL);
1605 Assert(mcvlist->nitems > 0);
1607
1608 matches = palloc_array(bool, mcvlist->nitems);
1609 memset(matches, !is_or, sizeof(bool) * mcvlist->nitems);
1610
1611 /*
1612 * Loop through the list of clauses, and for each of them evaluate all the
1613 * MCV items not yet eliminated by the preceding clauses.
1614 */
1615 foreach(l, clauses)
1616 {
1617 Node *clause = (Node *) lfirst(l);
1618
1619 /* if it's a RestrictInfo, then extract the clause */
1620 if (IsA(clause, RestrictInfo))
1621 clause = (Node *) ((RestrictInfo *) clause)->clause;
1622
1623 /*
1624 * Handle the various types of clauses - OpClause, NullTest and
1625 * AND/OR/NOT
1626 */
1627 if (is_opclause(clause))
1628 {
1629 OpExpr *expr = (OpExpr *) clause;
1631
1632 /* valid only after examine_opclause_args returns true */
1634 Const *cst;
1635 bool expronleft;
1636 int idx;
1637 Oid collid;
1638
1639 fmgr_info(get_opcode(expr->opno), &opproc);
1640
1641 /* extract the var/expr and const from the expression */
1643 elog(ERROR, "incompatible clause");
1644
1645 /* match the attribute/expression to a dimension of the statistic */
1646 idx = mcv_match_expression(clause_expr, keys, exprs, &collid);
1647
1648 /*
1649 * Walk through the MCV items and evaluate the current clause. We
1650 * can skip items that were already ruled out, and terminate if
1651 * there are no remaining MCV items that might possibly match.
1652 */
1653 for (uint32 i = 0; i < mcvlist->nitems; i++)
1654 {
1655 bool match = true;
1656 MCVItem *item = &mcvlist->items[i];
1657
1658 Assert(idx >= 0);
1659
1660 /*
1661 * When the MCV item or the Const value is NULL we can treat
1662 * this as a mismatch. We must not call the operator because
1663 * of strictness.
1664 */
1665 if (item->isnull[idx] || cst->constisnull)
1666 {
1667 matches[i] = RESULT_MERGE(matches[i], is_or, false);
1668 continue;
1669 }
1670
1671 /*
1672 * Skip MCV items that can't change result in the bitmap. Once
1673 * the value gets false for AND-lists, or true for OR-lists,
1674 * we don't need to look at more clauses.
1675 */
1677 continue;
1678
1679 /*
1680 * First check whether the constant is below the lower
1681 * boundary (in that case we can skip the bucket, because
1682 * there's no overlap).
1683 *
1684 * We don't store collations used to build the statistics, but
1685 * we can use the collation for the attribute itself, as
1686 * stored in varcollid. We do reset the statistics after a
1687 * type change (including collation change), so this is OK.
1688 * For expressions, we use the collation extracted from the
1689 * expression itself.
1690 */
1691 if (expronleft)
1693 collid,
1694 item->values[idx],
1695 cst->constvalue));
1696 else
1698 collid,
1699 cst->constvalue,
1700 item->values[idx]));
1701
1702 /* update the match bitmap with the result */
1703 matches[i] = RESULT_MERGE(matches[i], is_or, match);
1704 }
1705 }
1706 else if (IsA(clause, ScalarArrayOpExpr))
1707 {
1708 ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) clause;
1710
1711 /* valid only after examine_opclause_args returns true */
1713 Const *cst;
1714 bool expronleft;
1715 Oid collid;
1716 int idx;
1717
1718 /* array evaluation */
1720 int16 elmlen;
1721 bool elmbyval;
1722 char elmalign;
1723 int num_elems;
1724 Datum *elem_values;
1725 bool *elem_nulls;
1726
1727 fmgr_info(get_opcode(expr->opno), &opproc);
1728
1729 /* extract the var/expr and const from the expression */
1731 elog(ERROR, "incompatible clause");
1732
1733 /* We expect Var on left */
1734 if (!expronleft)
1735 elog(ERROR, "incompatible clause");
1736
1737 /*
1738 * Deconstruct the array constant, unless it's NULL (we'll cover
1739 * that case below)
1740 */
1741 if (!cst->constisnull)
1742 {
1743 arrayval = DatumGetArrayTypeP(cst->constvalue);
1745 &elmlen, &elmbyval, &elmalign);
1748 elmlen, elmbyval, elmalign,
1749 &elem_values, &elem_nulls, &num_elems);
1750 }
1751
1752 /* match the attribute/expression to a dimension of the statistic */
1753 idx = mcv_match_expression(clause_expr, keys, exprs, &collid);
1754
1755 /*
1756 * Walk through the MCV items and evaluate the current clause. We
1757 * can skip items that were already ruled out, and terminate if
1758 * there are no remaining MCV items that might possibly match.
1759 */
1760 for (uint32 i = 0; i < mcvlist->nitems; i++)
1761 {
1762 int j;
1763 bool match = !expr->useOr;
1764 MCVItem *item = &mcvlist->items[i];
1765
1766 /*
1767 * When the MCV item or the Const value is NULL we can treat
1768 * this as a mismatch. We must not call the operator because
1769 * of strictness.
1770 */
1771 if (item->isnull[idx] || cst->constisnull)
1772 {
1773 matches[i] = RESULT_MERGE(matches[i], is_or, false);
1774 continue;
1775 }
1776
1777 /*
1778 * Skip MCV items that can't change result in the bitmap. Once
1779 * the value gets false for AND-lists, or true for OR-lists,
1780 * we don't need to look at more clauses.
1781 */
1783 continue;
1784
1785 for (j = 0; j < num_elems; j++)
1786 {
1787 Datum elem_value = elem_values[j];
1788 bool elem_isnull = elem_nulls[j];
1789 bool elem_match;
1790
1791 /* NULL values always evaluate as not matching. */
1792 if (elem_isnull)
1793 {
1794 match = RESULT_MERGE(match, expr->useOr, false);
1795 continue;
1796 }
1797
1798 /*
1799 * Stop evaluating the array elements once we reach a
1800 * matching value that can't change - ALL() is the same as
1801 * AND-list, ANY() is the same as OR-list.
1802 */
1803 if (RESULT_IS_FINAL(match, expr->useOr))
1804 break;
1805
1807 collid,
1808 item->values[idx],
1809 elem_value));
1810
1811 match = RESULT_MERGE(match, expr->useOr, elem_match);
1812 }
1813
1814 /* update the match bitmap with the result */
1815 matches[i] = RESULT_MERGE(matches[i], is_or, match);
1816 }
1817 }
1818 else if (IsA(clause, NullTest))
1819 {
1820 NullTest *expr = (NullTest *) clause;
1821 Node *clause_expr = (Node *) (expr->arg);
1822
1823 /* match the attribute/expression to a dimension of the statistic */
1824 int idx = mcv_match_expression(clause_expr, keys, exprs, NULL);
1825
1826 /*
1827 * Walk through the MCV items and evaluate the current clause. We
1828 * can skip items that were already ruled out, and terminate if
1829 * there are no remaining MCV items that might possibly match.
1830 */
1831 for (uint32 i = 0; i < mcvlist->nitems; i++)
1832 {
1833 bool match = false; /* assume mismatch */
1834 MCVItem *item = &mcvlist->items[i];
1835
1836 /* if the clause mismatches the MCV item, update the bitmap */
1837 switch (expr->nulltesttype)
1838 {
1839 case IS_NULL:
1840 match = (item->isnull[idx]) ? true : match;
1841 break;
1842
1843 case IS_NOT_NULL:
1844 match = (!item->isnull[idx]) ? true : match;
1845 break;
1846 }
1847
1848 /* now, update the match bitmap, depending on OR/AND type */
1849 matches[i] = RESULT_MERGE(matches[i], is_or, match);
1850 }
1851 }
1852 else if (is_orclause(clause) || is_andclause(clause))
1853 {
1854 /* AND/OR clause, with all subclauses being compatible */
1855
1856 BoolExpr *bool_clause = ((BoolExpr *) clause);
1857 List *bool_clauses = bool_clause->args;
1858
1859 /* match/mismatch bitmap for each MCV item */
1860 bool *bool_matches = NULL;
1861
1864
1865 /* build the match bitmap for the OR-clauses */
1867 mcvlist, is_orclause(clause));
1868
1869 /*
1870 * Merge the bitmap produced by mcv_get_match_bitmap into the
1871 * current one. We need to consider if we're evaluating AND or OR
1872 * condition when merging the results.
1873 */
1874 for (uint32 i = 0; i < mcvlist->nitems; i++)
1876
1878 }
1879 else if (is_notclause(clause))
1880 {
1881 /* NOT clause, with all subclauses compatible */
1882
1883 BoolExpr *not_clause = ((BoolExpr *) clause);
1884 List *not_args = not_clause->args;
1885
1886 /* match/mismatch bitmap for each MCV item */
1887 bool *not_matches = NULL;
1888
1889 Assert(not_args != NIL);
1891
1892 /* build the match bitmap for the NOT-clause */
1894 mcvlist, false);
1895
1896 /*
1897 * Merge the bitmap produced by mcv_get_match_bitmap into the
1898 * current one. We're handling a NOT clause, so invert the result
1899 * before merging it into the global bitmap.
1900 */
1901 for (uint32 i = 0; i < mcvlist->nitems; i++)
1903
1905 }
1906 else if (IsA(clause, Var))
1907 {
1908 /* Var (has to be a boolean Var, possibly from below NOT) */
1909
1910 Var *var = (Var *) (clause);
1911
1912 /* match the attribute to a dimension of the statistic */
1913 int idx = bms_member_index(keys, var->varattno);
1914
1915 Assert(var->vartype == BOOLOID);
1916
1917 /*
1918 * Walk through the MCV items and evaluate the current clause. We
1919 * can skip items that were already ruled out, and terminate if
1920 * there are no remaining MCV items that might possibly match.
1921 */
1922 for (uint32 i = 0; i < mcvlist->nitems; i++)
1923 {
1924 MCVItem *item = &mcvlist->items[i];
1925 bool match = false;
1926
1927 /* if the item is NULL, it's a mismatch */
1928 if (!item->isnull[idx] && DatumGetBool(item->values[idx]))
1929 match = true;
1930
1931 /* update the result bitmap */
1932 matches[i] = RESULT_MERGE(matches[i], is_or, match);
1933 }
1934 }
1935 else
1936 {
1937 /* Otherwise, it must be a bare boolean-returning expression */
1938 int idx;
1939
1940 /* match the expression to a dimension of the statistic */
1941 idx = mcv_match_expression(clause, keys, exprs, NULL);
1942
1943 /*
1944 * Walk through the MCV items and evaluate the current clause. We
1945 * can skip items that were already ruled out, and terminate if
1946 * there are no remaining MCV items that might possibly match.
1947 */
1948 for (uint32 i = 0; i < mcvlist->nitems; i++)
1949 {
1950 bool match;
1951 MCVItem *item = &mcvlist->items[i];
1952
1953 /* "match" just means it's bool TRUE */
1954 match = !item->isnull[idx] && DatumGetBool(item->values[idx]);
1955
1956 /* now, update the match bitmap, depending on OR/AND type */
1957 matches[i] = RESULT_MERGE(matches[i], is_or, match);
1958 }
1959 }
1960 }
1961
1962 return matches;
1963}
1964
1965
1966/*
1967 * mcv_combine_selectivities
1968 * Combine per-column and multi-column MCV selectivity estimates.
1969 *
1970 * simple_sel is a "simple" selectivity estimate (produced without using any
1971 * extended statistics, essentially assuming independence of columns/clauses).
1972 *
1973 * mcv_sel and mcv_basesel are sums of the frequencies and base frequencies of
1974 * all matching MCV items. The difference (mcv_sel - mcv_basesel) is then
1975 * essentially interpreted as a correction to be added to simple_sel, as
1976 * described below.
1977 *
1978 * mcv_totalsel is the sum of the frequencies of all MCV items (not just the
1979 * matching ones). This is used as an upper bound on the portion of the
1980 * selectivity estimates not covered by the MCV statistics.
1981 *
1982 * Note: While simple and base selectivities are defined in a quite similar
1983 * way, the values are computed differently and are not therefore equal. The
1984 * simple selectivity is computed as a product of per-clause estimates, while
1985 * the base selectivity is computed by adding up base frequencies of matching
1986 * items of the multi-column MCV list. So the values may differ for two main
1987 * reasons - (a) the MCV list may not cover 100% of the data and (b) some of
1988 * the MCV items did not match the estimated clauses.
1989 *
1990 * As both (a) and (b) reduce the base selectivity value, it generally holds
1991 * that (simple_sel >= mcv_basesel). If the MCV list covers all the data, the
1992 * values may be equal.
1993 *
1994 * So, other_sel = (simple_sel - mcv_basesel) is an estimate for the part not
1995 * covered by the MCV list, and (mcv_sel - mcv_basesel) may be seen as a
1996 * correction for the part covered by the MCV list. Those two statements are
1997 * actually equivalent.
1998 */
2004{
2007
2008 /* estimated selectivity of values not covered by MCV matches */
2011
2012 /* this non-MCV selectivity cannot exceed 1 - mcv_totalsel */
2013 if (other_sel > 1.0 - mcv_totalsel)
2014 other_sel = 1.0 - mcv_totalsel;
2015
2016 /* overall selectivity is the sum of the MCV and non-MCV parts */
2017 sel = mcv_sel + other_sel;
2019
2020 return sel;
2021}
2022
2023
2024/*
2025 * mcv_clauselist_selectivity
2026 * Use MCV statistics to estimate the selectivity of an implicitly-ANDed
2027 * list of clauses.
2028 *
2029 * This determines which MCV items match every clause in the list and returns
2030 * the sum of the frequencies of those items.
2031 *
2032 * In addition, it returns the sum of the base frequencies of each of those
2033 * items (that is the sum of the selectivities that each item would have if
2034 * the columns were independent of one another), and the total selectivity of
2035 * all the MCV items (not just the matching ones). These are expected to be
2036 * used together with a "simple" selectivity estimate (one based only on
2037 * per-column statistics) to produce an overall selectivity estimate that
2038 * makes use of both per-column and multi-column statistics --- see
2039 * mcv_combine_selectivities().
2040 */
2043 List *clauses, int varRelid,
2044 JoinType jointype, SpecialJoinInfo *sjinfo,
2045 RelOptInfo *rel,
2047{
2048 MCVList *mcv;
2049 Selectivity s = 0.0;
2050 RangeTblEntry *rte = root->simple_rte_array[rel->relid];
2051
2052 /* match/mismatch bitmap for each MCV item */
2053 bool *matches = NULL;
2054
2055 /* load the MCV list stored in the statistics object */
2056 mcv = statext_mcv_load(stat->statOid, rte->inh);
2057
2058 /* build a match bitmap for the clauses */
2059 matches = mcv_get_match_bitmap(root, clauses, stat->keys, stat->exprs,
2060 mcv, false);
2061
2062 /* sum frequencies for all the matching MCV items */
2063 *basesel = 0.0;
2064 *totalsel = 0.0;
2065 for (uint32 i = 0; i < mcv->nitems; i++)
2066 {
2067 *totalsel += mcv->items[i].frequency;
2068
2069 if (matches[i] != false)
2070 {
2071 *basesel += mcv->items[i].base_frequency;
2072 s += mcv->items[i].frequency;
2073 }
2074 }
2075
2076 return s;
2077}
2078
2079
2080/*
2081 * mcv_clause_selectivity_or
2082 * Use MCV statistics to estimate the selectivity of a clause that
2083 * appears in an ORed list of clauses.
2084 *
2085 * As with mcv_clauselist_selectivity() this determines which MCV items match
2086 * the clause and returns both the sum of the frequencies and the sum of the
2087 * base frequencies of those items, as well as the sum of the frequencies of
2088 * all MCV items (not just the matching ones) so that this information can be
2089 * used by mcv_combine_selectivities() to produce a selectivity estimate that
2090 * makes use of both per-column and multi-column statistics.
2091 *
2092 * Additionally, we return information to help compute the overall selectivity
2093 * of the ORed list of clauses assumed to contain this clause. This function
2094 * is intended to be called for each clause in the ORed list of clauses,
2095 * allowing the overall selectivity to be computed using the following
2096 * algorithm:
2097 *
2098 * Suppose P[n] = P(C[1] OR C[2] OR ... OR C[n]) is the combined selectivity
2099 * of the first n clauses in the list. Then the combined selectivity taking
2100 * into account the next clause C[n+1] can be written as
2101 *
2102 * P[n+1] = P[n] + P(C[n+1]) - P((C[1] OR ... OR C[n]) AND C[n+1])
2103 *
2104 * The final term above represents the overlap between the clauses examined so
2105 * far and the (n+1)'th clause. To estimate its selectivity, we track the
2106 * match bitmap for the ORed list of clauses examined so far and examine its
2107 * intersection with the match bitmap for the (n+1)'th clause.
2108 *
2109 * We then also return the sums of the MCV item frequencies and base
2110 * frequencies for the match bitmap intersection corresponding to the overlap
2111 * term above, so that they can be combined with a simple selectivity estimate
2112 * for that term.
2113 *
2114 * The parameter "or_matches" is an in/out parameter tracking the match bitmap
2115 * for the clauses examined so far. The caller is expected to set it to NULL
2116 * the first time it calls this function.
2117 */
2120 MCVList *mcv, Node *clause, bool **or_matches,
2123{
2124 Selectivity s = 0.0;
2125 bool *new_matches;
2126
2127 /* build the OR-matches bitmap, if not built already */
2128 if (*or_matches == NULL)
2129 *or_matches = palloc0_array(bool, mcv->nitems);
2130
2131 /* build the match bitmap for the new clause */
2133 stat->exprs, mcv, false);
2134
2135 /*
2136 * Sum the frequencies for all the MCV items matching this clause and also
2137 * those matching the overlap between this clause and any of the preceding
2138 * clauses as described above.
2139 */
2140 *basesel = 0.0;
2141 *overlap_mcvsel = 0.0;
2142 *overlap_basesel = 0.0;
2143 *totalsel = 0.0;
2144 for (uint32 i = 0; i < mcv->nitems; i++)
2145 {
2146 *totalsel += mcv->items[i].frequency;
2147
2148 if (new_matches[i])
2149 {
2150 s += mcv->items[i].frequency;
2151 *basesel += mcv->items[i].base_frequency;
2152
2153 if ((*or_matches)[i])
2154 {
2155 *overlap_mcvsel += mcv->items[i].frequency;
2156 *overlap_basesel += mcv->items[i].base_frequency;
2157 }
2158 }
2159
2160 /* update the OR-matches bitmap for the next clause */
2161 (*or_matches)[i] = (*or_matches)[i] || new_matches[i];
2162 }
2163
2165
2166 return s;
2167}
2168
2169/*
2170 * Free allocations of a MCVList.
2171 */
2172void
2174{
2175 for (uint32 i = 0; i < mcvlist->nitems; i++)
2176 {
2177 MCVItem *item = &mcvlist->items[i];
2178
2179 pfree(item->values);
2180 pfree(item->isnull);
2181 }
2182 pfree(mcvlist);
2183}
2184
2185/*
2186 * Create the MCV composite datum, which is a serialization of an array of
2187 * MCVItems.
2188 *
2189 * The inputs consist of four separate arrays of equal length "numitems"
2190 * (mcv_elems, mcv_nulls, freqs and base_freqs) that form the basics of
2191 * what is stored in the catalogs. These form an array of composite
2192 * records defined by the three atttypX arrays of equal length "numattrs".
2193 *
2194 * If any data element fails to convert to the input type specified for that
2195 * attribute, then function will return a NULL Datum if elevel < ERROR.
2196 */
2197Datum
2198statext_mcv_import(int elevel, int numattrs,
2199 Oid *atttypids, int32 *atttypmods, Oid *atttypcolls,
2200 int nitems, Datum *mcv_elems, bool *mcv_nulls,
2202{
2204 bytea *bytes;
2206
2207 /*
2208 * Allocate the MCV list structure, set the global parameters.
2209 */
2211 (sizeof(MCVItem) * nitems));
2212
2213 mcvlist->magic = STATS_MCV_MAGIC;
2215 mcvlist->ndimensions = numattrs;
2216 mcvlist->nitems = nitems;
2217
2218 /* Set the values for the 1-D arrays and allocate space for the 2-D arrays */
2219 for (int i = 0; i < nitems; i++)
2220 {
2221 MCVItem *item = &mcvlist->items[i];
2222
2223 item->frequency = freqs[i];
2224 item->base_frequency = base_freqs[i];
2225 item->values = (Datum *) palloc0_array(Datum, numattrs);
2226 item->isnull = (bool *) palloc0_array(bool, numattrs);
2227 }
2228
2229 /*
2230 * Walk through each dimension, determine the input function for that
2231 * type, and then attempt to convert all values in that column via that
2232 * function. We approach this column-wise because it is simpler to deal
2233 * with one input function at time, and possibly more cache-friendly.
2234 */
2235 for (int j = 0; j < numattrs; j++)
2236 {
2237 FmgrInfo finfo;
2238 Oid ioparam;
2239 Oid infunc;
2240 int index = j;
2241
2243 fmgr_info(infunc, &finfo);
2244
2245 /* store info about data type OIDs */
2246 mcvlist->types[j] = atttypids[j];
2247
2248 for (int i = 0; i < nitems; i++)
2249 {
2250 MCVItem *item = &mcvlist->items[i];
2251
2252 if (mcv_nulls[index])
2253 {
2254 /* NULL value detected, hence no input to process */
2255 item->values[j] = (Datum) 0;
2256 item->isnull[j] = true;
2257 }
2258 else
2259 {
2262
2263 if (!InputFunctionCallSafe(&finfo, s, ioparam, atttypmods[j],
2264 (Node *) &escontext, &item->values[j]))
2265 {
2266 ereport(elevel,
2268 errmsg("could not parse MCV element \"%s\": incorrect value", s)));
2269 pfree(s);
2270 goto error;
2271 }
2272
2273 pfree(s);
2274 }
2275
2276 index += numattrs;
2277 }
2278 }
2279
2280 /*
2281 * The function statext_mcv_serialize() requires an array of pointers to
2282 * VacAttrStats records, but only a few fields within those records have
2283 * to be filled out.
2284 */
2286
2287 for (int i = 0; i < numattrs; i++)
2288 {
2289 Oid typid = atttypids[i];
2291
2293
2295 elog(ERROR, "cache lookup failed for type %u", typid);
2296
2298
2299 vastats[i]->attrtype = (Form_pg_type) GETSTRUCT(typtuple);
2300 vastats[i]->attrtypid = typid;
2301 vastats[i]->attrcollid = atttypcolls[i];
2302 }
2303
2305
2306 for (int i = 0; i < numattrs; i++)
2307 {
2308 pfree(vastats[i]);
2309 }
2310 pfree((void *) vastats);
2311
2314
2315 if (bytes == NULL)
2316 {
2317 ereport(elevel,
2319 errmsg("could not import MCV list")));
2320 goto error;
2321 }
2322
2323 return PointerGetDatum(bytes);
2324
2325error:
2327 return (Datum) 0;
2328}
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:263
#define DatumGetArrayTypeP(X)
Definition array.h:261
#define ARR_ELEMTYPE(a)
Definition array.h:292
ArrayBuildState * accumArrayResult(ArrayBuildState *astate, Datum dvalue, bool disnull, Oid element_type, MemoryContext rcontext)
Datum makeArrayResult(ArrayBuildState *astate, MemoryContext rcontext)
void deconstruct_array(const ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
int16 AttrNumber
Definition attnum.h:21
int bms_num_members(const Bitmapset *a)
Definition bitmapset.c:879
int bms_member_index(Bitmapset *a, int x)
Definition bitmapset.c:674
static Datum values[MAXATTR]
Definition bootstrap.c:190
#define TextDatumGetCString(d)
Definition builtins.h:99
Datum byteaout(PG_FUNCTION_ARGS)
Definition bytea.c:275
Datum byteasend(PG_FUNCTION_ARGS)
Definition bytea.c:377
#define MAXALIGN(LEN)
Definition c.h:955
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:308
#define VARHDRSZ
Definition c.h:840
#define Assert(condition)
Definition c.h:1002
double float8
Definition c.h:773
int16_t int16
Definition c.h:678
int32_t int32
Definition c.h:679
uint16_t uint16
Definition c.h:682
uint32_t uint32
Definition c.h:683
#define PG_UINT16_MAX
Definition c.h:730
size_t Size
Definition c.h:748
uint32 result
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
Oid collid
Datum arg
Definition elog.c:1323
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
bool equal(const void *a, const void *b)
Definition equalfuncs.c:223
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
AttInMetadata * TupleDescGetAttInMetadata(TupleDesc tupdesc)
struct SortSupportData * SortSupport
Definition execnodes.h:61
int compare_scalars_simple(const void *a, const void *b, void *arg)
int compare_datums_simple(Datum a, Datum b, SortSupport ssup)
SortItem * build_sorted_items(StatsBuildData *data, int *nitems, MultiSortSupport mss, int numattrs, AttrNumber *attnums)
int multi_sort_compare(const void *a, const void *b, void *arg)
MultiSortSupport multi_sort_init(int ndims)
void multi_sort_add_dimension(MultiSortSupport mss, int sortdim, Oid oper, Oid collation)
bool examine_opclause_args(List *args, Node **exprp, Const **cstp, bool *expronleftp)
MultiSortSupportData * MultiSortSupport
#define palloc_array(type, count)
Definition fe_memutils.h:91
#define palloc0_array(type, count)
Definition fe_memutils.h:92
#define palloc0_object(type)
Definition fe_memutils.h:90
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition fmgr.c:1151
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition fmgr.c:129
bool InputFunctionCallSafe(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod, Node *escontext, Datum *result)
Definition fmgr.c:1586
#define PG_RETURN_VOID()
Definition fmgr.h:350
#define PG_DETOAST_DATUM(datum)
Definition fmgr.h:240
#define FunctionCall1(flinfo, arg1)
Definition fmgr.h:706
#define PG_GETARG_BYTEA_P(n)
Definition fmgr.h:336
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
#define DatumGetByteaP(X)
Definition fmgr.h:332
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition funcapi.c:276
#define SRF_IS_FIRSTCALL()
Definition funcapi.h:304
#define SRF_PERCALL_SETUP()
Definition funcapi.h:308
@ TYPEFUNC_COMPOSITE
Definition funcapi.h:149
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition funcapi.h:310
#define SRF_FIRSTCALL_INIT()
Definition funcapi.h:306
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
Definition funcapi.h:230
#define SRF_RETURN_DONE(_funcctx)
Definition funcapi.h:328
return str start
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1025
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
#define nitems(x)
Definition indent.h:31
long val
Definition informix.c:689
static struct @175 value
int b
Definition isn.c:74
int a
Definition isn.c:73
int j
Definition isn.c:78
int i
Definition isn.c:77
void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena)
Definition lsyscache.c:3223
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition lsyscache.c:2585
RegProcedure get_opcode(Oid opno)
Definition lsyscache.c:1585
void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam)
Definition lsyscache.c:3190
void statext_mcv_free(MCVList *mcvlist)
Definition mcv.c:2174
Datum pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS)
Definition mcv.c:1335
Datum pg_mcv_list_in(PG_FUNCTION_ARGS)
Definition mcv.c:1469
#define ITEM_SIZE(ndims)
Definition mcv.c:51
MCVList * statext_mcv_deserialize(bytea *data)
Definition mcv.c:993
Selectivity mcv_clauselist_selectivity(PlannerInfo *root, StatisticExtInfo *stat, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo, RelOptInfo *rel, Selectivity *basesel, Selectivity *totalsel)
Definition mcv.c:2043
static MultiSortSupport build_mss(StatsBuildData *data)
Definition mcv.c:345
static int compare_sort_item_count(const void *a, const void *b, void *arg)
Definition mcv.c:401
MCVList * statext_mcv_load(Oid mvoid, bool inh)
Definition mcv.c:556
Datum pg_mcv_list_out(PG_FUNCTION_ARGS)
Definition mcv.c:1495
static int count_distinct_groups(int numrows, SortItem *items, MultiSortSupport mss)
Definition mcv.c:377
Datum statext_mcv_import(int elevel, int numattrs, Oid *atttypids, int32 *atttypmods, Oid *atttypcolls, int nitems, Datum *mcv_elems, bool *mcv_nulls, float8 *freqs, float8 *base_freqs)
Definition mcv.c:2199
#define MinSizeOfMCVList
Definition mcv.c:57
Datum pg_mcv_list_send(PG_FUNCTION_ARGS)
Definition mcv.c:1520
#define SizeOfMCVList(ndims, nitems)
Definition mcv.c:66
static bool * mcv_get_match_bitmap(PlannerInfo *root, List *clauses, Bitmapset *keys, List *exprs, MCVList *mcvlist, bool is_or)
Definition mcv.c:1596
#define RESULT_MERGE(value, is_or, match)
Definition mcv.c:86
static double get_mincount_for_mcv_list(int samplerows, double totalrows)
Definition mcv.c:146
Selectivity mcv_combine_selectivities(Selectivity simple_sel, Selectivity mcv_sel, Selectivity mcv_basesel, Selectivity mcv_totalsel)
Definition mcv.c:2001
Selectivity mcv_clause_selectivity_or(PlannerInfo *root, StatisticExtInfo *stat, MCVList *mcv, Node *clause, bool **or_matches, Selectivity *basesel, Selectivity *overlap_mcvsel, Selectivity *overlap_basesel, Selectivity *totalsel)
Definition mcv.c:2120
#define RESULT_IS_FINAL(value, is_or)
Definition mcv.c:98
static int mcv_match_expression(Node *expr, Bitmapset *keys, List *exprs, Oid *collid)
Definition mcv.c:1532
MCVList * statext_mcv_build(StatsBuildData *data, double totalrows, int stattarget)
Definition mcv.c:178
Datum pg_mcv_list_recv(PG_FUNCTION_ARGS)
Definition mcv.c:1504
static int sort_item_compare(const void *a, const void *b, void *arg)
Definition mcv.c:463
static SortItem ** build_column_frequencies(SortItem *groups, int ngroups, MultiSortSupport mss, int *ncounts)
Definition mcv.c:488
bytea * statext_mcv_serialize(MCVList *mcvlist, VacAttrStats **stats)
Definition mcv.c:619
static SortItem * build_distinct_groups(int numrows, SortItem *items, MultiSortSupport mss, int *ndistinct)
Definition mcv.c:422
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1635
void pfree(void *pointer)
Definition mcxt.c:1619
void * palloc0(Size size)
Definition mcxt.c:1420
void * palloc(Size size)
Definition mcxt.c:1390
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
Oid exprCollation(const Node *expr)
Definition nodeFuncs.c:826
static bool is_andclause(const void *clause)
Definition nodeFuncs.h:107
static bool is_orclause(const void *clause)
Definition nodeFuncs.h:116
static bool is_opclause(const void *clause)
Definition nodeFuncs.h:76
static bool is_notclause(const void *clause)
Definition nodeFuncs.h:125
#define IsA(nodeptr, _type_)
Definition nodes.h:162
double Selectivity
Definition nodes.h:258
JoinType
Definition nodes.h:296
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
const void size_t len
const void * data
#define lfirst(lc)
Definition pg_list.h:172
static int list_length(const List *l)
Definition pg_list.h:152
#define NIL
Definition pg_list.h:68
#define list_make1(x1)
Definition pg_list.h:244
END_CATALOG_STRUCT typedef FormData_pg_type * Form_pg_type
Definition pg_type.h:265
void * bsearch_arg(const void *key, const void *base0, size_t nmemb, size_t size, int(*compar)(const void *, const void *, void *), void *arg)
Definition bsearch_arg.c:55
void qsort_interruptible(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg)
static bool DatumGetBool(Datum X)
Definition postgres.h:100
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
static char * DatumGetCString(Datum X)
Definition postgres.h:365
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
static Datum Float8GetDatum(float8 X)
Definition postgres.h:515
static Datum Int32GetDatum(int32 X)
Definition postgres.h:212
#define PointerGetDatum(X)
Definition postgres.h:354
#define InvalidOid
unsigned int Oid
static int fb(int x)
@ IS_NULL
Definition primnodes.h:1975
@ IS_NOT_NULL
Definition primnodes.h:1975
tree ctl root
Definition radixtree.h:1857
#define CLAMP_PROBABILITY(p)
Definition selfuncs.h:63
void PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup)
static int ApplySortComparator(Datum datum1, bool isNull1, Datum datum2, bool isNull2, SortSupport ssup)
static void error(void)
#define STATS_MCV_TYPE_BASIC
Definition statistics.h:67
#define STATS_MCV_MAGIC
Definition statistics.h:66
#define STATS_MCVLIST_MAX_ITEMS
Definition statistics.h:70
#define STATS_MAX_DIMENSIONS
Definition statistics.h:19
Definition pg_list.h:54
bool * isnull
Definition statistics.h:82
double frequency
Definition statistics.h:80
double base_frequency
Definition statistics.h:81
Datum * values
Definition statistics.h:83
SortSupportData ssup[FLEXIBLE_ARRAY_MEMBER]
Definition nodes.h:133
NullTestType nulltesttype
Definition primnodes.h:1982
Expr * arg
Definition primnodes.h:1981
Oid opno
Definition primnodes.h:835
List * args
Definition primnodes.h:853
MemoryContext ssup_cxt
Definition sortsupport.h:66
Form_pg_type attrtype
Definition vacuum.h:127
Oid attrcollid
Definition vacuum.h:128
AttrNumber varattno
Definition primnodes.h:275
Definition type.h:97
Definition c.h:835
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache2(SysCacheIdentifier cacheId, Datum key1, Datum key2)
Definition syscache.c:231
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:596
#define SearchSysCacheCopy1(cacheId, key1)
Definition syscache.h:91
static ItemArray items
static Datum fetch_att(const void *T, bool attbyval, int attlen)
Definition tupmacs.h:108
static void store_att_byval(void *T, Datum newdatum, int attlen)
Definition tupmacs.h:457
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition typcache.c:389
#define TYPECACHE_LT_OPR
Definition typcache.h:139
static Size VARSIZE_ANY(const void *PTR)
Definition varatt.h:460
static Size VARSIZE_ANY_EXHDR(const void *PTR)
Definition varatt.h:472
static char * VARDATA(const void *PTR)
Definition varatt.h:305
static char * VARDATA_ANY(const void *PTR)
Definition varatt.h:486
static void SET_VARSIZE(void *PTR, Size len)
Definition varatt.h:432
text * cstring_to_text(const char *s)
Definition varlena.c:184
const char * type

◆ MinSizeOfMCVList

#define MinSizeOfMCVList    (VARHDRSZ + sizeof(uint32) * 3 + sizeof(AttrNumber))

Definition at line 57 of file mcv.c.

◆ RESULT_IS_FINAL

#define RESULT_IS_FINAL (   value,
  is_or 
)    ((is_or) ? (value) : (!(value)))

Definition at line 98 of file mcv.c.

◆ RESULT_MERGE

#define RESULT_MERGE (   value,
  is_or,
  match 
)     ((is_or) ? ((value) || (match)) : ((value) && (match)))

Definition at line 86 of file mcv.c.

87 : ((value) && (match)))

◆ SizeOfMCVList

#define SizeOfMCVList (   ndims,
  nitems 
)
Value:
((MinSizeOfMCVList + sizeof(Oid) * (ndims)) + \
((ndims) * sizeof(DimensionInfo)) + \
((nitems) * ITEM_SIZE(ndims)))

Definition at line 66 of file mcv.c.

Function Documentation

◆ build_column_frequencies()

static SortItem ** build_column_frequencies ( SortItem groups,
int  ngroups,
MultiSortSupport  mss,
int ncounts 
)
static

Definition at line 488 of file mcv.c.

490{
491 int i,
492 dim;
494 char *ptr;
495
496 Assert(groups);
498
499 /* allocate arrays for all columns as a single chunk */
500 ptr = palloc(MAXALIGN(sizeof(SortItem *) * mss->ndims) +
501 mss->ndims * MAXALIGN(sizeof(SortItem) * ngroups));
502
503 /* initial array of pointers */
504 result = (SortItem **) ptr;
505 ptr += MAXALIGN(sizeof(SortItem *) * mss->ndims);
506
507 for (dim = 0; dim < mss->ndims; dim++)
508 {
509 SortSupport ssup = &mss->ssup[dim];
510
511 /* array of values for a single column */
512 result[dim] = (SortItem *) ptr;
513 ptr += MAXALIGN(sizeof(SortItem) * ngroups);
514
515 /* extract data for the dimension */
516 for (i = 0; i < ngroups; i++)
517 {
518 /* point into the input groups */
519 result[dim][i].values = &groups[i].values[dim];
520 result[dim][i].isnull = &groups[i].isnull[dim];
521 result[dim][i].count = groups[i].count;
522 }
523
524 /* sort the values, deduplicate */
526 sort_item_compare, ssup);
527
528 /*
529 * Identify distinct values, compute frequency (there might be
530 * multiple MCV items containing this value, so we need to sum counts
531 * from all of them.
532 */
533 ncounts[dim] = 1;
534 for (i = 1; i < ngroups; i++)
535 {
536 if (sort_item_compare(&result[dim][i - 1], &result[dim][i], ssup) == 0)
537 {
538 result[dim][ncounts[dim] - 1].count += result[dim][i].count;
539 continue;
540 }
541
542 result[dim][ncounts[dim]] = result[dim][i];
543
544 ncounts[dim]++;
545 }
546 }
547
548 return result;
549}

References Assert, fb(), i, MAXALIGN, palloc(), qsort_interruptible(), result, and sort_item_compare().

Referenced by statext_mcv_build().

◆ build_distinct_groups()

static SortItem * build_distinct_groups ( int  numrows,
SortItem items,
MultiSortSupport  mss,
int ndistinct 
)
static

Definition at line 422 of file mcv.c.

424{
425 int i,
426 j;
427 int ngroups = count_distinct_groups(numrows, items, mss);
428
429 SortItem *groups = (SortItem *) palloc(ngroups * sizeof(SortItem));
430
431 j = 0;
432 groups[0] = items[0];
433 groups[0].count = 1;
434
435 for (i = 1; i < numrows; i++)
436 {
437 /* Assume sorted in ascending order. */
438 Assert(multi_sort_compare(&items[i], &items[i - 1], mss) >= 0);
439
440 /* New distinct group detected. */
441 if (multi_sort_compare(&items[i], &items[i - 1], mss) != 0)
442 {
443 groups[++j] = items[i];
444 groups[j].count = 0;
445 }
446
447 groups[j].count++;
448 }
449
450 /* ensure we filled the expected number of distinct groups */
451 Assert(j + 1 == ngroups);
452
453 /* Sort the distinct groups by frequency (in descending order). */
456
457 *ndistinct = ngroups;
458 return groups;
459}

References Assert, compare_sort_item_count(), count_distinct_groups(), fb(), i, items, j, multi_sort_compare(), palloc(), and qsort_interruptible().

Referenced by statext_mcv_build().

◆ build_mss()

static MultiSortSupport build_mss ( StatsBuildData data)
static

Definition at line 345 of file mcv.c.

346{
347 int i;
348 int numattrs = data->nattnums;
349
350 /* Sort by multiple columns (using array of SortSupport) */
352
353 /* prepare the sort functions for all the attributes */
354 for (i = 0; i < numattrs; i++)
355 {
356 VacAttrStats *colstat = data->stats[i];
358
360 if (type->lt_opr == InvalidOid) /* shouldn't happen */
361 elog(ERROR, "cache lookup failed for ordering operator for type %u",
362 colstat->attrtypid);
363
364 multi_sort_add_dimension(mss, i, type->lt_opr, colstat->attrcollid);
365 }
366
367 return mss;
368}

References data, elog, ERROR, fb(), i, InvalidOid, lookup_type_cache(), multi_sort_add_dimension(), multi_sort_init(), type, and TYPECACHE_LT_OPR.

Referenced by statext_mcv_build().

◆ compare_sort_item_count()

static int compare_sort_item_count ( const void a,
const void b,
void arg 
)
static

Definition at line 401 of file mcv.c.

402{
403 const SortItem *ia = a;
404 const SortItem *ib = b;
405
406 if (ia->count == ib->count)
407 return 0;
408 else if (ia->count > ib->count)
409 return -1;
410
411 return 1;
412}

References a, b, and fb().

Referenced by build_distinct_groups().

◆ count_distinct_groups()

static int count_distinct_groups ( int  numrows,
SortItem items,
MultiSortSupport  mss 
)
static

Definition at line 377 of file mcv.c.

378{
379 int i;
380 int ndistinct;
381
382 ndistinct = 1;
383 for (i = 1; i < numrows; i++)
384 {
385 /* make sure the array really is sorted */
386 Assert(multi_sort_compare(&items[i], &items[i - 1], mss) >= 0);
387
388 if (multi_sort_compare(&items[i], &items[i - 1], mss) != 0)
389 ndistinct += 1;
390 }
391
392 return ndistinct;
393}

References Assert, fb(), i, items, and multi_sort_compare().

Referenced by build_distinct_groups().

◆ get_mincount_for_mcv_list()

static double get_mincount_for_mcv_list ( int  samplerows,
double  totalrows 
)
static

Definition at line 146 of file mcv.c.

147{
148 double n = samplerows;
149 double N = totalrows;
150 double numer,
151 denom;
152
153 numer = n * (N - n);
154 denom = N - n + 0.04 * n * (N - 1);
155
156 /* Guard against division by zero (possible if n = N = 1) */
157 if (denom == 0.0)
158 return 0.0;
159
160 return numer / denom;
161}

References fb().

Referenced by statext_mcv_build().

◆ mcv_clause_selectivity_or()

Selectivity mcv_clause_selectivity_or ( PlannerInfo root,
StatisticExtInfo stat,
MCVList mcv,
Node clause,
bool **  or_matches,
Selectivity basesel,
Selectivity overlap_mcvsel,
Selectivity overlap_basesel,
Selectivity totalsel 
)

Definition at line 2120 of file mcv.c.

2124{
2125 Selectivity s = 0.0;
2126 bool *new_matches;
2127
2128 /* build the OR-matches bitmap, if not built already */
2129 if (*or_matches == NULL)
2130 *or_matches = palloc0_array(bool, mcv->nitems);
2131
2132 /* build the match bitmap for the new clause */
2134 stat->exprs, mcv, false);
2135
2136 /*
2137 * Sum the frequencies for all the MCV items matching this clause and also
2138 * those matching the overlap between this clause and any of the preceding
2139 * clauses as described above.
2140 */
2141 *basesel = 0.0;
2142 *overlap_mcvsel = 0.0;
2143 *overlap_basesel = 0.0;
2144 *totalsel = 0.0;
2145 for (uint32 i = 0; i < mcv->nitems; i++)
2146 {
2147 *totalsel += mcv->items[i].frequency;
2148
2149 if (new_matches[i])
2150 {
2151 s += mcv->items[i].frequency;
2152 *basesel += mcv->items[i].base_frequency;
2153
2154 if ((*or_matches)[i])
2155 {
2156 *overlap_mcvsel += mcv->items[i].frequency;
2158 }
2159 }
2160
2161 /* update the OR-matches bitmap for the next clause */
2162 (*or_matches)[i] = (*or_matches)[i] || new_matches[i];
2163 }
2164
2166
2167 return s;
2168}
uint32 nitems
Definition statistics.h:91
MCVItem items[FLEXIBLE_ARRAY_MEMBER]
Definition statistics.h:94

References MCVItem::base_frequency, fb(), MCVItem::frequency, i, MCVList::items, list_make1, mcv_get_match_bitmap(), MCVList::nitems, palloc0_array, pfree(), and root.

Referenced by statext_mcv_clauselist_selectivity().

◆ mcv_clauselist_selectivity()

Selectivity mcv_clauselist_selectivity ( PlannerInfo root,
StatisticExtInfo stat,
List clauses,
int  varRelid,
JoinType  jointype,
SpecialJoinInfo sjinfo,
RelOptInfo rel,
Selectivity basesel,
Selectivity totalsel 
)

Definition at line 2043 of file mcv.c.

2048{
2049 MCVList *mcv;
2050 Selectivity s = 0.0;
2051 RangeTblEntry *rte = root->simple_rte_array[rel->relid];
2052
2053 /* match/mismatch bitmap for each MCV item */
2054 bool *matches = NULL;
2055
2056 /* load the MCV list stored in the statistics object */
2057 mcv = statext_mcv_load(stat->statOid, rte->inh);
2058
2059 /* build a match bitmap for the clauses */
2060 matches = mcv_get_match_bitmap(root, clauses, stat->keys, stat->exprs,
2061 mcv, false);
2062
2063 /* sum frequencies for all the matching MCV items */
2064 *basesel = 0.0;
2065 *totalsel = 0.0;
2066 for (uint32 i = 0; i < mcv->nitems; i++)
2067 {
2068 *totalsel += mcv->items[i].frequency;
2069
2070 if (matches[i] != false)
2071 {
2072 *basesel += mcv->items[i].base_frequency;
2073 s += mcv->items[i].frequency;
2074 }
2075 }
2076
2077 return s;
2078}
Index relid
Definition pathnodes.h:1069

References MCVItem::base_frequency, fb(), MCVItem::frequency, i, MCVList::items, mcv_get_match_bitmap(), MCVList::nitems, RelOptInfo::relid, root, and statext_mcv_load().

Referenced by statext_mcv_clauselist_selectivity().

◆ mcv_combine_selectivities()

Selectivity mcv_combine_selectivities ( Selectivity  simple_sel,
Selectivity  mcv_sel,
Selectivity  mcv_basesel,
Selectivity  mcv_totalsel 
)

Definition at line 2001 of file mcv.c.

2005{
2008
2009 /* estimated selectivity of values not covered by MCV matches */
2012
2013 /* this non-MCV selectivity cannot exceed 1 - mcv_totalsel */
2014 if (other_sel > 1.0 - mcv_totalsel)
2015 other_sel = 1.0 - mcv_totalsel;
2016
2017 /* overall selectivity is the sum of the MCV and non-MCV parts */
2018 sel = mcv_sel + other_sel;
2020
2021 return sel;
2022}

References CLAMP_PROBABILITY, and fb().

Referenced by statext_mcv_clauselist_selectivity().

◆ mcv_get_match_bitmap()

static bool * mcv_get_match_bitmap ( PlannerInfo root,
List clauses,
Bitmapset keys,
List exprs,
MCVList mcvlist,
bool  is_or 
)
static

Definition at line 1596 of file mcv.c.

1599{
1600 ListCell *l;
1601 bool *matches;
1602
1603 /* The bitmap may be partially built. */
1604 Assert(clauses != NIL);
1605 Assert(mcvlist != NULL);
1606 Assert(mcvlist->nitems > 0);
1608
1609 matches = palloc_array(bool, mcvlist->nitems);
1610 memset(matches, !is_or, sizeof(bool) * mcvlist->nitems);
1611
1612 /*
1613 * Loop through the list of clauses, and for each of them evaluate all the
1614 * MCV items not yet eliminated by the preceding clauses.
1615 */
1616 foreach(l, clauses)
1617 {
1618 Node *clause = (Node *) lfirst(l);
1619
1620 /* if it's a RestrictInfo, then extract the clause */
1621 if (IsA(clause, RestrictInfo))
1622 clause = (Node *) ((RestrictInfo *) clause)->clause;
1623
1624 /*
1625 * Handle the various types of clauses - OpClause, NullTest and
1626 * AND/OR/NOT
1627 */
1628 if (is_opclause(clause))
1629 {
1630 OpExpr *expr = (OpExpr *) clause;
1632
1633 /* valid only after examine_opclause_args returns true */
1635 Const *cst;
1636 bool expronleft;
1637 int idx;
1638 Oid collid;
1639
1640 fmgr_info(get_opcode(expr->opno), &opproc);
1641
1642 /* extract the var/expr and const from the expression */
1644 elog(ERROR, "incompatible clause");
1645
1646 /* match the attribute/expression to a dimension of the statistic */
1647 idx = mcv_match_expression(clause_expr, keys, exprs, &collid);
1648
1649 /*
1650 * Walk through the MCV items and evaluate the current clause. We
1651 * can skip items that were already ruled out, and terminate if
1652 * there are no remaining MCV items that might possibly match.
1653 */
1654 for (uint32 i = 0; i < mcvlist->nitems; i++)
1655 {
1656 bool match = true;
1657 MCVItem *item = &mcvlist->items[i];
1658
1659 Assert(idx >= 0);
1660
1661 /*
1662 * When the MCV item or the Const value is NULL we can treat
1663 * this as a mismatch. We must not call the operator because
1664 * of strictness.
1665 */
1666 if (item->isnull[idx] || cst->constisnull)
1667 {
1668 matches[i] = RESULT_MERGE(matches[i], is_or, false);
1669 continue;
1670 }
1671
1672 /*
1673 * Skip MCV items that can't change result in the bitmap. Once
1674 * the value gets false for AND-lists, or true for OR-lists,
1675 * we don't need to look at more clauses.
1676 */
1678 continue;
1679
1680 /*
1681 * First check whether the constant is below the lower
1682 * boundary (in that case we can skip the bucket, because
1683 * there's no overlap).
1684 *
1685 * We don't store collations used to build the statistics, but
1686 * we can use the collation for the attribute itself, as
1687 * stored in varcollid. We do reset the statistics after a
1688 * type change (including collation change), so this is OK.
1689 * For expressions, we use the collation extracted from the
1690 * expression itself.
1691 */
1692 if (expronleft)
1694 collid,
1695 item->values[idx],
1696 cst->constvalue));
1697 else
1699 collid,
1700 cst->constvalue,
1701 item->values[idx]));
1702
1703 /* update the match bitmap with the result */
1704 matches[i] = RESULT_MERGE(matches[i], is_or, match);
1705 }
1706 }
1707 else if (IsA(clause, ScalarArrayOpExpr))
1708 {
1709 ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) clause;
1711
1712 /* valid only after examine_opclause_args returns true */
1714 Const *cst;
1715 bool expronleft;
1716 Oid collid;
1717 int idx;
1718
1719 /* array evaluation */
1721 int16 elmlen;
1722 bool elmbyval;
1723 char elmalign;
1724 int num_elems;
1725 Datum *elem_values;
1726 bool *elem_nulls;
1727
1728 fmgr_info(get_opcode(expr->opno), &opproc);
1729
1730 /* extract the var/expr and const from the expression */
1732 elog(ERROR, "incompatible clause");
1733
1734 /* We expect Var on left */
1735 if (!expronleft)
1736 elog(ERROR, "incompatible clause");
1737
1738 /*
1739 * Deconstruct the array constant, unless it's NULL (we'll cover
1740 * that case below)
1741 */
1742 if (!cst->constisnull)
1743 {
1744 arrayval = DatumGetArrayTypeP(cst->constvalue);
1746 &elmlen, &elmbyval, &elmalign);
1749 elmlen, elmbyval, elmalign,
1750 &elem_values, &elem_nulls, &num_elems);
1751 }
1752
1753 /* match the attribute/expression to a dimension of the statistic */
1754 idx = mcv_match_expression(clause_expr, keys, exprs, &collid);
1755
1756 /*
1757 * Walk through the MCV items and evaluate the current clause. We
1758 * can skip items that were already ruled out, and terminate if
1759 * there are no remaining MCV items that might possibly match.
1760 */
1761 for (uint32 i = 0; i < mcvlist->nitems; i++)
1762 {
1763 int j;
1764 bool match = !expr->useOr;
1765 MCVItem *item = &mcvlist->items[i];
1766
1767 /*
1768 * When the MCV item or the Const value is NULL we can treat
1769 * this as a mismatch. We must not call the operator because
1770 * of strictness.
1771 */
1772 if (item->isnull[idx] || cst->constisnull)
1773 {
1774 matches[i] = RESULT_MERGE(matches[i], is_or, false);
1775 continue;
1776 }
1777
1778 /*
1779 * Skip MCV items that can't change result in the bitmap. Once
1780 * the value gets false for AND-lists, or true for OR-lists,
1781 * we don't need to look at more clauses.
1782 */
1784 continue;
1785
1786 for (j = 0; j < num_elems; j++)
1787 {
1788 Datum elem_value = elem_values[j];
1789 bool elem_isnull = elem_nulls[j];
1790 bool elem_match;
1791
1792 /* NULL values always evaluate as not matching. */
1793 if (elem_isnull)
1794 {
1795 match = RESULT_MERGE(match, expr->useOr, false);
1796 continue;
1797 }
1798
1799 /*
1800 * Stop evaluating the array elements once we reach a
1801 * matching value that can't change - ALL() is the same as
1802 * AND-list, ANY() is the same as OR-list.
1803 */
1804 if (RESULT_IS_FINAL(match, expr->useOr))
1805 break;
1806
1808 collid,
1809 item->values[idx],
1810 elem_value));
1811
1812 match = RESULT_MERGE(match, expr->useOr, elem_match);
1813 }
1814
1815 /* update the match bitmap with the result */
1816 matches[i] = RESULT_MERGE(matches[i], is_or, match);
1817 }
1818 }
1819 else if (IsA(clause, NullTest))
1820 {
1821 NullTest *expr = (NullTest *) clause;
1822 Node *clause_expr = (Node *) (expr->arg);
1823
1824 /* match the attribute/expression to a dimension of the statistic */
1825 int idx = mcv_match_expression(clause_expr, keys, exprs, NULL);
1826
1827 /*
1828 * Walk through the MCV items and evaluate the current clause. We
1829 * can skip items that were already ruled out, and terminate if
1830 * there are no remaining MCV items that might possibly match.
1831 */
1832 for (uint32 i = 0; i < mcvlist->nitems; i++)
1833 {
1834 bool match = false; /* assume mismatch */
1835 MCVItem *item = &mcvlist->items[i];
1836
1837 /* if the clause mismatches the MCV item, update the bitmap */
1838 switch (expr->nulltesttype)
1839 {
1840 case IS_NULL:
1841 match = (item->isnull[idx]) ? true : match;
1842 break;
1843
1844 case IS_NOT_NULL:
1845 match = (!item->isnull[idx]) ? true : match;
1846 break;
1847 }
1848
1849 /* now, update the match bitmap, depending on OR/AND type */
1850 matches[i] = RESULT_MERGE(matches[i], is_or, match);
1851 }
1852 }
1853 else if (is_orclause(clause) || is_andclause(clause))
1854 {
1855 /* AND/OR clause, with all subclauses being compatible */
1856
1857 BoolExpr *bool_clause = ((BoolExpr *) clause);
1858 List *bool_clauses = bool_clause->args;
1859
1860 /* match/mismatch bitmap for each MCV item */
1861 bool *bool_matches = NULL;
1862
1865
1866 /* build the match bitmap for the OR-clauses */
1868 mcvlist, is_orclause(clause));
1869
1870 /*
1871 * Merge the bitmap produced by mcv_get_match_bitmap into the
1872 * current one. We need to consider if we're evaluating AND or OR
1873 * condition when merging the results.
1874 */
1875 for (uint32 i = 0; i < mcvlist->nitems; i++)
1877
1879 }
1880 else if (is_notclause(clause))
1881 {
1882 /* NOT clause, with all subclauses compatible */
1883
1884 BoolExpr *not_clause = ((BoolExpr *) clause);
1885 List *not_args = not_clause->args;
1886
1887 /* match/mismatch bitmap for each MCV item */
1888 bool *not_matches = NULL;
1889
1890 Assert(not_args != NIL);
1892
1893 /* build the match bitmap for the NOT-clause */
1895 mcvlist, false);
1896
1897 /*
1898 * Merge the bitmap produced by mcv_get_match_bitmap into the
1899 * current one. We're handling a NOT clause, so invert the result
1900 * before merging it into the global bitmap.
1901 */
1902 for (uint32 i = 0; i < mcvlist->nitems; i++)
1904
1906 }
1907 else if (IsA(clause, Var))
1908 {
1909 /* Var (has to be a boolean Var, possibly from below NOT) */
1910
1911 Var *var = (Var *) (clause);
1912
1913 /* match the attribute to a dimension of the statistic */
1914 int idx = bms_member_index(keys, var->varattno);
1915
1916 Assert(var->vartype == BOOLOID);
1917
1918 /*
1919 * Walk through the MCV items and evaluate the current clause. We
1920 * can skip items that were already ruled out, and terminate if
1921 * there are no remaining MCV items that might possibly match.
1922 */
1923 for (uint32 i = 0; i < mcvlist->nitems; i++)
1924 {
1925 MCVItem *item = &mcvlist->items[i];
1926 bool match = false;
1927
1928 /* if the item is NULL, it's a mismatch */
1929 if (!item->isnull[idx] && DatumGetBool(item->values[idx]))
1930 match = true;
1931
1932 /* update the result bitmap */
1933 matches[i] = RESULT_MERGE(matches[i], is_or, match);
1934 }
1935 }
1936 else
1937 {
1938 /* Otherwise, it must be a bare boolean-returning expression */
1939 int idx;
1940
1941 /* match the expression to a dimension of the statistic */
1942 idx = mcv_match_expression(clause, keys, exprs, NULL);
1943
1944 /*
1945 * Walk through the MCV items and evaluate the current clause. We
1946 * can skip items that were already ruled out, and terminate if
1947 * there are no remaining MCV items that might possibly match.
1948 */
1949 for (uint32 i = 0; i < mcvlist->nitems; i++)
1950 {
1951 bool match;
1952 MCVItem *item = &mcvlist->items[i];
1953
1954 /* "match" just means it's bool TRUE */
1955 match = !item->isnull[idx] && DatumGetBool(item->values[idx]);
1956
1957 /* now, update the match bitmap, depending on OR/AND type */
1958 matches[i] = RESULT_MERGE(matches[i], is_or, match);
1959 }
1960 }
1961 }
1962
1963 return matches;
1964}

References NullTest::arg, OpExpr::args, ScalarArrayOpExpr::args, ARR_ELEMTYPE, Assert, bms_member_index(), collid, DatumGetArrayTypeP, DatumGetBool(), deconstruct_array(), elog, ERROR, examine_opclause_args(), fb(), fmgr_info(), FunctionCall2Coll(), get_opcode(), get_typlenbyvalalign(), i, idx(), is_andclause(), IS_NOT_NULL, is_notclause(), IS_NULL, is_opclause(), is_orclause(), IsA, MCVItem::isnull, j, lfirst, list_length(), mcv_get_match_bitmap(), mcv_match_expression(), NIL, NullTest::nulltesttype, OpExpr::opno, ScalarArrayOpExpr::opno, palloc_array, pfree(), RESULT_IS_FINAL, RESULT_MERGE, root, STATS_MCVLIST_MAX_ITEMS, ScalarArrayOpExpr::useOr, MCVItem::values, and Var::varattno.

Referenced by mcv_clause_selectivity_or(), mcv_clauselist_selectivity(), and mcv_get_match_bitmap().

◆ mcv_match_expression()

static int mcv_match_expression ( Node expr,
Bitmapset keys,
List exprs,
Oid collid 
)
static

Definition at line 1532 of file mcv.c.

1533{
1534 int idx;
1535
1536 if (IsA(expr, Var))
1537 {
1538 /* simple Var, so just lookup using varattno */
1539 Var *var = (Var *) expr;
1540
1541 if (collid)
1542 *collid = var->varcollid;
1543
1544 idx = bms_member_index(keys, var->varattno);
1545
1546 if (idx < 0)
1547 elog(ERROR, "variable not found in statistics object");
1548 }
1549 else
1550 {
1551 /* expression - lookup in stats expressions */
1552 ListCell *lc;
1553
1554 if (collid)
1555 *collid = exprCollation(expr);
1556
1557 /* expressions are stored after the simple columns */
1558 idx = bms_num_members(keys);
1559 foreach(lc, exprs)
1560 {
1561 Node *stat_expr = (Node *) lfirst(lc);
1562
1563 if (equal(expr, stat_expr))
1564 break;
1565
1566 idx++;
1567 }
1568
1569 if (lc == NULL)
1570 elog(ERROR, "expression not found in statistics object");
1571 }
1572
1573 return idx;
1574}

References bms_member_index(), bms_num_members(), collid, elog, equal(), ERROR, exprCollation(), fb(), idx(), IsA, lfirst, and Var::varattno.

Referenced by mcv_get_match_bitmap().

◆ pg_mcv_list_in()

Datum pg_mcv_list_in ( PG_FUNCTION_ARGS  )

Definition at line 1469 of file mcv.c.

1470{
1471 /*
1472 * pg_mcv_list stores the data in binary form and parsing text input is
1473 * not needed, so disallow this.
1474 */
1475 ereport(ERROR,
1477 errmsg("cannot accept a value of type %s", "pg_mcv_list")));
1478
1479 PG_RETURN_VOID(); /* keep compiler quiet */
1480}

References ereport, errcode(), errmsg, ERROR, fb(), and PG_RETURN_VOID.

◆ pg_mcv_list_out()

Datum pg_mcv_list_out ( PG_FUNCTION_ARGS  )

Definition at line 1495 of file mcv.c.

1496{
1497 return byteaout(fcinfo);
1498}

References byteaout().

◆ pg_mcv_list_recv()

Datum pg_mcv_list_recv ( PG_FUNCTION_ARGS  )

Definition at line 1504 of file mcv.c.

1505{
1506 ereport(ERROR,
1508 errmsg("cannot accept a value of type %s", "pg_mcv_list")));
1509
1510 PG_RETURN_VOID(); /* keep compiler quiet */
1511}

References ereport, errcode(), errmsg, ERROR, fb(), and PG_RETURN_VOID.

◆ pg_mcv_list_send()

Datum pg_mcv_list_send ( PG_FUNCTION_ARGS  )

Definition at line 1520 of file mcv.c.

1521{
1522 return byteasend(fcinfo);
1523}

References byteasend().

◆ pg_stats_ext_mcvlist_items()

Datum pg_stats_ext_mcvlist_items ( PG_FUNCTION_ARGS  )

Definition at line 1335 of file mcv.c.

1336{
1338
1339 /* stuff done only on the first call of the function */
1340 if (SRF_IS_FIRSTCALL())
1341 {
1342 MemoryContext oldcontext;
1344 TupleDesc tupdesc;
1345
1346 /* create a function context for cross-call persistence */
1348
1349 /* switch to memory context appropriate for multiple function calls */
1350 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
1351
1353
1354 funcctx->user_fctx = mcvlist;
1355
1356 /* total number of tuples to be returned */
1357 funcctx->max_calls = 0;
1358 if (funcctx->user_fctx != NULL)
1359 funcctx->max_calls = mcvlist->nitems;
1360
1361 /* Build a tuple descriptor for our result type */
1362 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1363 ereport(ERROR,
1365 errmsg("function returning record called in context "
1366 "that cannot accept type record")));
1367 tupdesc = BlessTupleDesc(tupdesc);
1368
1369 /*
1370 * generate attribute metadata needed later to produce tuples from raw
1371 * C strings
1372 */
1373 funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
1374
1375 MemoryContextSwitchTo(oldcontext);
1376 }
1377
1378 /* stuff done on every call of the function */
1380
1381 if (funcctx->call_cntr < funcctx->max_calls) /* do when there is more
1382 * left to send */
1383 {
1384 Datum values[5];
1385 bool nulls[5];
1386 HeapTuple tuple;
1387 Datum result;
1390
1391 int i;
1393 MCVItem *item;
1394
1395 mcvlist = (MCVList *) funcctx->user_fctx;
1396
1397 Assert(funcctx->call_cntr < mcvlist->nitems);
1398
1399 item = &mcvlist->items[funcctx->call_cntr];
1400
1401 for (i = 0; i < mcvlist->ndimensions; i++)
1402 {
1403
1405 BoolGetDatum(item->isnull[i]),
1406 false,
1407 BOOLOID,
1409
1410 if (!item->isnull[i])
1411 {
1412 bool isvarlena;
1413 Oid outfunc;
1415 Datum val;
1416 text *txt;
1417
1418 /* lookup output func for the type */
1421
1422 val = FunctionCall1(&fmgrinfo, item->values[i]);
1424
1427 false,
1428 TEXTOID,
1430 }
1431 else
1433 (Datum) 0,
1434 true,
1435 TEXTOID,
1437 }
1438
1439 values[0] = Int32GetDatum(funcctx->call_cntr);
1442 values[3] = Float8GetDatum(item->frequency);
1443 values[4] = Float8GetDatum(item->base_frequency);
1444
1445 /* no NULLs in the tuple */
1446 memset(nulls, 0, sizeof(nulls));
1447
1448 /* build a tuple */
1449 tuple = heap_form_tuple(funcctx->attinmeta->tupdesc, values, nulls);
1450
1451 /* make the tuple into a datum */
1452 result = HeapTupleGetDatum(tuple);
1453
1455 }
1456 else /* do when there is no more left */
1457 {
1459 }
1460}

References accumArrayResult(), Assert, MCVItem::base_frequency, BlessTupleDesc(), BoolGetDatum(), cstring_to_text(), CurrentMemoryContext, DatumGetPointer(), ereport, errcode(), errmsg, ERROR, fb(), Float8GetDatum(), fmgr_info(), MCVItem::frequency, FunctionCall1, get_call_result_type(), getTypeOutputInfo(), heap_form_tuple(), HeapTupleGetDatum(), i, Int32GetDatum(), MCVItem::isnull, makeArrayResult(), MemoryContextSwitchTo(), PG_GETARG_BYTEA_P, PointerGetDatum, result, SRF_FIRSTCALL_INIT, SRF_IS_FIRSTCALL, SRF_PERCALL_SETUP, SRF_RETURN_DONE, SRF_RETURN_NEXT, statext_mcv_deserialize(), TupleDescGetAttInMetadata(), TYPEFUNC_COMPOSITE, val, values, and MCVItem::values.

◆ sort_item_compare()

static int sort_item_compare ( const void a,
const void b,
void arg 
)
static

Definition at line 463 of file mcv.c.

464{
465 SortSupport ssup = (SortSupport) arg;
466 const SortItem *ia = a;
467 const SortItem *ib = b;
468
469 return ApplySortComparator(ia->values[0], ia->isnull[0],
470 ib->values[0], ib->isnull[0],
471 ssup);
472}

References a, ApplySortComparator(), arg, b, and fb().

Referenced by build_column_frequencies().

◆ statext_mcv_build()

MCVList * statext_mcv_build ( StatsBuildData data,
double  totalrows,
int  stattarget 
)

Definition at line 178 of file mcv.c.

179{
180 int i,
181 numattrs,
182 numrows,
183 ngroups,
184 nitems;
185 double mincount;
190
191 /* comparator for all the columns */
192 mss = build_mss(data);
193
194 /* sort the rows */
196 data->nattnums, data->attnums);
197
198 if (!items)
199 return NULL;
200
201 /* for convenience */
202 numattrs = data->nattnums;
203 numrows = data->numrows;
204
205 /* transform the sorted rows into groups (sorted by frequency) */
207
208 /*
209 * The maximum number of MCV items to store, based on the statistics
210 * target we computed for the statistics object (from the target set for
211 * the object itself, attributes and the system default). In any case, we
212 * can't keep more groups than we have available.
213 */
214 nitems = stattarget;
215 if (nitems > ngroups)
216 nitems = ngroups;
217
218 /*
219 * Decide how many items to keep in the MCV list. We can't use the same
220 * algorithm as per-column MCV lists, because that only considers the
221 * actual group frequency - but we're primarily interested in how the
222 * actual frequency differs from the base frequency (product of simple
223 * per-column frequencies, as if the columns were independent).
224 *
225 * Using the same algorithm might exclude items that are close to the
226 * "average" frequency of the sample. But that does not say whether the
227 * observed frequency is close to the base frequency or not. We also need
228 * to consider unexpectedly uncommon items (again, compared to the base
229 * frequency), and the single-column algorithm does not have to.
230 *
231 * We simply decide how many items to keep by computing the minimum count
232 * using get_mincount_for_mcv_list() and then keep all items that seem to
233 * be more common than that.
234 */
236
237 /*
238 * Walk the groups until we find the first group with a count below the
239 * mincount threshold (the index of that group is the number of groups we
240 * want to keep).
241 */
242 for (i = 0; i < nitems; i++)
243 {
244 if (groups[i].count < mincount)
245 {
246 nitems = i;
247 break;
248 }
249 }
250
251 /*
252 * At this point, we know the number of items for the MCV list. There
253 * might be none (for uniform distribution with many groups), and in that
254 * case, there will be no MCV list. Otherwise, construct the MCV list.
255 */
256 if (nitems > 0)
257 {
258 int j;
261
262 /* frequencies for values in each attribute */
263 SortItem **freqs;
264 int *nfreqs;
265
266 /* used to search values */
268 + sizeof(SortSupportData));
269
270 /* compute frequencies for values in each column */
273
274 /*
275 * Allocate the MCV list structure, set the global parameters.
276 */
278 sizeof(MCVItem) * nitems);
279
280 mcvlist->magic = STATS_MCV_MAGIC;
282 mcvlist->ndimensions = numattrs;
283 mcvlist->nitems = nitems;
284
285 /* store info about data type OIDs */
286 for (i = 0; i < numattrs; i++)
287 mcvlist->types[i] = data->stats[i]->attrtypid;
288
289 /* Copy the first chunk of groups into the result. */
290 for (i = 0; i < nitems; i++)
291 {
292 /* just point to the proper place in the list */
293 MCVItem *item = &mcvlist->items[i];
294
296 item->isnull = palloc_array(bool, numattrs);
297
298 /* copy values for the group */
299 memcpy(item->values, groups[i].values, sizeof(Datum) * numattrs);
300 memcpy(item->isnull, groups[i].isnull, sizeof(bool) * numattrs);
301
302 /* groups should be sorted by frequency in descending order */
303 Assert((i == 0) || (groups[i - 1].count >= groups[i].count));
304
305 /* group frequency */
306 item->frequency = (double) groups[i].count / numrows;
307
308 /* base frequency, if the attributes were independent */
309 item->base_frequency = 1.0;
310 for (j = 0; j < numattrs; j++)
311 {
312 SortItem *freq;
313
314 /* single dimension */
315 tmp->ndims = 1;
316 tmp->ssup[0] = mss->ssup[j];
317
318 /* fill search key */
319 key.values = &groups[i].values[j];
320 key.isnull = &groups[i].isnull[j];
321
322 freq = (SortItem *) bsearch_arg(&key, freqs[j], nfreqs[j],
323 sizeof(SortItem),
324 multi_sort_compare, tmp);
325
326 item->base_frequency *= ((double) freq->count) / numrows;
327 }
328 }
329
330 pfree(nfreqs);
331 pfree(freqs);
332 }
333
334 pfree(items);
335 pfree(groups);
336
337 return mcvlist;
338}

References Assert, MCVItem::base_frequency, bsearch_arg(), build_column_frequencies(), build_distinct_groups(), build_mss(), build_sorted_items(), data, fb(), MCVItem::frequency, get_mincount_for_mcv_list(), i, MCVItem::isnull, items, j, memcpy(), multi_sort_compare(), MultiSortSupportData::ndims, nitems, palloc(), palloc0(), palloc0_array, palloc_array, pfree(), MultiSortSupportData::ssup, STATS_MCV_MAGIC, STATS_MCV_TYPE_BASIC, and MCVItem::values.

Referenced by BuildRelationExtStatistics().

◆ statext_mcv_deserialize()

MCVList * statext_mcv_deserialize ( bytea data)

Definition at line 993 of file mcv.c.

994{
995 int dim,
996 i;
999 char *raw;
1000 char *ptr;
1001 char *endptr PG_USED_FOR_ASSERTS_ONLY;
1002
1003 int ndims,
1004 nitems;
1005 DimensionInfo *info = NULL;
1006
1007 /* local allocation buffer (used only for deserialization) */
1008 Datum **map = NULL;
1009
1010 /* MCV list */
1011 Size mcvlen;
1012
1013 /* buffer used for the result */
1014 Size datalen;
1015 char *dataptr;
1016 char *valuesptr;
1017 char *isnullptr;
1018
1019 if (data == NULL)
1020 return NULL;
1021
1022 /*
1023 * We can't possibly deserialize a MCV list if there's not even a complete
1024 * header. We need an explicit formula here, because we serialize the
1025 * header fields one by one, so we need to ignore struct alignment.
1026 */
1028 elog(ERROR, "invalid MCV size %zu (expected at least %zu)",
1030
1031 /* read the MCV list header */
1033
1034 /* pointer to the data part (skip the varlena header) */
1035 raw = (char *) data;
1036 ptr = VARDATA_ANY(raw);
1037 endptr = raw + VARSIZE_ANY(data);
1038
1039 /* get the header and perform further sanity checks */
1040 memcpy(&mcvlist->magic, ptr, sizeof(uint32));
1041 ptr += sizeof(uint32);
1042
1043 memcpy(&mcvlist->type, ptr, sizeof(uint32));
1044 ptr += sizeof(uint32);
1045
1046 memcpy(&mcvlist->nitems, ptr, sizeof(uint32));
1047 ptr += sizeof(uint32);
1048
1049 memcpy(&mcvlist->ndimensions, ptr, sizeof(AttrNumber));
1050 ptr += sizeof(AttrNumber);
1051
1052 if (mcvlist->magic != STATS_MCV_MAGIC)
1053 elog(ERROR, "invalid MCV magic %u (expected %u)",
1054 mcvlist->magic, STATS_MCV_MAGIC);
1055
1056 if (mcvlist->type != STATS_MCV_TYPE_BASIC)
1057 elog(ERROR, "invalid MCV type %u (expected %u)",
1059
1060 if (mcvlist->ndimensions == 0)
1061 elog(ERROR, "invalid zero-length dimension array in MCVList");
1062 else if ((mcvlist->ndimensions > STATS_MAX_DIMENSIONS) ||
1063 (mcvlist->ndimensions < 0))
1064 elog(ERROR, "invalid length (%d) dimension array in MCVList",
1065 mcvlist->ndimensions);
1066
1067 if (mcvlist->nitems == 0)
1068 elog(ERROR, "invalid zero-length item array in MCVList");
1069 else if (mcvlist->nitems > STATS_MCVLIST_MAX_ITEMS)
1070 elog(ERROR, "invalid length (%u) item array in MCVList",
1071 mcvlist->nitems);
1072
1073 nitems = mcvlist->nitems;
1074 ndims = mcvlist->ndimensions;
1075
1076 /*
1077 * Check amount of data including DimensionInfo for all dimensions and
1078 * also the serialized items (including uint16 indexes). Also, walk
1079 * through the dimension information and add it to the sum.
1080 */
1082
1083 /*
1084 * Check that we have at least the dimension and info records, along with
1085 * the items. We don't know the size of the serialized values yet. We need
1086 * to do this check first, before accessing the dimension info.
1087 */
1089 elog(ERROR, "invalid MCV size %zu (expected %zu)",
1091
1092 /* Now copy the array of type Oids. */
1093 memcpy(mcvlist->types, ptr, sizeof(Oid) * ndims);
1094 ptr += (sizeof(Oid) * ndims);
1095
1096 /* Now it's safe to access the dimension info. */
1097 info = palloc(ndims * sizeof(DimensionInfo));
1098
1099 memcpy(info, ptr, ndims * sizeof(DimensionInfo));
1100 ptr += (ndims * sizeof(DimensionInfo));
1101
1102 /* account for the value arrays */
1103 for (dim = 0; dim < ndims; dim++)
1104 {
1105 /*
1106 * XXX I wonder if we can/should rely on asserts here. Maybe those
1107 * checks should be done every time?
1108 */
1109 Assert(info[dim].nvalues >= 0);
1110 Assert(info[dim].nbytes >= 0);
1111
1112 expected_size += info[dim].nbytes;
1113 }
1114
1115 /*
1116 * Now we know the total expected MCV size, including all the pieces
1117 * (header, dimension info. items and deduplicated data). So do the final
1118 * check on size.
1119 */
1121 elog(ERROR, "invalid MCV size %zu (expected %zu)",
1123
1124 /*
1125 * We need an array of Datum values for each dimension, so that we can
1126 * easily translate the uint16 indexes later. We also need a top-level
1127 * array of pointers to those per-dimension arrays.
1128 *
1129 * While allocating the arrays for dimensions, compute how much space we
1130 * need for a copy of the by-ref data, as we can't simply point to the
1131 * original values (it might go away).
1132 */
1133 datalen = 0; /* space for by-ref data */
1134 map = palloc_array(Datum *, ndims);
1135
1136 for (dim = 0; dim < ndims; dim++)
1137 {
1138 map[dim] = palloc_array(Datum, info[dim].nvalues);
1139
1140 /* space needed for a copy of data for by-ref types */
1141 datalen += info[dim].nbytes_aligned;
1142 }
1143
1144 /*
1145 * Now resize the MCV list so that the allocation includes all the data.
1146 *
1147 * Allocate space for a copy of the data, as we can't simply reference the
1148 * serialized data - it's not aligned properly, and it may disappear while
1149 * we're still using the MCV list, e.g. due to catcache release.
1150 *
1151 * We do care about alignment here, because we will allocate all the
1152 * pieces at once, but then use pointers to different parts.
1153 */
1154 mcvlen = MAXALIGN(offsetof(MCVList, items) + (sizeof(MCVItem) * nitems));
1155
1156 /* arrays of values and isnull flags for all MCV items */
1157 mcvlen += nitems * MAXALIGN(sizeof(Datum) * ndims);
1158 mcvlen += nitems * MAXALIGN(sizeof(bool) * ndims);
1159
1160 /* we don't quite need to align this, but it makes some asserts easier */
1161 mcvlen += MAXALIGN(datalen);
1162
1163 /* now resize the deserialized MCV list, and compute pointers to parts */
1165
1166 /* pointer to the beginning of values/isnull arrays */
1167 valuesptr = (char *) mcvlist
1168 + MAXALIGN(offsetof(MCVList, items) + (sizeof(MCVItem) * nitems));
1169
1170 isnullptr = valuesptr + (nitems * MAXALIGN(sizeof(Datum) * ndims));
1171
1172 dataptr = isnullptr + (nitems * MAXALIGN(sizeof(bool) * ndims));
1173
1174 /*
1175 * Build mapping (index => value) for translating the serialized data into
1176 * the in-memory representation.
1177 */
1178 for (dim = 0; dim < ndims; dim++)
1179 {
1180 /* remember start position in the input array */
1181 char *start PG_USED_FOR_ASSERTS_ONLY = ptr;
1182
1183 if (info[dim].typbyval)
1184 {
1185 /* for by-val types we simply copy data into the mapping */
1186 for (i = 0; i < info[dim].nvalues; i++)
1187 {
1188 Datum v = 0;
1189
1190 memcpy(&v, ptr, info[dim].typlen);
1191 ptr += info[dim].typlen;
1192
1193 map[dim][i] = fetch_att(&v, true, info[dim].typlen);
1194
1195 /* no under/overflow of input array */
1196 Assert(ptr <= (start + info[dim].nbytes));
1197 }
1198 }
1199 else
1200 {
1201 /* for by-ref types we need to also make a copy of the data */
1202
1203 /* passed by reference, but fixed length (name, tid, ...) */
1204 if (info[dim].typlen > 0)
1205 {
1206 for (i = 0; i < info[dim].nvalues; i++)
1207 {
1208 memcpy(dataptr, ptr, info[dim].typlen);
1209 ptr += info[dim].typlen;
1210
1211 /* just point into the array */
1212 map[dim][i] = PointerGetDatum(dataptr);
1213 dataptr += MAXALIGN(info[dim].typlen);
1214 }
1215 }
1216 else if (info[dim].typlen == -1)
1217 {
1218 /* varlena */
1219 for (i = 0; i < info[dim].nvalues; i++)
1220 {
1221 uint32 len;
1222
1223 /* read the uint32 length */
1224 memcpy(&len, ptr, sizeof(uint32));
1225 ptr += sizeof(uint32);
1226
1227 /* the length is data-only */
1228 SET_VARSIZE(dataptr, len + VARHDRSZ);
1229 memcpy(VARDATA(dataptr), ptr, len);
1230 ptr += len;
1231
1232 /* just point into the array */
1233 map[dim][i] = PointerGetDatum(dataptr);
1234
1235 /* skip to place of the next deserialized value */
1236 dataptr += MAXALIGN(len + VARHDRSZ);
1237 }
1238 }
1239 else if (info[dim].typlen == -2)
1240 {
1241 /* cstring */
1242 for (i = 0; i < info[dim].nvalues; i++)
1243 {
1244 uint32 len;
1245
1246 memcpy(&len, ptr, sizeof(uint32));
1247 ptr += sizeof(uint32);
1248
1249 memcpy(dataptr, ptr, len);
1250 ptr += len;
1251
1252 /* just point into the array */
1253 map[dim][i] = PointerGetDatum(dataptr);
1254 dataptr += MAXALIGN(len);
1255 }
1256 }
1257
1258 /* no under/overflow of input array */
1259 Assert(ptr <= (start + info[dim].nbytes));
1260
1261 /* no overflow of the output mcv value */
1262 Assert(dataptr <= ((char *) mcvlist + mcvlen));
1263 }
1264
1265 /* check we consumed input data for this dimension exactly */
1266 Assert(ptr == (start + info[dim].nbytes));
1267 }
1268
1269 /* we should have also filled the MCV list exactly */
1270 Assert(dataptr == ((char *) mcvlist + mcvlen));
1271
1272 /* deserialize the MCV items and translate the indexes to Datums */
1273 for (i = 0; i < nitems; i++)
1274 {
1275 MCVItem *item = &mcvlist->items[i];
1276
1277 item->values = (Datum *) valuesptr;
1278 valuesptr += MAXALIGN(sizeof(Datum) * ndims);
1279
1280 item->isnull = (bool *) isnullptr;
1281 isnullptr += MAXALIGN(sizeof(bool) * ndims);
1282
1283 memcpy(item->isnull, ptr, sizeof(bool) * ndims);
1284 ptr += sizeof(bool) * ndims;
1285
1286 memcpy(&item->frequency, ptr, sizeof(double));
1287 ptr += sizeof(double);
1288
1289 memcpy(&item->base_frequency, ptr, sizeof(double));
1290 ptr += sizeof(double);
1291
1292 /* finally translate the indexes (for non-NULL only) */
1293 for (dim = 0; dim < ndims; dim++)
1294 {
1295 uint16 index;
1296
1297 memcpy(&index, ptr, sizeof(uint16));
1298 ptr += sizeof(uint16);
1299
1300 if (item->isnull[dim])
1301 continue;
1302
1303 item->values[dim] = map[dim][index];
1304 }
1305
1306 /* check we're not overflowing the input */
1307 Assert(ptr <= endptr);
1308 }
1309
1310 /* check that we processed all the data */
1311 Assert(ptr == endptr);
1312
1313 /* release the buffers used for mapping */
1314 for (dim = 0; dim < ndims; dim++)
1315 pfree(map[dim]);
1316
1317 pfree(map);
1318
1319 return mcvlist;
1320}

References Assert, MCVItem::base_frequency, data, elog, ERROR, fb(), fetch_att(), MCVItem::frequency, i, MCVItem::isnull, items, len, MAXALIGN, memcpy(), MinSizeOfMCVList, DimensionInfo::nbytes, DimensionInfo::nbytes_aligned, nitems, DimensionInfo::nvalues, palloc(), palloc0(), palloc_array, pfree(), PG_USED_FOR_ASSERTS_ONLY, PointerGetDatum, repalloc(), SET_VARSIZE(), SizeOfMCVList, start, STATS_MAX_DIMENSIONS, STATS_MCV_MAGIC, STATS_MCV_TYPE_BASIC, STATS_MCVLIST_MAX_ITEMS, DimensionInfo::typlen, MCVItem::values, VARDATA(), VARDATA_ANY(), VARHDRSZ, and VARSIZE_ANY().

Referenced by pg_stats_ext_mcvlist_items(), and statext_mcv_load().

◆ statext_mcv_free()

void statext_mcv_free ( MCVList mcvlist)

Definition at line 2174 of file mcv.c.

2175{
2176 for (uint32 i = 0; i < mcvlist->nitems; i++)
2177 {
2178 MCVItem *item = &mcvlist->items[i];
2179
2180 pfree(item->values);
2181 pfree(item->isnull);
2182 }
2183 pfree(mcvlist);
2184}

References fb(), i, MCVItem::isnull, pfree(), and MCVItem::values.

Referenced by statext_mcv_import().

◆ statext_mcv_import()

Datum statext_mcv_import ( int  elevel,
int  numattrs,
Oid atttypids,
int32 atttypmods,
Oid atttypcolls,
int  nitems,
Datum mcv_elems,
bool mcv_nulls,
float8 freqs,
float8 base_freqs 
)

Definition at line 2199 of file mcv.c.

2203{
2205 bytea *bytes;
2207
2208 /*
2209 * Allocate the MCV list structure, set the global parameters.
2210 */
2212 (sizeof(MCVItem) * nitems));
2213
2214 mcvlist->magic = STATS_MCV_MAGIC;
2216 mcvlist->ndimensions = numattrs;
2217 mcvlist->nitems = nitems;
2218
2219 /* Set the values for the 1-D arrays and allocate space for the 2-D arrays */
2220 for (int i = 0; i < nitems; i++)
2221 {
2222 MCVItem *item = &mcvlist->items[i];
2223
2224 item->frequency = freqs[i];
2225 item->base_frequency = base_freqs[i];
2226 item->values = (Datum *) palloc0_array(Datum, numattrs);
2227 item->isnull = (bool *) palloc0_array(bool, numattrs);
2228 }
2229
2230 /*
2231 * Walk through each dimension, determine the input function for that
2232 * type, and then attempt to convert all values in that column via that
2233 * function. We approach this column-wise because it is simpler to deal
2234 * with one input function at time, and possibly more cache-friendly.
2235 */
2236 for (int j = 0; j < numattrs; j++)
2237 {
2238 FmgrInfo finfo;
2239 Oid ioparam;
2240 Oid infunc;
2241 int index = j;
2242
2244 fmgr_info(infunc, &finfo);
2245
2246 /* store info about data type OIDs */
2247 mcvlist->types[j] = atttypids[j];
2248
2249 for (int i = 0; i < nitems; i++)
2250 {
2251 MCVItem *item = &mcvlist->items[i];
2252
2253 if (mcv_nulls[index])
2254 {
2255 /* NULL value detected, hence no input to process */
2256 item->values[j] = (Datum) 0;
2257 item->isnull[j] = true;
2258 }
2259 else
2260 {
2263
2264 if (!InputFunctionCallSafe(&finfo, s, ioparam, atttypmods[j],
2265 (Node *) &escontext, &item->values[j]))
2266 {
2267 ereport(elevel,
2269 errmsg("could not parse MCV element \"%s\": incorrect value", s)));
2270 pfree(s);
2271 goto error;
2272 }
2273
2274 pfree(s);
2275 }
2276
2277 index += numattrs;
2278 }
2279 }
2280
2281 /*
2282 * The function statext_mcv_serialize() requires an array of pointers to
2283 * VacAttrStats records, but only a few fields within those records have
2284 * to be filled out.
2285 */
2287
2288 for (int i = 0; i < numattrs; i++)
2289 {
2290 Oid typid = atttypids[i];
2292
2294
2296 elog(ERROR, "cache lookup failed for type %u", typid);
2297
2299
2300 vastats[i]->attrtype = (Form_pg_type) GETSTRUCT(typtuple);
2301 vastats[i]->attrtypid = typid;
2302 vastats[i]->attrcollid = atttypcolls[i];
2303 }
2304
2306
2307 for (int i = 0; i < numattrs; i++)
2308 {
2309 pfree(vastats[i]);
2310 }
2311 pfree((void *) vastats);
2312
2315
2316 if (bytes == NULL)
2317 {
2318 ereport(elevel,
2320 errmsg("could not import MCV list")));
2321 goto error;
2322 }
2323
2324 return PointerGetDatum(bytes);
2325
2326error:
2328 return (Datum) 0;
2329}

References MCVItem::base_frequency, elog, ereport, errcode(), errmsg, ERROR, error(), fb(), fmgr_info(), Form_pg_type, MCVItem::frequency, GETSTRUCT(), getTypeInputInfo(), HeapTupleIsValid, i, InputFunctionCallSafe(), MCVItem::isnull, items, j, nitems, ObjectIdGetDatum(), palloc0(), palloc0_array, palloc0_object, pfree(), PointerGetDatum, SearchSysCacheCopy1, statext_mcv_free(), statext_mcv_serialize(), STATS_MCV_MAGIC, STATS_MCV_TYPE_BASIC, TextDatumGetCString, and MCVItem::values.

Referenced by import_mcv().

◆ statext_mcv_load()

MCVList * statext_mcv_load ( Oid  mvoid,
bool  inh 
)

Definition at line 556 of file mcv.c.

557{
559 bool isnull;
563
564 if (!HeapTupleIsValid(htup))
565 elog(ERROR, "cache lookup failed for statistics object %u", mvoid);
566
569
570 if (isnull)
571 elog(ERROR,
572 "requested statistics kind \"%c\" is not yet built for statistics object %u",
574
576
577 ReleaseSysCache(htup);
578
579 return result;
580}

References BoolGetDatum(), DatumGetByteaP, elog, ERROR, fb(), HeapTupleIsValid, ObjectIdGetDatum(), ReleaseSysCache(), result, SearchSysCache2(), statext_mcv_deserialize(), and SysCacheGetAttr().

Referenced by mcv_clauselist_selectivity(), and statext_mcv_clauselist_selectivity().

◆ statext_mcv_serialize()

bytea * statext_mcv_serialize ( MCVList mcvlist,
VacAttrStats **  stats 
)

Definition at line 619 of file mcv.c.

620{
621 int dim;
622 int ndims = mcvlist->ndimensions;
623
624 SortSupport ssup;
625 DimensionInfo *info;
626
628
629 /* serialized items (indexes into arrays, etc.) */
630 bytea *raw;
631 char *ptr;
632 char *endptr PG_USED_FOR_ASSERTS_ONLY;
633
634 /* values per dimension (and number of non-NULL values) */
635 Datum **values = palloc0_array(Datum *, ndims);
636 int *counts = palloc0_array(int, ndims);
637
638 /*
639 * We'll include some rudimentary information about the attribute types
640 * (length, by-val flag), so that we don't have to look them up while
641 * deserializing the MCV list (we already have the type OID in the
642 * header). This is safe because when changing the type of the attribute
643 * the statistics gets dropped automatically. We need to store the info
644 * about the arrays of deduplicated values anyway.
645 */
646 info = palloc0_array(DimensionInfo, ndims);
647
648 /* sort support data for all attributes included in the MCV list */
649 ssup = palloc0_array(SortSupportData, ndims);
650
651 /* collect and deduplicate values for each dimension (attribute) */
652 for (dim = 0; dim < ndims; dim++)
653 {
654 int ndistinct;
655 TypeCacheEntry *typentry;
656
657 /*
658 * Lookup the LT operator (can't get it from stats extra_data, as we
659 * don't know how to interpret that - scalar vs. array etc.).
660 */
661 typentry = lookup_type_cache(stats[dim]->attrtypid, TYPECACHE_LT_OPR);
662
663 /* copy important info about the data type (length, by-value) */
664 info[dim].typlen = stats[dim]->attrtype->typlen;
665 info[dim].typbyval = stats[dim]->attrtype->typbyval;
666
667 /* allocate space for values in the attribute and collect them */
668 values[dim] = palloc0_array(Datum, mcvlist->nitems);
669
670 for (uint32 i = 0; i < mcvlist->nitems; i++)
671 {
672 /* skip NULL values - we don't need to deduplicate those */
673 if (mcvlist->items[i].isnull[dim])
674 continue;
675
676 /* append the value at the end */
677 values[dim][counts[dim]] = mcvlist->items[i].values[dim];
678 counts[dim] += 1;
679 }
680
681 /* if there are just NULL values in this dimension, we're done */
682 if (counts[dim] == 0)
683 continue;
684
685 /* sort and deduplicate the data */
686 ssup[dim].ssup_cxt = CurrentMemoryContext;
687 ssup[dim].ssup_collation = stats[dim]->attrcollid;
688 ssup[dim].ssup_nulls_first = false;
689
690 PrepareSortSupportFromOrderingOp(typentry->lt_opr, &ssup[dim]);
691
692 qsort_interruptible(values[dim], counts[dim], sizeof(Datum),
693 compare_scalars_simple, &ssup[dim]);
694
695 /*
696 * Walk through the array and eliminate duplicate values, but keep the
697 * ordering (so that we can do a binary search later). We know there's
698 * at least one item as (counts[dim] != 0), so we can skip the first
699 * element.
700 */
701 ndistinct = 1; /* number of distinct values */
702 for (int i = 1; i < counts[dim]; i++)
703 {
704 /* expect sorted array */
705 Assert(compare_datums_simple(values[dim][i - 1], values[dim][i], &ssup[dim]) <= 0);
706
707 /* if the value is the same as the previous one, we can skip it */
708 if (!compare_datums_simple(values[dim][i - 1], values[dim][i], &ssup[dim]))
709 continue;
710
711 values[dim][ndistinct] = values[dim][i];
712 ndistinct += 1;
713 }
714
715 /* we must not exceed PG_UINT16_MAX, as we use uint16 indexes */
716 Assert(ndistinct <= PG_UINT16_MAX);
717
718 /*
719 * Store additional info about the attribute - number of deduplicated
720 * values, and also size of the serialized data. For fixed-length data
721 * types this is trivial to compute, for varwidth types we need to
722 * actually walk the array and sum the sizes.
723 */
724 info[dim].nvalues = ndistinct;
725
726 if (info[dim].typbyval) /* by-value data types */
727 {
728 info[dim].nbytes = info[dim].nvalues * info[dim].typlen;
729
730 /*
731 * We copy the data into the MCV item during deserialization, so
732 * we don't need to allocate any extra space.
733 */
734 info[dim].nbytes_aligned = 0;
735 }
736 else if (info[dim].typlen > 0) /* fixed-length by-ref */
737 {
738 /*
739 * We don't care about alignment in the serialized data, so we
740 * pack the data as much as possible. But we also track how much
741 * data will be needed after deserialization, and in that case we
742 * need to account for alignment of each item.
743 *
744 * Note: As the items are fixed-length, we could easily compute
745 * this during deserialization, but we do it here anyway.
746 */
747 info[dim].nbytes = info[dim].nvalues * info[dim].typlen;
748 info[dim].nbytes_aligned = info[dim].nvalues * MAXALIGN(info[dim].typlen);
749 }
750 else if (info[dim].typlen == -1) /* varlena */
751 {
752 info[dim].nbytes = 0;
753 info[dim].nbytes_aligned = 0;
754 for (int i = 0; i < info[dim].nvalues; i++)
755 {
756 Size len;
757
758 /*
759 * For varlena values, we detoast the values and store the
760 * length and data separately. We don't bother with alignment
761 * here, which means that during deserialization we need to
762 * copy the fields and only access the copies.
763 */
765
766 /* serialized length (uint32 length + data) */
768 info[dim].nbytes += sizeof(uint32); /* length */
769 info[dim].nbytes += len; /* value (no header) */
770
771 /*
772 * During deserialization we'll build regular varlena values
773 * with full headers, and we need to align them properly.
774 */
775 info[dim].nbytes_aligned += MAXALIGN(VARHDRSZ + len);
776 }
777 }
778 else if (info[dim].typlen == -2) /* cstring */
779 {
780 info[dim].nbytes = 0;
781 info[dim].nbytes_aligned = 0;
782 for (int i = 0; i < info[dim].nvalues; i++)
783 {
784 Size len;
785
786 /*
787 * cstring is handled similar to varlena - first we store the
788 * length as uint32 and then the data. We don't care about
789 * alignment, which means that during deserialization we need
790 * to copy the fields and only access the copies.
791 */
792
793 /* c-strings include terminator, so +1 byte */
794 len = strlen(DatumGetCString(values[dim][i])) + 1;
795 info[dim].nbytes += sizeof(uint32); /* length */
796 info[dim].nbytes += len; /* value */
797
798 /* space needed for properly aligned deserialized copies */
799 info[dim].nbytes_aligned += MAXALIGN(len);
800 }
801 }
802
803 /* we know (count>0) so there must be some data */
804 Assert(info[dim].nbytes > 0);
805 }
806
807 /*
808 * Now we can finally compute how much space we'll actually need for the
809 * whole serialized MCV list (varlena header, MCV header, dimension info
810 * for each attribute, deduplicated values and items).
811 */
812 total_length = (3 * sizeof(uint32)) /* magic + type + nitems */
813 + sizeof(AttrNumber) /* ndimensions */
814 + (ndims * sizeof(Oid)); /* attribute types */
815
816 /* dimension info */
817 total_length += ndims * sizeof(DimensionInfo);
818
819 /* add space for the arrays of deduplicated values */
820 for (int i = 0; i < ndims; i++)
821 total_length += info[i].nbytes;
822
823 /*
824 * And finally account for the items (those are fixed-length, thanks to
825 * replacing values with uint16 indexes into the deduplicated arrays).
826 */
827 total_length += mcvlist->nitems * ITEM_SIZE(dim);
828
829 /*
830 * Allocate space for the whole serialized MCV list (we'll skip bytes, so
831 * we set them to zero to make the result more compressible).
832 */
835
836 ptr = VARDATA(raw);
837 endptr = ptr + total_length;
838
839 /* copy the MCV list header fields, one by one */
840 memcpy(ptr, &mcvlist->magic, sizeof(uint32));
841 ptr += sizeof(uint32);
842
843 memcpy(ptr, &mcvlist->type, sizeof(uint32));
844 ptr += sizeof(uint32);
845
846 memcpy(ptr, &mcvlist->nitems, sizeof(uint32));
847 ptr += sizeof(uint32);
848
849 memcpy(ptr, &mcvlist->ndimensions, sizeof(AttrNumber));
850 ptr += sizeof(AttrNumber);
851
852 memcpy(ptr, mcvlist->types, sizeof(Oid) * ndims);
853 ptr += (sizeof(Oid) * ndims);
854
855 /* store information about the attributes (data amounts, ...) */
856 memcpy(ptr, info, sizeof(DimensionInfo) * ndims);
857 ptr += sizeof(DimensionInfo) * ndims;
858
859 /* Copy the deduplicated values for all attributes to the output. */
860 for (dim = 0; dim < ndims; dim++)
861 {
862 /* remember the starting point for Asserts later */
864
865 for (int i = 0; i < info[dim].nvalues; i++)
866 {
867 Datum value = values[dim][i];
868
869 if (info[dim].typbyval) /* passed by value */
870 {
871 Datum tmp;
872
873 /*
874 * For byval types, we need to copy just the significant bytes
875 * - we can't use memcpy directly, as that assumes
876 * little-endian behavior. store_att_byval does almost what
877 * we need, but it requires a properly aligned buffer - the
878 * output buffer does not guarantee that. So we simply use a
879 * local Datum variable (which guarantees proper alignment),
880 * and then copy the value from it.
881 */
882 store_att_byval(&tmp, value, info[dim].typlen);
883
884 memcpy(ptr, &tmp, info[dim].typlen);
885 ptr += info[dim].typlen;
886 }
887 else if (info[dim].typlen > 0) /* passed by reference */
888 {
889 /* no special alignment needed, treated as char array */
890 memcpy(ptr, DatumGetPointer(value), info[dim].typlen);
891 ptr += info[dim].typlen;
892 }
893 else if (info[dim].typlen == -1) /* varlena */
894 {
896
897 /* copy the length */
898 memcpy(ptr, &len, sizeof(uint32));
899 ptr += sizeof(uint32);
900
901 /* data from the varlena value (without the header) */
903 ptr += len;
904 }
905 else if (info[dim].typlen == -2) /* cstring */
906 {
908
909 /* copy the length */
910 memcpy(ptr, &len, sizeof(uint32));
911 ptr += sizeof(uint32);
912
913 /* value */
915 ptr += len;
916 }
917
918 /* no underflows or overflows */
919 Assert((ptr > start) && ((ptr - start) <= info[dim].nbytes));
920 }
921
922 /* we should get exactly nbytes of data for this dimension */
923 Assert((ptr - start) == info[dim].nbytes);
924 }
925
926 /* Serialize the items, with uint16 indexes instead of the values. */
927 for (uint32 i = 0; i < mcvlist->nitems; i++)
928 {
929 MCVItem *mcvitem = &mcvlist->items[i];
930
931 /* don't write beyond the allocated space */
932 Assert(ptr <= (endptr - ITEM_SIZE(dim)));
933
934 /* copy NULL and frequency flags into the serialized MCV */
935 memcpy(ptr, mcvitem->isnull, sizeof(bool) * ndims);
936 ptr += sizeof(bool) * ndims;
937
938 memcpy(ptr, &mcvitem->frequency, sizeof(double));
939 ptr += sizeof(double);
940
941 memcpy(ptr, &mcvitem->base_frequency, sizeof(double));
942 ptr += sizeof(double);
943
944 /* store the indexes last */
945 for (dim = 0; dim < ndims; dim++)
946 {
947 uint16 index = 0;
948 Datum *value;
949
950 /* do the lookup only for non-NULL values */
951 if (!mcvitem->isnull[dim])
952 {
953 value = (Datum *) bsearch_arg(&mcvitem->values[dim], values[dim],
954 info[dim].nvalues, sizeof(Datum),
955 compare_scalars_simple, &ssup[dim]);
956
957 Assert(value != NULL); /* serialization or deduplication
958 * error */
959
960 /* compute index within the deduplicated array */
961 index = (uint16) (value - values[dim]);
962
963 /* check the index is within expected bounds */
964 Assert(index < info[dim].nvalues);
965 }
966
967 /* copy the index into the serialized MCV */
968 memcpy(ptr, &index, sizeof(uint16));
969 ptr += sizeof(uint16);
970 }
971
972 /* make sure we don't overflow the allocated value */
973 Assert(ptr <= endptr);
974 }
975
976 /* at this point we expect to match the total_length exactly */
977 Assert(ptr == endptr);
978
979 pfree(values);
980 pfree(counts);
981
982 return raw;
983}

References Assert, VacAttrStats::attrcollid, VacAttrStats::attrtype, bsearch_arg(), compare_datums_simple(), compare_scalars_simple(), CurrentMemoryContext, DatumGetCString(), DatumGetPointer(), fb(), i, ITEM_SIZE, len, lookup_type_cache(), TypeCacheEntry::lt_opr, MAXALIGN, memcpy(), DimensionInfo::nbytes, DimensionInfo::nbytes_aligned, DimensionInfo::nvalues, palloc0(), palloc0_array, pfree(), PG_DETOAST_DATUM, PG_UINT16_MAX, PG_USED_FOR_ASSERTS_ONLY, PointerGetDatum, PrepareSortSupportFromOrderingOp(), qsort_interruptible(), SET_VARSIZE(), SortSupportData::ssup_collation, SortSupportData::ssup_cxt, SortSupportData::ssup_nulls_first, start, store_att_byval(), DimensionInfo::typbyval, TYPECACHE_LT_OPR, DimensionInfo::typlen, value, values, VARDATA(), VARDATA_ANY(), VARHDRSZ, and VARSIZE_ANY_EXHDR().

Referenced by statext_mcv_import(), and statext_store().