Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * mvdistinct.c
4 : : * POSTGRES multivariate ndistinct coefficients
5 : : *
6 : : * Estimating number of groups in a combination of columns (e.g. for GROUP BY)
7 : : * is tricky, and the estimation error is often significant.
8 : :
9 : : * The multivariate ndistinct coefficients address this by storing ndistinct
10 : : * estimates for combinations of the user-specified columns. So for example
11 : : * given a statistics object on three columns (a,b,c), this module estimates
12 : : * and stores n-distinct for (a,b), (a,c), (b,c) and (a,b,c). The per-column
13 : : * estimates are already available in pg_statistic.
14 : : *
15 : : *
16 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
17 : : * Portions Copyright (c) 1994, Regents of the University of California
18 : : *
19 : : * IDENTIFICATION
20 : : * src/backend/statistics/mvdistinct.c
21 : : *
22 : : *-------------------------------------------------------------------------
23 : : */
24 : : #include "postgres.h"
25 : :
26 : : #include <math.h>
27 : :
28 : : #include "catalog/pg_statistic_ext.h"
29 : : #include "catalog/pg_statistic_ext_data.h"
30 : : #include "statistics/extended_stats_internal.h"
31 : : #include "utils/syscache.h"
32 : : #include "utils/typcache.h"
33 : : #include "varatt.h"
34 : :
35 : : static double ndistinct_for_combination(double totalrows, StatsBuildData *data,
36 : : int k, int *combination);
37 : : static double estimate_ndistinct(double totalrows, int numrows, int d, int f1);
38 : : static int n_choose_k(int n, int k);
39 : : static int num_combinations(int n);
40 : :
41 : : /* size of the struct header fields (magic, type, nitems) */
42 : : #define SizeOfHeader (3 * sizeof(uint32))
43 : :
44 : : /* size of a serialized ndistinct item (coefficient, natts, atts) */
45 : : #define SizeOfItem(natts) \
46 : : (sizeof(double) + sizeof(int) + (natts) * sizeof(AttrNumber))
47 : :
48 : : /* minimal size of a ndistinct item (with two attributes) */
49 : : #define MinSizeOfItem SizeOfItem(2)
50 : :
51 : : /* minimal size of mvndistinct, when all items are minimal */
52 : : #define MinSizeOfItems(nitems) \
53 : : (SizeOfHeader + (nitems) * MinSizeOfItem)
54 : :
55 : : /* Combination generator API */
56 : :
57 : : /* internal state for generator of k-combinations of n elements */
58 : : typedef struct CombinationGenerator
59 : : {
60 : : int k; /* size of the combination */
61 : : int n; /* total number of elements */
62 : : int current; /* index of the next combination to return */
63 : : int ncombinations; /* number of combinations (size of array) */
64 : : int *combinations; /* array of pre-built combinations */
65 : : } CombinationGenerator;
66 : :
67 : : static CombinationGenerator *generator_init(int n, int k);
68 : : static void generator_free(CombinationGenerator *state);
69 : : static int *generator_next(CombinationGenerator *state);
70 : : static void generate_combinations(CombinationGenerator *state);
71 : :
72 : :
73 : : /*
74 : : * statext_ndistinct_build
75 : : * Compute ndistinct coefficient for the combination of attributes.
76 : : *
77 : : * This computes the ndistinct estimate using the same estimator used
78 : : * in analyze.c and then computes the coefficient.
79 : : *
80 : : * To handle expressions easily, we treat them as system attributes with
81 : : * negative attnums, and offset everything by number of expressions to
82 : : * allow using Bitmapsets.
83 : : */
84 : : MVNDistinct *
85 : 189 : statext_ndistinct_build(double totalrows, StatsBuildData *data)
86 : : {
87 : : MVNDistinct *result;
88 : : int k;
89 : : uint32 itemcnt;
90 : 189 : int numattrs = data->nattnums;
91 : 189 : int numcombs = num_combinations(numattrs);
92 : :
93 : 189 : result = palloc(offsetof(MVNDistinct, items) +
94 : 189 : numcombs * sizeof(MVNDistinctItem));
95 : 189 : result->magic = STATS_NDISTINCT_MAGIC;
96 : 189 : result->type = STATS_NDISTINCT_TYPE_BASIC;
97 : 189 : result->nitems = numcombs;
98 : :
99 : 189 : itemcnt = 0;
100 [ + + ]: 462 : for (k = 2; k <= numattrs; k++)
101 : : {
102 : : int *combination;
103 : : CombinationGenerator *generator;
104 : :
105 : : /* generate combinations of K out of N elements */
106 : 273 : generator = generator_init(numattrs, k);
107 : :
108 [ + + ]: 810 : while ((combination = generator_next(generator)))
109 : : {
110 : 537 : MVNDistinctItem *item = &result->items[itemcnt];
111 : : int j;
112 : :
113 : 537 : item->attributes = palloc_array(AttrNumber, k);
114 : 537 : item->nattributes = k;
115 : :
116 : : /* translate the indexes to attnums */
117 [ + + ]: 1791 : for (j = 0; j < k; j++)
118 : : {
119 : 1254 : item->attributes[j] = data->attnums[combination[j]];
120 : :
121 : : Assert(AttributeNumberIsValid(item->attributes[j]));
122 : : }
123 : :
124 : 537 : item->ndistinct =
125 : 537 : ndistinct_for_combination(totalrows, data, k, combination);
126 : :
127 : 537 : itemcnt++;
128 : : Assert(itemcnt <= result->nitems);
129 : : }
130 : :
131 : 273 : generator_free(generator);
132 : : }
133 : :
134 : : /* must consume exactly the whole output array */
135 : : Assert(itemcnt == result->nitems);
136 : :
137 : 189 : return result;
138 : : }
139 : :
140 : : /*
141 : : * statext_ndistinct_load
142 : : * Load the ndistinct value for the indicated pg_statistic_ext tuple
143 : : */
144 : : MVNDistinct *
145 : 355 : statext_ndistinct_load(Oid mvoid, bool inh)
146 : : {
147 : : MVNDistinct *result;
148 : : bool isnull;
149 : : Datum ndist;
150 : : HeapTuple htup;
151 : :
152 : 355 : htup = SearchSysCache2(STATEXTDATASTXOID,
153 : : ObjectIdGetDatum(mvoid), BoolGetDatum(inh));
154 [ - + ]: 355 : if (!HeapTupleIsValid(htup))
155 [ # # ]: 0 : elog(ERROR, "cache lookup failed for statistics object %u", mvoid);
156 : :
157 : 355 : ndist = SysCacheGetAttr(STATEXTDATASTXOID, htup,
158 : : Anum_pg_statistic_ext_data_stxdndistinct, &isnull);
159 [ - + ]: 355 : if (isnull)
160 [ # # ]: 0 : elog(ERROR,
161 : : "requested statistics kind \"%c\" is not yet built for statistics object %u",
162 : : STATS_EXT_NDISTINCT, mvoid);
163 : :
164 : 355 : result = statext_ndistinct_deserialize(DatumGetByteaPP(ndist));
165 : :
166 : 355 : ReleaseSysCache(htup);
167 : :
168 : 355 : return result;
169 : : }
170 : :
171 : : /*
172 : : * statext_ndistinct_serialize
173 : : * serialize ndistinct to the on-disk bytea format
174 : : */
175 : : bytea *
176 : 229 : statext_ndistinct_serialize(MVNDistinct *ndistinct)
177 : : {
178 : : bytea *output;
179 : : char *tmp;
180 : : Size len;
181 : :
182 : : Assert(ndistinct->magic == STATS_NDISTINCT_MAGIC);
183 : : Assert(ndistinct->type == STATS_NDISTINCT_TYPE_BASIC);
184 : :
185 : : /*
186 : : * Base size is size of scalar fields in the struct, plus one base struct
187 : : * for each item, including number of items for each.
188 : : */
189 : 229 : len = VARHDRSZ + SizeOfHeader;
190 : :
191 : : /* and also include space for the actual attribute numbers */
192 [ + + ]: 830 : for (uint32 i = 0; i < ndistinct->nitems; i++)
193 : : {
194 : : int nmembers;
195 : :
196 : 601 : nmembers = ndistinct->items[i].nattributes;
197 : : Assert(nmembers >= 2);
198 : :
199 : 601 : len += SizeOfItem(nmembers);
200 : : }
201 : :
202 : 229 : output = (bytea *) palloc(len);
203 : 229 : SET_VARSIZE(output, len);
204 : :
205 : 229 : tmp = VARDATA(output);
206 : :
207 : : /* Store the base struct values (magic, type, nitems) */
208 : 229 : memcpy(tmp, &ndistinct->magic, sizeof(uint32));
209 : 229 : tmp += sizeof(uint32);
210 : 229 : memcpy(tmp, &ndistinct->type, sizeof(uint32));
211 : 229 : tmp += sizeof(uint32);
212 : 229 : memcpy(tmp, &ndistinct->nitems, sizeof(uint32));
213 : 229 : tmp += sizeof(uint32);
214 : :
215 : : /*
216 : : * store number of attributes and attribute numbers for each entry
217 : : */
218 [ + + ]: 830 : for (uint32 i = 0; i < ndistinct->nitems; i++)
219 : : {
220 : 601 : MVNDistinctItem item = ndistinct->items[i];
221 : 601 : int nmembers = item.nattributes;
222 : :
223 : 601 : memcpy(tmp, &item.ndistinct, sizeof(double));
224 : 601 : tmp += sizeof(double);
225 : 601 : memcpy(tmp, &nmembers, sizeof(int));
226 : 601 : tmp += sizeof(int);
227 : :
228 : 601 : memcpy(tmp, item.attributes, sizeof(AttrNumber) * nmembers);
229 : 601 : tmp += nmembers * sizeof(AttrNumber);
230 : :
231 : : /* protect against overflows */
232 : : Assert(tmp <= ((char *) output + len));
233 : : }
234 : :
235 : : /* check we used exactly the expected space */
236 : : Assert(tmp == ((char *) output + len));
237 : :
238 : 229 : return output;
239 : : }
240 : :
241 : : /*
242 : : * statext_ndistinct_deserialize
243 : : * Read an on-disk bytea format MVNDistinct to in-memory format
244 : : */
245 : : MVNDistinct *
246 : 463 : statext_ndistinct_deserialize(bytea *data)
247 : : {
248 : : Size minimum_size;
249 : : MVNDistinct ndist;
250 : : MVNDistinct *ndistinct;
251 : : char *tmp;
252 : :
253 [ - + ]: 463 : if (data == NULL)
254 : 0 : return NULL;
255 : :
256 : : /* we expect at least the basic fields of MVNDistinct struct */
257 [ - + ]: 463 : if (VARSIZE_ANY_EXHDR(data) < SizeOfHeader)
258 [ # # ]: 0 : elog(ERROR, "invalid MVNDistinct size %zu (expected at least %zu)",
259 : : VARSIZE_ANY_EXHDR(data), SizeOfHeader);
260 : :
261 : : /* initialize pointer to the data part (skip the varlena header) */
262 : 463 : tmp = VARDATA_ANY(data);
263 : :
264 : : /* read the header fields and perform basic sanity checks */
265 : 463 : memcpy(&ndist.magic, tmp, sizeof(uint32));
266 : 463 : tmp += sizeof(uint32);
267 : 463 : memcpy(&ndist.type, tmp, sizeof(uint32));
268 : 463 : tmp += sizeof(uint32);
269 : 463 : memcpy(&ndist.nitems, tmp, sizeof(uint32));
270 : 463 : tmp += sizeof(uint32);
271 : :
272 [ - + ]: 463 : if (ndist.magic != STATS_NDISTINCT_MAGIC)
273 [ # # ]: 0 : elog(ERROR, "invalid ndistinct magic %08x (expected %08x)",
274 : : ndist.magic, STATS_NDISTINCT_MAGIC);
275 [ - + ]: 463 : if (ndist.type != STATS_NDISTINCT_TYPE_BASIC)
276 [ # # ]: 0 : elog(ERROR, "invalid ndistinct type %d (expected %d)",
277 : : ndist.type, STATS_NDISTINCT_TYPE_BASIC);
278 [ - + ]: 463 : if (ndist.nitems == 0)
279 [ # # ]: 0 : elog(ERROR, "invalid zero-length item array in MVNDistinct");
280 : :
281 : : /* what minimum bytea size do we expect for those parameters */
282 : 463 : minimum_size = MinSizeOfItems(ndist.nitems);
283 [ - + ]: 463 : if (VARSIZE_ANY_EXHDR(data) < minimum_size)
284 [ # # ]: 0 : elog(ERROR, "invalid MVNDistinct size %zu (expected at least %zu)",
285 : : VARSIZE_ANY_EXHDR(data), minimum_size);
286 : :
287 : : /*
288 : : * Allocate space for the ndistinct items (no space for each item's
289 : : * attnos: those live in bitmapsets allocated separately)
290 : : */
291 : 463 : ndistinct = palloc0(MAXALIGN(offsetof(MVNDistinct, items)) +
292 : 463 : (ndist.nitems * sizeof(MVNDistinctItem)));
293 : 463 : ndistinct->magic = ndist.magic;
294 : 463 : ndistinct->type = ndist.type;
295 : 463 : ndistinct->nitems = ndist.nitems;
296 : :
297 [ + + ]: 2262 : for (uint32 i = 0; i < ndistinct->nitems; i++)
298 : : {
299 : 1799 : MVNDistinctItem *item = &ndistinct->items[i];
300 : :
301 : : /* ndistinct value */
302 : 1799 : memcpy(&item->ndistinct, tmp, sizeof(double));
303 : 1799 : tmp += sizeof(double);
304 : :
305 : : /* number of attributes */
306 : 1799 : memcpy(&item->nattributes, tmp, sizeof(int));
307 : 1799 : tmp += sizeof(int);
308 : : Assert((item->nattributes >= 2) && (item->nattributes <= STATS_MAX_DIMENSIONS));
309 : :
310 : : item->attributes
311 : 1799 : = (AttrNumber *) palloc(item->nattributes * sizeof(AttrNumber));
312 : :
313 : 1799 : memcpy(item->attributes, tmp, sizeof(AttrNumber) * item->nattributes);
314 : 1799 : tmp += sizeof(AttrNumber) * item->nattributes;
315 : :
316 : : /* still within the bytea */
317 : : Assert(tmp <= ((char *) data + VARSIZE_ANY(data)));
318 : : }
319 : :
320 : : /* we should have consumed the whole bytea exactly */
321 : : Assert(tmp == ((char *) data + VARSIZE_ANY(data)));
322 : :
323 : 463 : return ndistinct;
324 : : }
325 : :
326 : : /*
327 : : * Free allocations of a MVNDistinct.
328 : : */
329 : : void
330 : 32 : statext_ndistinct_free(MVNDistinct *ndistinct)
331 : : {
332 [ + + ]: 128 : for (uint32 i = 0; i < ndistinct->nitems; i++)
333 : 96 : pfree(ndistinct->items[i].attributes);
334 : 32 : pfree(ndistinct);
335 : 32 : }
336 : :
337 : : /*
338 : : * Validate a set of MVNDistincts against the extended statistics object
339 : : * definition.
340 : : *
341 : : * Every MVNDistinctItem must be checked to ensure that the attnums in the
342 : : * attributes list correspond to attnums/expressions defined by the extended
343 : : * statistics object.
344 : : *
345 : : * Positive attnums are attributes which must be found in the stxkeys,
346 : : * while negative attnums correspond to an expression number, no attribute
347 : : * number can be below (0 - numexprs).
348 : : */
349 : : bool
350 : 32 : statext_ndistinct_validate(const MVNDistinct *ndistinct,
351 : : const int2vector *stxkeys,
352 : : int numexprs, int elevel)
353 : : {
354 : 32 : int attnum_expr_lowbound = 0 - numexprs;
355 : :
356 : : /* Scan through each MVNDistinct entry */
357 [ + + ]: 120 : for (uint32 i = 0; i < ndistinct->nitems; i++)
358 : : {
359 : 96 : MVNDistinctItem item = ndistinct->items[i];
360 : :
361 : : /*
362 : : * Cross-check each attribute in a MVNDistinct entry with the extended
363 : : * stats object definition.
364 : : */
365 [ + + ]: 304 : for (int j = 0; j < item.nattributes; j++)
366 : : {
367 : 216 : AttrNumber attnum = item.attributes[j];
368 : 216 : bool ok = false;
369 : :
370 [ + + ]: 216 : if (attnum > 0)
371 : : {
372 : : /* attribute number in stxkeys */
373 [ + + ]: 196 : for (int k = 0; k < stxkeys->dim1; k++)
374 : : {
375 [ + + ]: 188 : if (attnum == stxkeys->values[k])
376 : : {
377 : 120 : ok = true;
378 : 120 : break;
379 : : }
380 : : }
381 : : }
382 [ + - + - ]: 88 : else if ((attnum < 0) && (attnum >= attnum_expr_lowbound))
383 : : {
384 : : /* attribute number for an expression */
385 : 88 : ok = true;
386 : : }
387 : :
388 [ + + ]: 216 : if (!ok)
389 : : {
390 [ + - ]: 8 : ereport(elevel,
391 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
392 : : errmsg("could not validate \"%s\" object: invalid attribute number %d found",
393 : : "pg_ndistinct", attnum)));
394 : 8 : return false;
395 : : }
396 : : }
397 : : }
398 : :
399 : 24 : return true;
400 : : }
401 : :
402 : : /*
403 : : * ndistinct_for_combination
404 : : * Estimates number of distinct values in a combination of columns.
405 : : *
406 : : * This uses the same ndistinct estimator as compute_scalar_stats() in
407 : : * ANALYZE, i.e.,
408 : : * n*d / (n - f1 + f1*n/N)
409 : : *
410 : : * except that instead of values in a single column we are dealing with
411 : : * combination of multiple columns.
412 : : */
413 : : static double
414 : 537 : ndistinct_for_combination(double totalrows, StatsBuildData *data,
415 : : int k, int *combination)
416 : : {
417 : : int i,
418 : : j;
419 : : int f1,
420 : : cnt,
421 : : d;
422 : : bool *isnull;
423 : : Datum *values;
424 : : SortItem *items;
425 : : MultiSortSupport mss;
426 : 537 : int numrows = data->numrows;
427 : :
428 : 537 : mss = multi_sort_init(k);
429 : :
430 : : /*
431 : : * In order to determine the number of distinct elements, create separate
432 : : * values[]/isnull[] arrays with all the data we have, then sort them
433 : : * using the specified column combination as dimensions. We could try to
434 : : * sort in place, but it'd probably be more complex and bug-prone.
435 : : */
436 : 537 : items = palloc_array(SortItem, numrows);
437 : 537 : values = palloc0_array(Datum, numrows * k);
438 : 537 : isnull = palloc0_array(bool, numrows * k);
439 : :
440 [ + + ]: 643885 : for (i = 0; i < numrows; i++)
441 : : {
442 : 643348 : items[i].values = &values[i * k];
443 : 643348 : items[i].isnull = &isnull[i * k];
444 : : }
445 : :
446 : : /*
447 : : * For each dimension, set up sort-support and fill in the values from the
448 : : * sample data.
449 : : *
450 : : * We use the column data types' default sort operators and collations;
451 : : * perhaps at some point it'd be worth using column-specific collations?
452 : : */
453 [ + + ]: 1791 : for (i = 0; i < k; i++)
454 : : {
455 : : Oid typid;
456 : : TypeCacheEntry *type;
457 : 1254 : Oid collid = InvalidOid;
458 : 1254 : VacAttrStats *colstat = data->stats[combination[i]];
459 : :
460 : 1254 : typid = colstat->attrtypid;
461 : 1254 : collid = colstat->attrcollid;
462 : :
463 : 1254 : type = lookup_type_cache(typid, TYPECACHE_LT_OPR);
464 [ - + ]: 1254 : if (type->lt_opr == InvalidOid) /* shouldn't happen */
465 [ # # ]: 0 : elog(ERROR, "cache lookup failed for ordering operator for type %u",
466 : : typid);
467 : :
468 : : /* prepare the sort function for this dimension */
469 : 1254 : multi_sort_add_dimension(mss, i, type->lt_opr, collid);
470 : :
471 : : /* accumulate all the data for this dimension into the arrays */
472 [ + + ]: 1488150 : for (j = 0; j < numrows; j++)
473 : : {
474 : 1486896 : items[j].values[i] = data->values[combination[i]][j];
475 : 1486896 : items[j].isnull[i] = data->nulls[combination[i]][j];
476 : : }
477 : : }
478 : :
479 : : /* We can sort the array now ... */
480 : 537 : qsort_interruptible(items, numrows, sizeof(SortItem),
481 : : multi_sort_compare, mss);
482 : :
483 : : /* ... and count the number of distinct combinations */
484 : :
485 : 537 : f1 = 0;
486 : 537 : cnt = 1;
487 : 537 : d = 1;
488 [ + + ]: 643348 : for (i = 1; i < numrows; i++)
489 : : {
490 [ + + ]: 642811 : if (multi_sort_compare(&items[i], &items[i - 1], mss) != 0)
491 : : {
492 [ + + ]: 190527 : if (cnt == 1)
493 : 98199 : f1 += 1;
494 : :
495 : 190527 : d++;
496 : 190527 : cnt = 0;
497 : : }
498 : :
499 : 642811 : cnt += 1;
500 : : }
501 : :
502 [ + + ]: 537 : if (cnt == 1)
503 : 229 : f1 += 1;
504 : :
505 : 537 : return estimate_ndistinct(totalrows, numrows, d, f1);
506 : : }
507 : :
508 : : /* The Duj1 estimator (already used in analyze.c). */
509 : : static double
510 : 537 : estimate_ndistinct(double totalrows, int numrows, int d, int f1)
511 : : {
512 : : double numer,
513 : : denom,
514 : : ndistinct;
515 : :
516 : 537 : numer = (double) numrows * (double) d;
517 : :
518 : 537 : denom = (double) (numrows - f1) +
519 : 537 : (double) f1 * (double) numrows / totalrows;
520 : :
521 : 537 : ndistinct = numer / denom;
522 : :
523 : : /* Clamp to sane range in case of roundoff error */
524 [ - + ]: 537 : if (ndistinct < (double) d)
525 : 0 : ndistinct = (double) d;
526 : :
527 [ - + ]: 537 : if (ndistinct > totalrows)
528 : 0 : ndistinct = totalrows;
529 : :
530 : 537 : return floor(ndistinct + 0.5);
531 : : }
532 : :
533 : : /*
534 : : * n_choose_k
535 : : * computes binomial coefficients using an algorithm that is both
536 : : * efficient and prevents overflows
537 : : */
538 : : static int
539 : 273 : n_choose_k(int n, int k)
540 : : {
541 : : int d,
542 : : r;
543 : :
544 : : Assert((k > 0) && (n >= k));
545 : :
546 : : /* use symmetry of the binomial coefficients */
547 : 273 : k = Min(k, n - k);
548 : :
549 : 273 : r = 1;
550 [ + + ]: 381 : for (d = 1; d <= k; ++d)
551 : : {
552 : 108 : r *= n--;
553 : 108 : r /= d;
554 : : }
555 : :
556 : 273 : return r;
557 : : }
558 : :
559 : : /*
560 : : * num_combinations
561 : : * number of combinations, excluding single-value combinations
562 : : */
563 : : static int
564 : 189 : num_combinations(int n)
565 : : {
566 : 189 : return (1 << n) - (n + 1);
567 : : }
568 : :
569 : : /*
570 : : * generator_init
571 : : * initialize the generator of combinations
572 : : *
573 : : * The generator produces combinations of K elements in the interval (0..N).
574 : : * We prebuild all the combinations in this method, which is simpler than
575 : : * generating them on the fly.
576 : : */
577 : : static CombinationGenerator *
578 : 273 : generator_init(int n, int k)
579 : : {
580 : : CombinationGenerator *state;
581 : :
582 : : Assert((n >= k) && (k > 0));
583 : :
584 : : /* allocate the generator state as a single chunk of memory */
585 : 273 : state = palloc_object(CombinationGenerator);
586 : :
587 : 273 : state->ncombinations = n_choose_k(n, k);
588 : :
589 : : /* pre-allocate space for all combinations */
590 : 273 : state->combinations = palloc_array(int, k * state->ncombinations);
591 : :
592 : 273 : state->current = 0;
593 : 273 : state->k = k;
594 : 273 : state->n = n;
595 : :
596 : : /* now actually pre-generate all the combinations of K elements */
597 : 273 : generate_combinations(state);
598 : :
599 : : /* make sure we got the expected number of combinations */
600 : : Assert(state->current == state->ncombinations);
601 : :
602 : : /* reset the number, so we start with the first one */
603 : 273 : state->current = 0;
604 : :
605 : 273 : return state;
606 : : }
607 : :
608 : : /*
609 : : * generator_next
610 : : * returns the next combination from the prebuilt list
611 : : *
612 : : * Returns a combination of K array indexes (0 .. N), as specified to
613 : : * generator_init), or NULL when there are no more combination.
614 : : */
615 : : static int *
616 : 810 : generator_next(CombinationGenerator *state)
617 : : {
618 [ + + ]: 810 : if (state->current == state->ncombinations)
619 : 273 : return NULL;
620 : :
621 : 537 : return &state->combinations[state->k * state->current++];
622 : : }
623 : :
624 : : /*
625 : : * generator_free
626 : : * free the internal state of the generator
627 : : *
628 : : * Releases the generator internal state (pre-built combinations).
629 : : */
630 : : static void
631 : 273 : generator_free(CombinationGenerator *state)
632 : : {
633 : 273 : pfree(state->combinations);
634 : 273 : pfree(state);
635 : 273 : }
636 : :
637 : : /*
638 : : * generate_combinations_recurse
639 : : * given a prefix, generate all possible combinations
640 : : *
641 : : * Given a prefix (first few elements of the combination), generate following
642 : : * elements recursively. We generate the combinations in lexicographic order,
643 : : * which eliminates permutations of the same combination.
644 : : */
645 : : static void
646 : 2064 : generate_combinations_recurse(CombinationGenerator *state,
647 : : int index, int start, int *current)
648 : : {
649 : : /* If we haven't filled all the elements, simply recurse. */
650 [ + + ]: 2064 : if (index < state->k)
651 : : {
652 : : int i;
653 : :
654 : : /*
655 : : * The values have to be in ascending order, so make sure we start
656 : : * with the value passed by parameter.
657 : : */
658 : :
659 [ + + ]: 3318 : for (i = start; i < state->n; i++)
660 : : {
661 : 1791 : current[index] = i;
662 : 1791 : generate_combinations_recurse(state, (index + 1), (i + 1), current);
663 : : }
664 : :
665 : 1527 : return;
666 : : }
667 : : else
668 : : {
669 : : /* we got a valid combination, add it to the array */
670 : 537 : memcpy(&state->combinations[(state->k * state->current)],
671 : 537 : current, state->k * sizeof(int));
672 : 537 : state->current++;
673 : : }
674 : : }
675 : :
676 : : /*
677 : : * generate_combinations
678 : : * generate all k-combinations of N elements
679 : : */
680 : : static void
681 : 273 : generate_combinations(CombinationGenerator *state)
682 : : {
683 : 273 : int *current = palloc0_array(int, state->k);
684 : :
685 : 273 : generate_combinations_recurse(state, 0, 0, current);
686 : :
687 : 273 : pfree(current);
688 : 273 : }
|