diff --git a/src/backend/utils/adt/array_typanalyze.c b/src/backend/utils/adt/array_typanalyze.c new file mode 100644 index ffe8035..bd3f2cb *** a/src/backend/utils/adt/array_typanalyze.c --- b/src/backend/utils/adt/array_typanalyze.c *************** compute_array_stats(VacAttrStats *stats, *** 510,517 **** else num_mcelem = track_len; /* Generate MCELEM slot entry */ ! if (num_mcelem > 0) { MemoryContext old_context; Datum *mcelem_values; --- 510,526 ---- else num_mcelem = track_len; + /* + * If we didn't accumulate any frequent enough elements then use + * cutoff_freq as minfreq. Idea is that we assume all frequiencies to + * be in interval [0; cutoff_freq). Thus cutoff_freq / 2 would be sane + * estimate for element frequency. + */ + if (num_mcelem == 0) + minfreq = cutoff_freq; + /* Generate MCELEM slot entry */ ! if (num_mcelem >= 0) { MemoryContext old_context; Datum *mcelem_values; diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c new file mode 100644 index 093da76..f9a5b71 *** a/src/backend/utils/cache/lsyscache.c --- b/src/backend/utils/cache/lsyscache.c *************** get_attstatsslot(HeapTuple statstuple, *** 2863,2914 **** Anum_pg_statistic_stavalues1 + i, &isnull); if (isnull) ! elog(ERROR, "stavalues is null"); ! statarray = DatumGetArrayTypeP(val); ! /* ! * Need to get info about the array element type. We look at the ! * actual element type embedded in the array, which might be only ! * binary-compatible with the passed-in atttype. The info we extract ! * here should be the same either way, but deconstruct_array is picky ! * about having an exact type OID match. ! */ ! arrayelemtype = ARR_ELEMTYPE(statarray); ! typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(arrayelemtype)); ! if (!HeapTupleIsValid(typeTuple)) ! elog(ERROR, "cache lookup failed for type %u", arrayelemtype); ! typeForm = (Form_pg_type) GETSTRUCT(typeTuple); ! /* Deconstruct array into Datum elements; NULLs not expected */ ! deconstruct_array(statarray, ! arrayelemtype, ! typeForm->typlen, ! typeForm->typbyval, ! typeForm->typalign, ! values, NULL, nvalues); ! /* ! * If the element type is pass-by-reference, we now have a bunch of ! * Datums that are pointers into the syscache value. Copy them to ! * avoid problems if syscache decides to drop the entry. ! */ ! if (!typeForm->typbyval) ! { ! for (j = 0; j < *nvalues; j++) { ! (*values)[j] = datumCopy((*values)[j], ! typeForm->typbyval, ! typeForm->typlen); } - } ! ReleaseSysCache(typeTuple); ! /* ! * Free statarray if it's a detoasted copy. ! */ ! if ((Pointer) statarray != DatumGetPointer(val)) ! pfree(statarray); } if (numbers) --- 2863,2921 ---- Anum_pg_statistic_stavalues1 + i, &isnull); if (isnull) ! { ! *values = NULL; ! *nvalues = 0; ! } ! else ! { ! statarray = DatumGetArrayTypeP(val); ! /* ! * Need to get info about the array element type. We look at the ! * actual element type embedded in the array, which might be only ! * binary-compatible with the passed-in atttype. The info we ! * extract here should be the same either way, but deconstruct_array ! * is picky about having an exact type OID match. ! */ ! arrayelemtype = ARR_ELEMTYPE(statarray); ! typeTuple = SearchSysCache1(TYPEOID, ! ObjectIdGetDatum(arrayelemtype)); ! if (!HeapTupleIsValid(typeTuple)) ! elog(ERROR, "cache lookup failed for type %u", arrayelemtype); ! typeForm = (Form_pg_type) GETSTRUCT(typeTuple); ! /* Deconstruct array into Datum elements; NULLs not expected */ ! deconstruct_array(statarray, ! arrayelemtype, ! typeForm->typlen, ! typeForm->typbyval, ! typeForm->typalign, ! values, NULL, nvalues); ! /* ! * If the element type is pass-by-reference, we now have a bunch of ! * Datums that are pointers into the syscache value. Copy them to ! * avoid problems if syscache decides to drop the entry. ! */ ! if (!typeForm->typbyval) { ! for (j = 0; j < *nvalues; j++) ! { ! (*values)[j] = datumCopy((*values)[j], ! typeForm->typbyval, ! typeForm->typlen); ! } } ! ReleaseSysCache(typeTuple); ! /* ! * Free statarray if it's a detoasted copy. ! */ ! if ((Pointer) statarray != DatumGetPointer(val)) ! pfree(statarray); ! } } if (numbers) *************** get_attstatsslot(HeapTuple statstuple, *** 2917,2944 **** Anum_pg_statistic_stanumbers1 + i, &isnull); if (isnull) ! elog(ERROR, "stanumbers is null"); ! statarray = DatumGetArrayTypeP(val); ! /* ! * We expect the array to be a 1-D float4 array; verify that. We don't ! * need to use deconstruct_array() since the array data is just going ! * to look like a C array of float4 values. ! */ ! narrayelem = ARR_DIMS(statarray)[0]; ! if (ARR_NDIM(statarray) != 1 || narrayelem <= 0 || ! ARR_HASNULL(statarray) || ! ARR_ELEMTYPE(statarray) != FLOAT4OID) ! elog(ERROR, "stanumbers is not a 1-D float4 array"); ! *numbers = (float4 *) palloc(narrayelem * sizeof(float4)); ! memcpy(*numbers, ARR_DATA_PTR(statarray), narrayelem * sizeof(float4)); ! *nnumbers = narrayelem; ! /* ! * Free statarray if it's a detoasted copy. ! */ ! if ((Pointer) statarray != DatumGetPointer(val)) ! pfree(statarray); } return true; --- 2924,2958 ---- Anum_pg_statistic_stanumbers1 + i, &isnull); if (isnull) ! { ! *numbers = NULL; ! *nnumbers = 0; ! } ! else ! { ! statarray = DatumGetArrayTypeP(val); ! /* ! * We expect the array to be a 1-D float4 array; verify that. We ! * don't need to use deconstruct_array() since the array data is ! * just going to look like a C array of float4 values. ! */ ! narrayelem = ARR_DIMS(statarray)[0]; ! if (ARR_NDIM(statarray) != 1 || narrayelem <= 0 || ! ARR_HASNULL(statarray) || ! ARR_ELEMTYPE(statarray) != FLOAT4OID) ! elog(ERROR, "stanumbers is not a 1-D float4 array"); ! *numbers = (float4 *) palloc(narrayelem * sizeof(float4)); ! memcpy(*numbers, ARR_DATA_PTR(statarray), ! narrayelem * sizeof(float4)); ! *nnumbers = narrayelem; ! /* ! * Free statarray if it's a detoasted copy. ! */ ! if ((Pointer) statarray != DatumGetPointer(val)) ! pfree(statarray); ! } } return true;