LCOV - code coverage report
Current view: top level - src/backend/statistics - extended_stats_funcs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 96.5 % 575 555
Test Date: 2026-05-23 21:16:15 Functions: 100.0 % 16 16
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * extended_stats_funcs.c
       4              :  *    Functions for manipulating extended statistics.
       5              :  *
       6              :  * This file includes the set of facilities required to support the direct
       7              :  * manipulations of extended statistics objects.
       8              :  *
       9              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      10              :  * Portions Copyright (c) 1994, Regents of the University of California
      11              :  *
      12              :  * IDENTIFICATION
      13              :  *    src/backend/statistics/extended_stats_funcs.c
      14              :  *
      15              :  *-------------------------------------------------------------------------
      16              :  */
      17              : #include "postgres.h"
      18              : 
      19              : #include "access/heapam.h"
      20              : #include "catalog/indexing.h"
      21              : #include "catalog/namespace.h"
      22              : #include "catalog/pg_collation_d.h"
      23              : #include "catalog/pg_database.h"
      24              : #include "catalog/pg_operator.h"
      25              : #include "catalog/pg_statistic_ext.h"
      26              : #include "catalog/pg_statistic_ext_data.h"
      27              : #include "miscadmin.h"
      28              : #include "nodes/makefuncs.h"
      29              : #include "nodes/nodeFuncs.h"
      30              : #include "optimizer/optimizer.h"
      31              : #include "statistics/extended_stats_internal.h"
      32              : #include "statistics/stat_utils.h"
      33              : #include "utils/acl.h"
      34              : #include "utils/array.h"
      35              : #include "utils/builtins.h"
      36              : #include "utils/fmgroids.h"
      37              : #include "utils/jsonb.h"
      38              : #include "utils/lsyscache.h"
      39              : #include "utils/syscache.h"
      40              : #include "utils/typcache.h"
      41              : 
      42              : 
      43              : /*
      44              :  * Index of the arguments for the SQL functions.
      45              :  */
      46              : enum extended_stats_argnum
      47              : {
      48              :     RELSCHEMA_ARG = 0,
      49              :     RELNAME_ARG,
      50              :     STATSCHEMA_ARG,
      51              :     STATNAME_ARG,
      52              :     INHERITED_ARG,
      53              :     NDISTINCT_ARG,
      54              :     DEPENDENCIES_ARG,
      55              :     MOST_COMMON_VALS_ARG,
      56              :     MOST_COMMON_FREQS_ARG,
      57              :     MOST_COMMON_BASE_FREQS_ARG,
      58              :     EXPRESSIONS_ARG,
      59              :     NUM_EXTENDED_STATS_ARGS,
      60              : };
      61              : 
      62              : /*
      63              :  * The argument names and type OIDs of the arguments for the SQL
      64              :  * functions.
      65              :  */
      66              : static struct StatsArgInfo extarginfo[] =
      67              : {
      68              :     [RELSCHEMA_ARG] = {"schemaname", TEXTOID},
      69              :     [RELNAME_ARG] = {"relname", TEXTOID},
      70              :     [STATSCHEMA_ARG] = {"statistics_schemaname", TEXTOID},
      71              :     [STATNAME_ARG] = {"statistics_name", TEXTOID},
      72              :     [INHERITED_ARG] = {"inherited", BOOLOID},
      73              :     [NDISTINCT_ARG] = {"n_distinct", PG_NDISTINCTOID},
      74              :     [DEPENDENCIES_ARG] = {"dependencies", PG_DEPENDENCIESOID},
      75              :     [MOST_COMMON_VALS_ARG] = {"most_common_vals", TEXTARRAYOID},
      76              :     [MOST_COMMON_FREQS_ARG] = {"most_common_freqs", FLOAT8ARRAYOID},
      77              :     [MOST_COMMON_BASE_FREQS_ARG] = {"most_common_base_freqs", FLOAT8ARRAYOID},
      78              :     [EXPRESSIONS_ARG] = {"exprs", JSONBOID},
      79              :     [NUM_EXTENDED_STATS_ARGS] = {0},
      80              : };
      81              : 
      82              : /*
      83              :  * An index of the elements of a stxdexpr Datum, which repeat for each
      84              :  * expression in the extended statistics object.
      85              :  */
      86              : enum extended_stats_exprs_element
      87              : {
      88              :     NULL_FRAC_ELEM = 0,
      89              :     AVG_WIDTH_ELEM,
      90              :     N_DISTINCT_ELEM,
      91              :     MOST_COMMON_VALS_ELEM,
      92              :     MOST_COMMON_FREQS_ELEM,
      93              :     HISTOGRAM_BOUNDS_ELEM,
      94              :     CORRELATION_ELEM,
      95              :     MOST_COMMON_ELEMS_ELEM,
      96              :     MOST_COMMON_ELEM_FREQS_ELEM,
      97              :     ELEM_COUNT_HISTOGRAM_ELEM,
      98              :     RANGE_LENGTH_HISTOGRAM_ELEM,
      99              :     RANGE_EMPTY_FRAC_ELEM,
     100              :     RANGE_BOUNDS_HISTOGRAM_ELEM,
     101              :     NUM_ATTRIBUTE_STATS_ELEMS
     102              : };
     103              : 
     104              : /*
     105              :  * The argument names of the repeating arguments for stxdexpr.
     106              :  */
     107              : static const char *extexprargname[NUM_ATTRIBUTE_STATS_ELEMS] =
     108              : {
     109              :     "null_frac",
     110              :     "avg_width",
     111              :     "n_distinct",
     112              :     "most_common_vals",
     113              :     "most_common_freqs",
     114              :     "histogram_bounds",
     115              :     "correlation",
     116              :     "most_common_elems",
     117              :     "most_common_elem_freqs",
     118              :     "elem_count_histogram",
     119              :     "range_length_histogram",
     120              :     "range_empty_frac",
     121              :     "range_bounds_histogram"
     122              : };
     123              : 
     124              : static bool extended_statistics_update(FunctionCallInfo fcinfo);
     125              : 
     126              : static HeapTuple get_pg_statistic_ext(Relation pg_stext, Oid nspoid,
     127              :                                       const char *stxname);
     128              : static bool delete_pg_statistic_ext_data(Oid stxoid, bool inherited);
     129              : 
     130              : /*
     131              :  * Track the extended statistics kinds expected for a pg_statistic_ext
     132              :  * tuple.
     133              :  */
     134              : typedef struct
     135              : {
     136              :     bool        ndistinct;
     137              :     bool        dependencies;
     138              :     bool        mcv;
     139              :     bool        expressions;
     140              : } StakindFlags;
     141              : 
     142              : static void expand_stxkind(HeapTuple tup, StakindFlags *enabled);
     143              : static void upsert_pg_statistic_ext_data(const Datum *values,
     144              :                                          const bool *nulls,
     145              :                                          const bool *replaces);
     146              : 
     147              : static bool check_mcvlist_array(const ArrayType *arr, int argindex,
     148              :                                 int required_ndims, int mcv_length);
     149              : static Datum import_expressions(Relation pgsd, int numexprs,
     150              :                                 Oid *atttypids, int32 *atttypmods,
     151              :                                 Oid *atttypcolls, Jsonb *exprs_jsonb,
     152              :                                 bool *exprs_is_perfect);
     153              : static Datum import_mcv(const ArrayType *mcv_arr,
     154              :                         const ArrayType *freqs_arr,
     155              :                         const ArrayType *base_freqs_arr,
     156              :                         Oid *atttypids, int32 *atttypmods,
     157              :                         Oid *atttypcolls, int numattrs,
     158              :                         bool *ok);
     159              : 
     160              : static char *jbv_string_get_cstr(JsonbValue *jval);
     161              : static bool jbv_to_infunc_datum(JsonbValue *jval, PGFunction func,
     162              :                                 AttrNumber exprnum, const char *argname,
     163              :                                 Datum *datum);
     164              : static bool key_in_expr_argnames(JsonbValue *key);
     165              : static bool check_all_expr_argnames_valid(JsonbContainer *cont, AttrNumber exprnum);
     166              : static Datum array_in_safe(FmgrInfo *array_in, const char *s, Oid typid,
     167              :                            int32 typmod, AttrNumber exprnum,
     168              :                            const char *element_name, bool *ok);
     169              : static Datum import_pg_statistic(Relation pgsd, JsonbContainer *cont,
     170              :                                  AttrNumber exprnum, FmgrInfo *array_in_fn,
     171              :                                  Oid typid, int32 typmod, Oid typcoll,
     172              :                                  bool *pg_statistic_ok);
     173              : 
     174              : /*
     175              :  * Fetch a pg_statistic_ext row by name and namespace OID.
     176              :  */
     177              : static HeapTuple
     178          296 : get_pg_statistic_ext(Relation pg_stext, Oid nspoid, const char *stxname)
     179              : {
     180              :     ScanKeyData key[2];
     181              :     SysScanDesc scan;
     182              :     HeapTuple   tup;
     183          296 :     Oid         stxoid = InvalidOid;
     184              : 
     185          296 :     ScanKeyInit(&key[0],
     186              :                 Anum_pg_statistic_ext_stxname,
     187              :                 BTEqualStrategyNumber,
     188              :                 F_NAMEEQ,
     189              :                 CStringGetDatum(stxname));
     190          296 :     ScanKeyInit(&key[1],
     191              :                 Anum_pg_statistic_ext_stxnamespace,
     192              :                 BTEqualStrategyNumber,
     193              :                 F_OIDEQ,
     194              :                 ObjectIdGetDatum(nspoid));
     195              : 
     196              :     /*
     197              :      * Try to find matching pg_statistic_ext row.
     198              :      */
     199          296 :     scan = systable_beginscan(pg_stext,
     200              :                               StatisticExtNameIndexId,
     201              :                               true,
     202              :                               NULL,
     203              :                               2,
     204              :                               key);
     205              : 
     206              :     /* Lookup is based on a unique index, so we get either 0 or 1 tuple. */
     207          296 :     tup = systable_getnext(scan);
     208              : 
     209          296 :     if (HeapTupleIsValid(tup))
     210          288 :         stxoid = ((Form_pg_statistic_ext) GETSTRUCT(tup))->oid;
     211              : 
     212          296 :     systable_endscan(scan);
     213              : 
     214          296 :     if (!OidIsValid(stxoid))
     215            8 :         return NULL;
     216              : 
     217          288 :     return SearchSysCacheCopy1(STATEXTOID, ObjectIdGetDatum(stxoid));
     218              : }
     219              : 
     220              : /*
     221              :  * Decode the stxkind column so that we know which stats types to expect,
     222              :  * returning a StakindFlags set depending on the stats kinds expected by
     223              :  * a pg_statistic_ext tuple.
     224              :  */
     225              : static void
     226          268 : expand_stxkind(HeapTuple tup, StakindFlags *enabled)
     227              : {
     228              :     Datum       datum;
     229              :     ArrayType  *arr;
     230              :     char       *kinds;
     231              : 
     232          268 :     datum = SysCacheGetAttrNotNull(STATEXTOID,
     233              :                                    tup,
     234              :                                    Anum_pg_statistic_ext_stxkind);
     235          268 :     arr = DatumGetArrayTypeP(datum);
     236          268 :     if (ARR_NDIM(arr) != 1 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != CHAROID)
     237            0 :         elog(ERROR, "stxkind is not a one-dimension char array");
     238              : 
     239          268 :     kinds = (char *) ARR_DATA_PTR(arr);
     240              : 
     241         1097 :     for (int i = 0; i < ARR_DIMS(arr)[0]; i++)
     242              :     {
     243          829 :         switch (kinds[i])
     244              :         {
     245          204 :             case STATS_EXT_NDISTINCT:
     246          204 :                 enabled->ndistinct = true;
     247          204 :                 break;
     248          205 :             case STATS_EXT_DEPENDENCIES:
     249          205 :                 enabled->dependencies = true;
     250          205 :                 break;
     251          219 :             case STATS_EXT_MCV:
     252          219 :                 enabled->mcv = true;
     253          219 :                 break;
     254          201 :             case STATS_EXT_EXPRESSIONS:
     255          201 :                 enabled->expressions = true;
     256          201 :                 break;
     257            0 :             default:
     258            0 :                 elog(ERROR, "incorrect stxkind %c found", kinds[i]);
     259              :                 break;
     260              :         }
     261              :     }
     262          268 : }
     263              : 
     264              : /*
     265              :  * Perform the actual storage of a pg_statistic_ext_data tuple.
     266              :  */
     267              : static void
     268          268 : upsert_pg_statistic_ext_data(const Datum *values, const bool *nulls,
     269              :                              const bool *replaces)
     270              : {
     271              :     Relation    pg_stextdata;
     272              :     HeapTuple   stxdtup;
     273              :     HeapTuple   newtup;
     274              : 
     275          268 :     pg_stextdata = table_open(StatisticExtDataRelationId, RowExclusiveLock);
     276              : 
     277          268 :     stxdtup = SearchSysCache2(STATEXTDATASTXOID,
     278              :                               values[Anum_pg_statistic_ext_data_stxoid - 1],
     279          268 :                               values[Anum_pg_statistic_ext_data_stxdinherit - 1]);
     280              : 
     281          268 :     if (HeapTupleIsValid(stxdtup))
     282              :     {
     283          244 :         newtup = heap_modify_tuple(stxdtup,
     284              :                                    RelationGetDescr(pg_stextdata),
     285              :                                    values,
     286              :                                    nulls,
     287              :                                    replaces);
     288          244 :         CatalogTupleUpdate(pg_stextdata, &newtup->t_self, newtup);
     289          244 :         ReleaseSysCache(stxdtup);
     290              :     }
     291              :     else
     292              :     {
     293           24 :         newtup = heap_form_tuple(RelationGetDescr(pg_stextdata), values, nulls);
     294           24 :         CatalogTupleInsert(pg_stextdata, newtup);
     295              :     }
     296              : 
     297          268 :     heap_freetuple(newtup);
     298              : 
     299          268 :     CommandCounterIncrement();
     300              : 
     301          268 :     table_close(pg_stextdata, RowExclusiveLock);
     302          268 : }
     303              : 
     304              : /*
     305              :  * Insert or update an extended statistics object.
     306              :  *
     307              :  * Major errors, such as the table not existing or permission errors, are
     308              :  * reported as ERRORs.  There are a couple of paths that generate a WARNING,
     309              :  * like when the statistics object or its schema do not exist, a conversion
     310              :  * failure on one statistic kind, or when other statistic kinds may still
     311              :  * be updated.
     312              :  */
     313              : static bool
     314          312 : extended_statistics_update(FunctionCallInfo fcinfo)
     315              : {
     316              :     char       *relnspname;
     317              :     char       *relname;
     318              :     Oid         nspoid;
     319              :     char       *nspname;
     320              :     char       *stxname;
     321              :     bool        inherited;
     322          312 :     Relation    pg_stext = NULL;
     323          312 :     HeapTuple   tup = NULL;
     324              : 
     325          312 :     StakindFlags enabled = {false, false, false, false};
     326          312 :     StakindFlags has = {false, false, false, false};
     327              : 
     328              :     Form_pg_statistic_ext stxform;
     329              : 
     330          312 :     Datum       values[Natts_pg_statistic_ext_data] = {0};
     331          312 :     bool        nulls[Natts_pg_statistic_ext_data] = {0};
     332          312 :     bool        replaces[Natts_pg_statistic_ext_data] = {0};
     333          312 :     bool        success = true;
     334              :     Datum       exprdatum;
     335              :     bool        isnull;
     336          312 :     List       *exprs = NIL;
     337          312 :     int         numattnums = 0;
     338          312 :     int         numexprs = 0;
     339          312 :     int         numattrs = 0;
     340              : 
     341              :     /* arrays of type info, if we need them */
     342          312 :     Oid        *atttypids = NULL;
     343          312 :     int32      *atttypmods = NULL;
     344          312 :     Oid        *atttypcolls = NULL;
     345              :     Oid         relid;
     346          312 :     Oid         locked_table = InvalidOid;
     347              : 
     348              :     /*
     349              :      * Fill out the StakindFlags "has" structure based on which parameters
     350              :      * were provided to the function.
     351              :      *
     352              :      * The MCV stats composite value is an array of record type, but this is
     353              :      * externally represented as three arrays that must be interleaved into
     354              :      * the array of records (pg_stats_ext stores four arrays,
     355              :      * most_common_val_nulls is built from the contents of most_common_vals).
     356              :      * Therefore, none of the three array values is meaningful unless the
     357              :      * other two are also present and in sync in terms of array length.
     358              :      */
     359          675 :     has.mcv = (!PG_ARGISNULL(MOST_COMMON_VALS_ARG) &&
     360          359 :                !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) &&
     361           47 :                !PG_ARGISNULL(MOST_COMMON_BASE_FREQS_ARG));
     362          312 :     has.ndistinct = !PG_ARGISNULL(NDISTINCT_ARG);
     363          312 :     has.dependencies = !PG_ARGISNULL(DEPENDENCIES_ARG);
     364          312 :     has.expressions = !PG_ARGISNULL(EXPRESSIONS_ARG);
     365              : 
     366          312 :     if (RecoveryInProgress())
     367              :     {
     368            0 :         ereport(WARNING,
     369              :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     370              :                 errmsg("recovery is in progress"),
     371              :                 errhint("Statistics cannot be modified during recovery."));
     372            0 :         return false;
     373              :     }
     374              : 
     375              :     /* relation arguments */
     376          312 :     stats_check_required_arg(fcinfo, extarginfo, RELSCHEMA_ARG);
     377          308 :     relnspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
     378          308 :     stats_check_required_arg(fcinfo, extarginfo, RELNAME_ARG);
     379          304 :     relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
     380              : 
     381              :     /* extended statistics arguments */
     382          304 :     stats_check_required_arg(fcinfo, extarginfo, STATSCHEMA_ARG);
     383          300 :     nspname = TextDatumGetCString(PG_GETARG_DATUM(STATSCHEMA_ARG));
     384          300 :     stats_check_required_arg(fcinfo, extarginfo, STATNAME_ARG);
     385          296 :     stxname = TextDatumGetCString(PG_GETARG_DATUM(STATNAME_ARG));
     386          296 :     stats_check_required_arg(fcinfo, extarginfo, INHERITED_ARG);
     387          292 :     inherited = PG_GETARG_BOOL(INHERITED_ARG);
     388              : 
     389              :     /*
     390              :      * First open the relation where we expect to find the statistics.  This
     391              :      * is similar to relation and attribute statistics, so as ACL checks are
     392              :      * done before any locks are taken, even before any attempts related to
     393              :      * the extended stats object.
     394              :      */
     395          292 :     relid = RangeVarGetRelidExtended(makeRangeVar(relnspname, relname, -1),
     396              :                                      ShareUpdateExclusiveLock, 0,
     397              :                                      RangeVarCallbackForStats, &locked_table);
     398              : 
     399          280 :     nspoid = get_namespace_oid(nspname, true);
     400          280 :     if (nspoid == InvalidOid)
     401              :     {
     402            4 :         ereport(WARNING,
     403              :                 errcode(ERRCODE_UNDEFINED_OBJECT),
     404              :                 errmsg("could not find schema \"%s\"", nspname));
     405            4 :         success = false;
     406            4 :         goto cleanup;
     407              :     }
     408              : 
     409          276 :     pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock);
     410          276 :     tup = get_pg_statistic_ext(pg_stext, nspoid, stxname);
     411              : 
     412          276 :     if (!HeapTupleIsValid(tup))
     413              :     {
     414            4 :         ereport(WARNING,
     415              :                 errcode(ERRCODE_UNDEFINED_OBJECT),
     416              :                 errmsg("could not find extended statistics object \"%s.%s\"",
     417              :                        nspname, stxname));
     418            4 :         success = false;
     419            4 :         goto cleanup;
     420              :     }
     421              : 
     422          272 :     stxform = (Form_pg_statistic_ext) GETSTRUCT(tup);
     423              : 
     424              :     /*
     425              :      * The relation tracked by the stats object has to match with the relation
     426              :      * we have already locked.
     427              :      */
     428          272 :     if (stxform->stxrelid != relid)
     429              :     {
     430            4 :         ereport(WARNING,
     431              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     432              :                 errmsg("could not restore extended statistics object \"%s.%s\": incorrect relation \"%s.%s\" specified",
     433              :                        nspname, stxname,
     434              :                        relnspname, relname));
     435              : 
     436            4 :         success = false;
     437            4 :         goto cleanup;
     438              :     }
     439              : 
     440              :     /* Find out what extended statistics kinds we should expect. */
     441          268 :     expand_stxkind(tup, &enabled);
     442          268 :     numattnums = stxform->stxkeys.dim1;
     443              : 
     444              :     /* decode expression (if any) */
     445          268 :     exprdatum = SysCacheGetAttr(STATEXTOID,
     446              :                                 tup,
     447              :                                 Anum_pg_statistic_ext_stxexprs,
     448              :                                 &isnull);
     449          268 :     if (!isnull)
     450              :     {
     451              :         char       *s;
     452              : 
     453          201 :         s = TextDatumGetCString(exprdatum);
     454          201 :         exprs = (List *) stringToNode(s);
     455          201 :         pfree(s);
     456              : 
     457              :         /*
     458              :          * Run the expressions through eval_const_expressions().  This is not
     459              :          * just an optimization, but is necessary, because the planner will be
     460              :          * comparing them to similarly-processed qual clauses, and may fail to
     461              :          * detect valid matches without this.
     462              :          *
     463              :          * We must not use canonicalize_qual(), however, since these are not
     464              :          * qual expressions.
     465              :          */
     466          201 :         exprs = (List *) eval_const_expressions(NULL, (Node *) exprs);
     467              : 
     468              :         /* May as well fix opfuncids too */
     469          201 :         fix_opfuncids((Node *) exprs);
     470              : 
     471              :         /* Compute the number of expression, for input validation. */
     472          201 :         numexprs = list_length(exprs);
     473              :     }
     474              : 
     475          268 :     numattrs = numattnums + numexprs;
     476              : 
     477              :     /*
     478              :      * If the object cannot support ndistinct, we should not have data for it.
     479              :      */
     480          268 :     if (has.ndistinct && !enabled.ndistinct)
     481              :     {
     482            4 :         ereport(WARNING,
     483              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     484              :                 errmsg("cannot specify parameter \"%s\"",
     485              :                        extarginfo[NDISTINCT_ARG].argname),
     486              :                 errhint("Extended statistics object \"%s.%s\" does not support statistics of this type.",
     487              :                         nspname, stxname));
     488              : 
     489            4 :         has.ndistinct = false;
     490            4 :         success = false;
     491              :     }
     492              : 
     493              :     /*
     494              :      * If the object cannot support dependencies, we should not have data for
     495              :      * it.
     496              :      */
     497          268 :     if (has.dependencies && !enabled.dependencies)
     498              :     {
     499            4 :         ereport(WARNING,
     500              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     501              :                 errmsg("cannot specify parameter \"%s\"",
     502              :                        extarginfo[DEPENDENCIES_ARG].argname),
     503              :                 errhint("Extended statistics object \"%s.%s\" does not support statistics of this type.",
     504              :                         nspname, stxname));
     505            4 :         has.dependencies = false;
     506            4 :         success = false;
     507              :     }
     508              : 
     509              :     /*
     510              :      * If the object cannot hold an MCV value, but any of the MCV parameters
     511              :      * are set, then issue a WARNING and ensure that we do not try to load MCV
     512              :      * stats later.  In pg_stats_ext, most_common_val_nulls, most_common_freqs
     513              :      * and most_common_base_freqs are NULL if most_common_vals is NULL.
     514              :      */
     515          268 :     if (!enabled.mcv)
     516              :     {
     517           49 :         if (!PG_ARGISNULL(MOST_COMMON_VALS_ARG) ||
     518           45 :             !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) ||
     519           45 :             !PG_ARGISNULL(MOST_COMMON_BASE_FREQS_ARG))
     520              :         {
     521            4 :             ereport(WARNING,
     522              :                     errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     523              :                     errmsg("cannot specify parameters \"%s\", \"%s\", or \"%s\"",
     524              :                            extarginfo[MOST_COMMON_VALS_ARG].argname,
     525              :                            extarginfo[MOST_COMMON_FREQS_ARG].argname,
     526              :                            extarginfo[MOST_COMMON_BASE_FREQS_ARG].argname),
     527              :                     errhint("Extended statistics object \"%s.%s\" does not support statistics of this type.",
     528              :                             nspname, stxname));
     529              : 
     530            4 :             has.mcv = false;
     531            4 :             success = false;
     532              :         }
     533              :     }
     534          219 :     else if (!has.mcv)
     535              :     {
     536              :         /*
     537              :          * If we do not have all of the MCV arrays set while the extended
     538              :          * statistics object expects something, something is wrong.  This
     539              :          * issues a WARNING if a partial input has been provided.
     540              :          */
     541          180 :         if (!PG_ARGISNULL(MOST_COMMON_VALS_ARG) ||
     542          172 :             !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) ||
     543          168 :             !PG_ARGISNULL(MOST_COMMON_BASE_FREQS_ARG))
     544              :         {
     545           12 :             ereport(WARNING,
     546              :                     errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     547              :                     errmsg("could not use \"%s\", \"%s\", and \"%s\": missing one or more parameters",
     548              :                            extarginfo[MOST_COMMON_VALS_ARG].argname,
     549              :                            extarginfo[MOST_COMMON_FREQS_ARG].argname,
     550              :                            extarginfo[MOST_COMMON_BASE_FREQS_ARG].argname));
     551           12 :             success = false;
     552              :         }
     553              :     }
     554              : 
     555              :     /*
     556              :      * If the object cannot support expressions, we should not have data for
     557              :      * them.
     558              :      */
     559          268 :     if (has.expressions && !enabled.expressions)
     560              :     {
     561            4 :         ereport(WARNING,
     562              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     563              :                 errmsg("cannot specify parameter \"%s\"",
     564              :                        extarginfo[EXPRESSIONS_ARG].argname),
     565              :                 errhint("Extended statistics object \"%s.%s\" does not support statistics of this type.",
     566              :                         nspname, stxname));
     567              : 
     568            4 :         has.expressions = false;
     569            4 :         success = false;
     570              :     }
     571              : 
     572              :     /*
     573              :      * Either of these statistic types requires that we supply a semi-filled
     574              :      * VacAttrStatsP array.
     575              :      *
     576              :      * It is not possible to use the existing lookup_var_attr_stats() and
     577              :      * examine_attribute() because these functions will skip attributes where
     578              :      * attstattarget is 0, and we may have statistics data to import for those
     579              :      * attributes.
     580              :      */
     581          268 :     if (has.mcv || has.expressions)
     582              :     {
     583          204 :         atttypids = palloc0_array(Oid, numattrs);
     584          204 :         atttypmods = palloc0_array(int32, numattrs);
     585          204 :         atttypcolls = palloc0_array(Oid, numattrs);
     586              : 
     587              :         /*
     588              :          * The leading stxkeys are attribute numbers up through numattnums.
     589              :          * These keys must be in ascending AttrNumber order, but we do not
     590              :          * rely on that.
     591              :          */
     592          553 :         for (int i = 0; i < numattnums; i++)
     593              :         {
     594          349 :             AttrNumber  attnum = stxform->stxkeys.values[i];
     595          349 :             HeapTuple   atup = SearchSysCache2(ATTNUM,
     596              :                                                ObjectIdGetDatum(relid),
     597              :                                                Int16GetDatum(attnum));
     598              : 
     599              :             Form_pg_attribute attr;
     600              : 
     601              :             /* Attribute not found */
     602          349 :             if (!HeapTupleIsValid(atup))
     603            0 :                 elog(ERROR, "stxkeys references nonexistent attnum %d", attnum);
     604              : 
     605          349 :             attr = (Form_pg_attribute) GETSTRUCT(atup);
     606              : 
     607          349 :             if (attr->attisdropped)
     608            0 :                 elog(ERROR, "stxkeys references dropped attnum %d", attnum);
     609              : 
     610          349 :             atttypids[i] = attr->atttypid;
     611          349 :             atttypmods[i] = attr->atttypmod;
     612          349 :             atttypcolls[i] = attr->attcollation;
     613          349 :             ReleaseSysCache(atup);
     614              :         }
     615              : 
     616              :         /*
     617              :          * After all the positive number attnums in stxkeys come the negative
     618              :          * numbers (if any) which represent expressions in the order that they
     619              :          * appear in stxdexpr.  Because the expressions are always
     620              :          * monotonically decreasing from -1, there is no point in looking at
     621              :          * the values in stxkeys, it's enough to know how many of them there
     622              :          * are.
     623              :          */
     624          499 :         for (int i = numattnums; i < numattrs; i++)
     625              :         {
     626          295 :             Node       *expr = list_nth(exprs, i - numattnums);
     627              : 
     628          295 :             atttypids[i] = exprType(expr);
     629          295 :             atttypmods[i] = exprTypmod(expr);
     630          295 :             atttypcolls[i] = exprCollation(expr);
     631              :         }
     632              :     }
     633              : 
     634              :     /*
     635              :      * Populate the pg_statistic_ext_data result tuple.
     636              :      */
     637              : 
     638              :     /* Primary Key: cannot be NULL or replaced. */
     639          268 :     values[Anum_pg_statistic_ext_data_stxoid - 1] = ObjectIdGetDatum(stxform->oid);
     640          268 :     nulls[Anum_pg_statistic_ext_data_stxoid - 1] = false;
     641          268 :     values[Anum_pg_statistic_ext_data_stxdinherit - 1] = BoolGetDatum(inherited);
     642          268 :     nulls[Anum_pg_statistic_ext_data_stxdinherit - 1] = false;
     643              : 
     644              :     /* All unspecified parameters will be left unmodified */
     645          268 :     nulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true;
     646          268 :     nulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = true;
     647          268 :     nulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
     648          268 :     nulls[Anum_pg_statistic_ext_data_stxdexpr - 1] = true;
     649              : 
     650              :     /*
     651              :      * For each stats kind, deserialize the data at hand and perform a round
     652              :      * of validation.  The resulting tuple is filled with a set of updated
     653              :      * values.
     654              :      */
     655              : 
     656          268 :     if (has.ndistinct)
     657              :     {
     658           32 :         Datum       ndistinct_datum = PG_GETARG_DATUM(NDISTINCT_ARG);
     659           32 :         bytea      *data = DatumGetByteaPP(ndistinct_datum);
     660           32 :         MVNDistinct *ndistinct = statext_ndistinct_deserialize(data);
     661              : 
     662           32 :         if (statext_ndistinct_validate(ndistinct, &stxform->stxkeys,
     663              :                                        numexprs, WARNING))
     664              :         {
     665           24 :             values[Anum_pg_statistic_ext_data_stxdndistinct - 1] = ndistinct_datum;
     666           24 :             nulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = false;
     667           24 :             replaces[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true;
     668              :         }
     669              :         else
     670            8 :             success = false;
     671              : 
     672           32 :         statext_ndistinct_free(ndistinct);
     673              :     }
     674              : 
     675          268 :     if (has.dependencies)
     676              :     {
     677           29 :         Datum       dependencies_datum = PG_GETARG_DATUM(DEPENDENCIES_ARG);
     678           29 :         bytea      *data = DatumGetByteaPP(dependencies_datum);
     679           29 :         MVDependencies *dependencies = statext_dependencies_deserialize(data);
     680              : 
     681           29 :         if (statext_dependencies_validate(dependencies, &stxform->stxkeys,
     682              :                                           numexprs, WARNING))
     683              :         {
     684           21 :             values[Anum_pg_statistic_ext_data_stxddependencies - 1] = dependencies_datum;
     685           21 :             nulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = false;
     686           21 :             replaces[Anum_pg_statistic_ext_data_stxddependencies - 1] = true;
     687              :         }
     688              :         else
     689            8 :             success = false;
     690              : 
     691           29 :         statext_dependencies_free(dependencies);
     692              :     }
     693              : 
     694          268 :     if (has.mcv)
     695              :     {
     696              :         Datum       datum;
     697           39 :         bool        val_ok = false;
     698              : 
     699           39 :         datum = import_mcv(PG_GETARG_ARRAYTYPE_P(MOST_COMMON_VALS_ARG),
     700           39 :                            PG_GETARG_ARRAYTYPE_P(MOST_COMMON_FREQS_ARG),
     701           39 :                            PG_GETARG_ARRAYTYPE_P(MOST_COMMON_BASE_FREQS_ARG),
     702              :                            atttypids, atttypmods, atttypcolls, numattrs,
     703              :                            &val_ok);
     704              : 
     705           39 :         if (val_ok)
     706              :         {
     707              :             Assert(datum != (Datum) 0);
     708           23 :             values[Anum_pg_statistic_ext_data_stxdmcv - 1] = datum;
     709           23 :             nulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = false;
     710           23 :             replaces[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
     711              :         }
     712              :         else
     713           16 :             success = false;
     714              :     }
     715              : 
     716          268 :     if (has.expressions)
     717              :     {
     718              :         Datum       datum;
     719              :         Relation    pgsd;
     720          177 :         bool        ok = false;
     721              : 
     722          177 :         pgsd = table_open(StatisticRelationId, RowExclusiveLock);
     723              : 
     724              :         /*
     725              :          * Generate the expressions array.
     726              :          *
     727              :          * The atttypids, atttypmods, and atttypcolls arrays have all the
     728              :          * regular attributes listed first, so we can pass those arrays with a
     729              :          * start point after the last regular attribute.  There are numexprs
     730              :          * elements remaining.
     731              :          */
     732          177 :         datum = import_expressions(pgsd, numexprs,
     733          177 :                                    &atttypids[numattnums],
     734          177 :                                    &atttypmods[numattnums],
     735          177 :                                    &atttypcolls[numattnums],
     736              :                                    PG_GETARG_JSONB_P(EXPRESSIONS_ARG),
     737              :                                    &ok);
     738              : 
     739          177 :         table_close(pgsd, RowExclusiveLock);
     740              : 
     741          177 :         if (ok)
     742              :         {
     743              :             Assert(datum != (Datum) 0);
     744           37 :             values[Anum_pg_statistic_ext_data_stxdexpr - 1] = datum;
     745           37 :             replaces[Anum_pg_statistic_ext_data_stxdexpr - 1] = true;
     746           37 :             nulls[Anum_pg_statistic_ext_data_stxdexpr - 1] = false;
     747              :         }
     748              :         else
     749          140 :             success = false;
     750              :     }
     751              : 
     752          268 :     upsert_pg_statistic_ext_data(values, nulls, replaces);
     753              : 
     754          280 : cleanup:
     755          280 :     if (HeapTupleIsValid(tup))
     756          272 :         heap_freetuple(tup);
     757          280 :     if (pg_stext != NULL)
     758          276 :         table_close(pg_stext, RowExclusiveLock);
     759          280 :     if (atttypids != NULL)
     760          204 :         pfree(atttypids);
     761          280 :     if (atttypmods != NULL)
     762          204 :         pfree(atttypmods);
     763          280 :     if (atttypcolls != NULL)
     764          204 :         pfree(atttypcolls);
     765          280 :     return success;
     766              : }
     767              : 
     768              : /*
     769              :  * Consistency checks to ensure that other mcvlist arrays are in alignment
     770              :  * with the mcv array.
     771              :  */
     772              : static bool
     773           58 : check_mcvlist_array(const ArrayType *arr, int argindex, int required_ndims,
     774              :                     int mcv_length)
     775              : {
     776           58 :     if (ARR_NDIM(arr) != required_ndims)
     777              :     {
     778            0 :         ereport(WARNING,
     779              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     780              :                 errmsg("could not parse array \"%s\": incorrect number of dimensions (%d required)",
     781              :                        extarginfo[argindex].argname, required_ndims));
     782            0 :         return false;
     783              :     }
     784              : 
     785           58 :     if (array_contains_nulls(arr))
     786              :     {
     787            0 :         ereport(WARNING,
     788              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     789              :                 errmsg("could not parse array \"%s\": NULL value found",
     790              :                        extarginfo[argindex].argname));
     791            0 :         return false;
     792              :     }
     793              : 
     794           58 :     if (ARR_DIMS(arr)[0] != mcv_length)
     795              :     {
     796            8 :         ereport(WARNING,
     797              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     798              :                 errmsg("could not parse array \"%s\": incorrect number of elements (same as \"%s\" required)",
     799              :                        extarginfo[argindex].argname,
     800              :                        extarginfo[MOST_COMMON_VALS_ARG].argname));
     801            8 :         return false;
     802              :     }
     803              : 
     804           50 :     return true;
     805              : }
     806              : 
     807              : /*
     808              :  * Create the stxdmcv datum from the equal-sized arrays of most common values,
     809              :  * their null flags, and the frequency and base frequency associated with
     810              :  * each value.
     811              :  */
     812              : static Datum
     813           39 : import_mcv(const ArrayType *mcv_arr, const ArrayType *freqs_arr,
     814              :            const ArrayType *base_freqs_arr, Oid *atttypids, int32 *atttypmods,
     815              :            Oid *atttypcolls, int numattrs, bool *ok)
     816              : {
     817              :     int         nitems;
     818              :     Datum      *mcv_elems;
     819              :     bool       *mcv_nulls;
     820              :     int         check_nummcv;
     821           39 :     Datum       mcv = (Datum) 0;
     822              : 
     823           39 :     *ok = false;
     824              : 
     825              :     /*
     826              :      * mcv_arr is an array of arrays.  Each inner array must have the same
     827              :      * number of elements "numattrs".
     828              :      */
     829           39 :     if (ARR_NDIM(mcv_arr) != 2)
     830              :     {
     831            4 :         ereport(WARNING,
     832              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     833              :                 errmsg("could not parse array \"%s\": incorrect number of dimensions (%d required)",
     834              :                        extarginfo[MOST_COMMON_VALS_ARG].argname, 2));
     835            4 :         goto mcv_error;
     836              :     }
     837              : 
     838           35 :     if (ARR_DIMS(mcv_arr)[1] != numattrs)
     839              :     {
     840            4 :         ereport(WARNING,
     841              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     842              :                 errmsg("could not parse array \"%s\": found %d attributes but expected %d",
     843              :                        extarginfo[MOST_COMMON_VALS_ARG].argname,
     844              :                        ARR_DIMS(mcv_arr)[1], numattrs));
     845            4 :         goto mcv_error;
     846              :     }
     847              : 
     848              :     /*
     849              :      * "most_common_freqs" and "most_common_base_freqs" arrays must be of the
     850              :      * same length, one-dimension and cannot contain NULLs.  We use mcv_arr as
     851              :      * the reference array for determining their length.
     852              :      */
     853           31 :     nitems = ARR_DIMS(mcv_arr)[0];
     854           31 :     if (!check_mcvlist_array(freqs_arr, MOST_COMMON_FREQS_ARG, 1, nitems) ||
     855           27 :         !check_mcvlist_array(base_freqs_arr, MOST_COMMON_BASE_FREQS_ARG, 1, nitems))
     856              :     {
     857              :         /* inconsistent input arrays found */
     858            8 :         goto mcv_error;
     859              :     }
     860              : 
     861              :     /*
     862              :      * This part builds the contents for "most_common_val_nulls", based on the
     863              :      * values from "most_common_vals".
     864              :      */
     865           23 :     deconstruct_array_builtin(mcv_arr, TEXTOID, &mcv_elems,
     866              :                               &mcv_nulls, &check_nummcv);
     867              : 
     868           23 :     mcv = statext_mcv_import(WARNING, numattrs,
     869              :                              atttypids, atttypmods, atttypcolls,
     870              :                              nitems, mcv_elems, mcv_nulls,
     871           23 :                              (float8 *) ARR_DATA_PTR(freqs_arr),
     872           23 :                              (float8 *) ARR_DATA_PTR(base_freqs_arr));
     873              : 
     874           23 :     *ok = (mcv != (Datum) 0);
     875              : 
     876           39 : mcv_error:
     877           39 :     return mcv;
     878              : }
     879              : 
     880              : /*
     881              :  * Check if key is found in the list of expression argnames.
     882              :  */
     883              : static bool
     884          674 : key_in_expr_argnames(JsonbValue *key)
     885              : {
     886              :     Assert(key->type == jbvString);
     887         3962 :     for (int i = 0; i < NUM_ATTRIBUTE_STATS_ELEMS; i++)
     888              :     {
     889         3954 :         if (strlen(extexprargname[i]) == key->val.string.len &&
     890          945 :             strncmp(extexprargname[i], key->val.string.val, key->val.string.len) == 0)
     891          666 :             return true;
     892              :     }
     893            8 :     return false;
     894              : }
     895              : 
     896              : /*
     897              :  * Verify that all of the keys in the object are valid argnames.
     898              :  */
     899              : static bool
     900          219 : check_all_expr_argnames_valid(JsonbContainer *cont, AttrNumber exprnum)
     901              : {
     902          219 :     bool        all_keys_valid = true;
     903              : 
     904              :     JsonbIterator *jbit;
     905              :     JsonbIteratorToken jitok;
     906              :     JsonbValue  jkey;
     907              : 
     908              :     Assert(JsonContainerIsObject(cont));
     909              : 
     910          219 :     jbit = JsonbIteratorInit(cont);
     911              : 
     912              :     /* We always start off with a BEGIN OBJECT */
     913          219 :     jitok = JsonbIteratorNext(&jbit, &jkey, false);
     914              :     Assert(jitok == WJB_BEGIN_OBJECT);
     915              : 
     916              :     while (true)
     917          710 :     {
     918              :         JsonbValue  jval;
     919              : 
     920          929 :         jitok = JsonbIteratorNext(&jbit, &jkey, false);
     921              : 
     922              :         /*
     923              :          * We have run of keys. This is the only condition where it is
     924              :          * memory-safe to break out of the loop.
     925              :          */
     926          929 :         if (jitok == WJB_END_OBJECT)
     927          219 :             break;
     928              : 
     929              :         /* We can only find keys inside an object */
     930              :         Assert(jitok == WJB_KEY);
     931              :         Assert(jkey.type == jbvString);
     932              : 
     933              :         /* A value must follow the key */
     934          710 :         jitok = JsonbIteratorNext(&jbit, &jval, false);
     935              :         Assert(jitok == WJB_VALUE);
     936              : 
     937              :         /*
     938              :          * If we have already found an invalid key, there is no point in
     939              :          * looking for more, because additional WARNINGs are just clutter. We
     940              :          * must continue iterating over the json to ensure that we clean up
     941              :          * all allocated memory.
     942              :          */
     943          710 :         if (!all_keys_valid)
     944           36 :             continue;
     945              : 
     946          674 :         if (!key_in_expr_argnames(&jkey))
     947              :         {
     948            8 :             char       *bad_element_name = jbv_string_get_cstr(&jkey);
     949              : 
     950            8 :             ereport(WARNING,
     951              :                     errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     952              :                     errmsg("could not import element in expression %d: invalid key name",
     953              :                            exprnum));
     954              : 
     955            8 :             pfree(bad_element_name);
     956            8 :             all_keys_valid = false;
     957              :         }
     958              :     }
     959          219 :     return all_keys_valid;
     960              : }
     961              : 
     962              : /*
     963              :  * Simple conversion of jbvString to cstring
     964              :  */
     965              : static char *
     966          466 : jbv_string_get_cstr(JsonbValue *jval)
     967              : {
     968              :     char       *s;
     969              : 
     970              :     Assert(jval->type == jbvString);
     971              : 
     972          466 :     s = palloc0(jval->val.string.len + 1);
     973          466 :     memcpy(s, jval->val.string.val, jval->val.string.len);
     974              : 
     975          466 :     return s;
     976              : }
     977              : 
     978              : /*
     979              :  * Apply a jbvString value to a safe scalar input function.
     980              :  */
     981              : static bool
     982          232 : jbv_to_infunc_datum(JsonbValue *jval, PGFunction func, AttrNumber exprnum,
     983              :                     const char *argname, Datum *datum)
     984              : {
     985          232 :     ErrorSaveContext escontext = {
     986              :         .type = T_ErrorSaveContext,
     987              :         .details_wanted = true
     988              :     };
     989              : 
     990          232 :     char       *s = jbv_string_get_cstr(jval);
     991              :     bool        ok;
     992              : 
     993          232 :     ok = DirectInputFunctionCallSafe(func, s, InvalidOid, -1,
     994              :                                      (Node *) &escontext, datum);
     995              : 
     996              :     /*
     997              :      * If we got a type import error, use the report generated and add an
     998              :      * error hint before throwing a warning.
     999              :      */
    1000          232 :     if (!ok)
    1001              :     {
    1002              :         StringInfoData hint_str;
    1003              : 
    1004           16 :         initStringInfo(&hint_str);
    1005           16 :         appendStringInfo(&hint_str,
    1006              :                          "Element \"%s\" in expression %d could not be parsed.",
    1007              :                          argname, exprnum);
    1008              : 
    1009           16 :         escontext.error_data->elevel = WARNING;
    1010           16 :         escontext.error_data->hint = hint_str.data;
    1011              : 
    1012           16 :         ThrowErrorData(escontext.error_data);
    1013           16 :         pfree(hint_str.data);
    1014              :     }
    1015              : 
    1016          232 :     pfree(s);
    1017          232 :     return ok;
    1018              : }
    1019              : 
    1020              : /*
    1021              :  * Build an array datum with element type elemtypid from a text datum, used as
    1022              :  * value of an attribute in a pg_statistic tuple.
    1023              :  *
    1024              :  * If an error is encountered, capture it, and reduce the elevel to WARNING.
    1025              :  *
    1026              :  * This is an adaptation of statatt_build_stavalues().
    1027              :  */
    1028              : static Datum
    1029          226 : array_in_safe(FmgrInfo *array_in, const char *s, Oid typid, int32 typmod,
    1030              :               AttrNumber exprnum, const char *element_name, bool *ok)
    1031              : {
    1032          226 :     LOCAL_FCINFO(fcinfo, 3);
    1033              :     Datum       result;
    1034              : 
    1035          226 :     ErrorSaveContext escontext = {
    1036              :         .type = T_ErrorSaveContext,
    1037              :         .details_wanted = true
    1038              :     };
    1039              : 
    1040          226 :     *ok = false;
    1041          226 :     InitFunctionCallInfoData(*fcinfo, array_in, 3, InvalidOid,
    1042              :                              (Node *) &escontext, NULL);
    1043              : 
    1044          226 :     fcinfo->args[0].value = CStringGetDatum(s);
    1045          226 :     fcinfo->args[0].isnull = false;
    1046          226 :     fcinfo->args[1].value = ObjectIdGetDatum(typid);
    1047          226 :     fcinfo->args[1].isnull = false;
    1048          226 :     fcinfo->args[2].value = Int32GetDatum(typmod);
    1049          226 :     fcinfo->args[2].isnull = false;
    1050              : 
    1051          226 :     result = FunctionCallInvoke(fcinfo);
    1052              : 
    1053              :     /*
    1054              :      * If the array_in function returned an error, we will want to report that
    1055              :      * ERROR as a WARNING, and add some location context to the error message.
    1056              :      * Overwriting the existing hint (if any) is not ideal, and an error
    1057              :      * context would only work for level >= ERROR.
    1058              :      */
    1059          226 :     if (escontext.error_occurred)
    1060              :     {
    1061              :         StringInfoData hint_str;
    1062              : 
    1063           24 :         initStringInfo(&hint_str);
    1064           24 :         appendStringInfo(&hint_str,
    1065              :                          "Element \"%s\" in expression %d could not be parsed.",
    1066              :                          element_name, exprnum);
    1067           24 :         escontext.error_data->elevel = WARNING;
    1068           24 :         escontext.error_data->hint = hint_str.data;
    1069           24 :         ThrowErrorData(escontext.error_data);
    1070           24 :         pfree(hint_str.data);
    1071           24 :         return (Datum) 0;
    1072              :     }
    1073              : 
    1074          202 :     if (ARR_NDIM(DatumGetArrayTypeP(result)) != 1)
    1075              :     {
    1076            4 :         ereport(WARNING,
    1077              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1078              :                  errmsg("could not import element \"%s\" in expression %d: must be a one-dimensional array",
    1079              :                         element_name, exprnum)));
    1080            4 :         return (Datum) 0;
    1081              :     }
    1082              : 
    1083          198 :     if (array_contains_nulls(DatumGetArrayTypeP(result)))
    1084              :     {
    1085            0 :         ereport(WARNING,
    1086              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1087              :                 errmsg("could not import element \"%s\" in expression %d: null value found",
    1088              :                        element_name, exprnum));
    1089            0 :         return (Datum) 0;
    1090              :     }
    1091              : 
    1092          198 :     *ok = true;
    1093          198 :     return result;
    1094              : }
    1095              : 
    1096              : /*
    1097              :  * Create a pg_statistic tuple from an expression JSONB container.
    1098              :  *
    1099              :  * The pg_statistic tuple is pre-populated with acceptable defaults, therefore
    1100              :  * even if there is an issue with all of the keys in the container, we can
    1101              :  * still return a legit tuple datum.
    1102              :  *
    1103              :  * Set pg_statistic_ok to true if all of the values found in the container
    1104              :  * were imported without issue.  pg_statistic_ok is switched to "true" once
    1105              :  * the full pg_statistic tuple has been built and validated.
    1106              :  */
    1107              : static Datum
    1108          227 : import_pg_statistic(Relation pgsd, JsonbContainer *cont,
    1109              :                     AttrNumber exprnum, FmgrInfo *array_in_fn,
    1110              :                     Oid typid, int32 typmod, Oid typcoll,
    1111              :                     bool *pg_statistic_ok)
    1112              : {
    1113          227 :     const char *argname = extarginfo[EXPRESSIONS_ARG].argname;
    1114              :     TypeCacheEntry *typcache;
    1115              :     Datum       values[Natts_pg_statistic];
    1116              :     bool        nulls[Natts_pg_statistic];
    1117              :     bool        replaces[Natts_pg_statistic];
    1118          227 :     HeapTuple   pgstup = NULL;
    1119          227 :     Datum       pgstdat = (Datum) 0;
    1120          227 :     Oid         elemtypid = InvalidOid;
    1121          227 :     Oid         elemeqopr = InvalidOid;
    1122          227 :     bool        found[NUM_ATTRIBUTE_STATS_ELEMS] = {0};
    1123          227 :     JsonbValue  val[NUM_ATTRIBUTE_STATS_ELEMS] = {0};
    1124              : 
    1125              :     /* Assume the worst by default. */
    1126          227 :     *pg_statistic_ok = false;
    1127              : 
    1128          227 :     if (!JsonContainerIsObject(cont))
    1129              :     {
    1130            4 :         ereport(WARNING,
    1131              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1132              :                 errmsg("could not parse \"%s\": invalid element in expression %d",
    1133              :                        argname, exprnum));
    1134            4 :         goto pg_statistic_error;
    1135              :     }
    1136              : 
    1137              :     /*
    1138              :      * Loop through all keys that we need to look up.  If any value found is
    1139              :      * neither a string nor a NULL, there is not much we can do, so just give
    1140              :      * on the entire tuple for this expression.
    1141              :      */
    1142         3070 :     for (int i = 0; i < NUM_ATTRIBUTE_STATS_ELEMS; i++)
    1143              :     {
    1144         2851 :         const char *s = extexprargname[i];
    1145         2851 :         int         len = strlen(s);
    1146              : 
    1147         2851 :         if (getKeyJsonValueFromContainer(cont, s, len, &val[i]) == NULL)
    1148         2145 :             continue;
    1149              : 
    1150          706 :         switch (val[i].type)
    1151              :         {
    1152          582 :             case jbvString:
    1153          582 :                 found[i] = true;
    1154          582 :                 break;
    1155              : 
    1156          120 :             case jbvNull:
    1157          120 :                 break;
    1158              : 
    1159            4 :             default:
    1160            4 :                 ereport(WARNING,
    1161              :                         errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1162              :                         errmsg("could not parse \"%s\": invalid element in expression %d", argname, exprnum),
    1163              :                         errhint("Value of element \"%s\" must be type a null or a string.", s));
    1164            4 :                 goto pg_statistic_error;
    1165              :         }
    1166              :     }
    1167              : 
    1168              :     /* Look for invalid keys */
    1169          219 :     if (!check_all_expr_argnames_valid(cont, exprnum))
    1170            8 :         goto pg_statistic_error;
    1171              : 
    1172              :     /*
    1173              :      * There are two arg pairs, MCV+MCF and MCEV+MCEF.  Both values must
    1174              :      * either be found or not be found.  Any disagreement is a warning.  Once
    1175              :      * we have ruled out disagreeing pairs, we can use either found flag as a
    1176              :      * proxy for the other.
    1177              :      */
    1178          211 :     if (found[MOST_COMMON_VALS_ELEM] != found[MOST_COMMON_FREQS_ELEM])
    1179              :     {
    1180           16 :         ereport(WARNING,
    1181              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1182              :                 errmsg("could not parse \"%s\": invalid element in expression %d",
    1183              :                        argname, exprnum),
    1184              :                 errhint("\"%s\" and \"%s\" must be both either strings or nulls.",
    1185              :                         extexprargname[MOST_COMMON_VALS_ELEM],
    1186              :                         extexprargname[MOST_COMMON_FREQS_ELEM]));
    1187           16 :         goto pg_statistic_error;
    1188              :     }
    1189          195 :     if (found[MOST_COMMON_ELEMS_ELEM] != found[MOST_COMMON_ELEM_FREQS_ELEM])
    1190              :     {
    1191           16 :         ereport(WARNING,
    1192              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1193              :                 errmsg("could not parse \"%s\": invalid element in expression %d",
    1194              :                        argname, exprnum),
    1195              :                 errhint("\"%s\" and \"%s\" must be both either strings or nulls.",
    1196              :                         extexprargname[MOST_COMMON_ELEMS_ELEM],
    1197              :                         extexprargname[MOST_COMMON_ELEM_FREQS_ELEM]));
    1198           16 :         goto pg_statistic_error;
    1199              :     }
    1200              : 
    1201              :     /*
    1202              :      * Range types may expect three values to be set.  All three of them must
    1203              :      * either be found or not be found.  Any disagreement is a warning.
    1204              :      */
    1205          179 :     if (found[RANGE_LENGTH_HISTOGRAM_ELEM] != found[RANGE_EMPTY_FRAC_ELEM] ||
    1206          171 :         found[RANGE_LENGTH_HISTOGRAM_ELEM] != found[RANGE_BOUNDS_HISTOGRAM_ELEM])
    1207              :     {
    1208           16 :         ereport(WARNING,
    1209              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1210              :                 errmsg("could not parse \"%s\": invalid element in expression %d",
    1211              :                        argname, exprnum),
    1212              :                 errhint("\"%s\", \"%s\", and \"%s\" must be all either strings or all nulls.",
    1213              :                         extexprargname[RANGE_LENGTH_HISTOGRAM_ELEM],
    1214              :                         extexprargname[RANGE_EMPTY_FRAC_ELEM],
    1215              :                         extexprargname[RANGE_BOUNDS_HISTOGRAM_ELEM]));
    1216           16 :         goto pg_statistic_error;
    1217              :     }
    1218              : 
    1219              :     /* This finds the right operators even if atttypid is a domain */
    1220          163 :     typcache = lookup_type_cache(typid, TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR);
    1221              : 
    1222          163 :     statatt_init_empty_tuple(InvalidOid, InvalidAttrNumber, false,
    1223              :                              values, nulls, replaces);
    1224              : 
    1225              :     /*
    1226              :      * Special case: collation for tsvector is DEFAULT_COLLATION_OID. See
    1227              :      * compute_tsvector_stats().
    1228              :      */
    1229          163 :     if (typid == TSVECTOROID)
    1230            4 :         typcoll = DEFAULT_COLLATION_OID;
    1231              : 
    1232              :     /*
    1233              :      * We only need to fetch element type and eq operator if we have a stat of
    1234              :      * type MCELEM or DECHIST, otherwise the values are unnecessary and not
    1235              :      * meaningful.
    1236              :      */
    1237          163 :     if (found[MOST_COMMON_ELEMS_ELEM] || found[ELEM_COUNT_HISTOGRAM_ELEM])
    1238              :     {
    1239           28 :         if (!statatt_get_elem_type(typid, typcache->typtype,
    1240              :                                    &elemtypid, &elemeqopr))
    1241              :         {
    1242            8 :             ereport(WARNING,
    1243              :                     errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1244              :                     errmsg("could not parse \"%s\": invalid element type in expression %d",
    1245              :                            argname, exprnum));
    1246            8 :             goto pg_statistic_error;
    1247              :         }
    1248              :     }
    1249              : 
    1250              :     /*
    1251              :      * These three fields can only be set if dealing with a range or
    1252              :      * multi-range type.
    1253              :      */
    1254          155 :     if (found[RANGE_LENGTH_HISTOGRAM_ELEM] ||
    1255          143 :         found[RANGE_EMPTY_FRAC_ELEM] ||
    1256          143 :         found[RANGE_BOUNDS_HISTOGRAM_ELEM])
    1257              :     {
    1258           12 :         if (typcache->typtype != TYPTYPE_RANGE &&
    1259           12 :             typcache->typtype != TYPTYPE_MULTIRANGE)
    1260              :         {
    1261            4 :             ereport(WARNING,
    1262              :                     errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1263              :                     errmsg("could not parse \"%s\": invalid data in expression %d",
    1264              :                            argname, exprnum),
    1265              :                     errhint("\"%s\", \"%s\", and \"%s\" can only be set for a range type.",
    1266              :                             extexprargname[RANGE_LENGTH_HISTOGRAM_ELEM],
    1267              :                             extexprargname[RANGE_EMPTY_FRAC_ELEM],
    1268              :                             extexprargname[RANGE_BOUNDS_HISTOGRAM_ELEM]));
    1269            4 :             goto pg_statistic_error;
    1270              :         }
    1271              :     }
    1272              : 
    1273              :     /* null_frac */
    1274          151 :     if (found[NULL_FRAC_ELEM])
    1275              :     {
    1276              :         Datum       datum;
    1277              : 
    1278           63 :         if (jbv_to_infunc_datum(&val[NULL_FRAC_ELEM], float4in, exprnum,
    1279              :                                 extexprargname[NULL_FRAC_ELEM], &datum))
    1280           59 :             values[Anum_pg_statistic_stanullfrac - 1] = datum;
    1281              :         else
    1282            4 :             goto pg_statistic_error;
    1283              :     }
    1284              : 
    1285              :     /* avg_width */
    1286          147 :     if (found[AVG_WIDTH_ELEM])
    1287              :     {
    1288              :         Datum       datum;
    1289              : 
    1290           59 :         if (jbv_to_infunc_datum(&val[AVG_WIDTH_ELEM], int4in, exprnum,
    1291              :                                 extexprargname[AVG_WIDTH_ELEM], &datum))
    1292           55 :             values[Anum_pg_statistic_stawidth - 1] = datum;
    1293              :         else
    1294            4 :             goto pg_statistic_error;
    1295              :     }
    1296              : 
    1297              :     /* n_distinct */
    1298          143 :     if (found[N_DISTINCT_ELEM])
    1299              :     {
    1300              :         Datum       datum;
    1301              : 
    1302           59 :         if (jbv_to_infunc_datum(&val[N_DISTINCT_ELEM], float4in, exprnum,
    1303              :                                 extexprargname[N_DISTINCT_ELEM], &datum))
    1304           55 :             values[Anum_pg_statistic_stadistinct - 1] = datum;
    1305              :         else
    1306            4 :             goto pg_statistic_error;
    1307              :     }
    1308              : 
    1309              :     /*
    1310              :      * The STAKIND statistics are the same as the ones found in attribute
    1311              :      * stats.  However, these are all derived from json strings, whereas the
    1312              :      * ones derived for attribute stats are a mix of datatypes.  This limits
    1313              :      * the opportunities for code sharing between the two.
    1314              :      *
    1315              :      * Some statistic kinds have both a stanumbers and a stavalues components.
    1316              :      * In those cases, both values must either be NOT NULL or both NULL, and
    1317              :      * if they aren't then we need to reject that stakind completely.
    1318              :      * Currently we go a step further and reject the expression array
    1319              :      * completely.
    1320              :      */
    1321              : 
    1322          139 :     if (found[MOST_COMMON_VALS_ELEM])
    1323              :     {
    1324              :         Datum       stavalues;
    1325              :         Datum       stanumbers;
    1326           75 :         bool        val_ok = false;
    1327           75 :         bool        num_ok = false;
    1328              :         char       *s;
    1329              : 
    1330           75 :         s = jbv_string_get_cstr(&val[MOST_COMMON_VALS_ELEM]);
    1331           75 :         stavalues = array_in_safe(array_in_fn, s, typid, typmod, exprnum,
    1332              :                                   extexprargname[MOST_COMMON_VALS_ELEM],
    1333              :                                   &val_ok);
    1334              : 
    1335           75 :         pfree(s);
    1336              : 
    1337           75 :         s = jbv_string_get_cstr(&val[MOST_COMMON_FREQS_ELEM]);
    1338           75 :         stanumbers = array_in_safe(array_in_fn, s, FLOAT4OID, -1, exprnum,
    1339              :                                    extexprargname[MOST_COMMON_FREQS_ELEM],
    1340              :                                    &num_ok);
    1341           75 :         pfree(s);
    1342              : 
    1343              :         /* Only set the slot if both datums have been built */
    1344           75 :         if (val_ok && num_ok)
    1345           59 :         {
    1346           63 :             ArrayType  *vals_arr = DatumGetArrayTypeP(stavalues);
    1347           63 :             ArrayType  *nums_arr = DatumGetArrayTypeP(stanumbers);
    1348           63 :             int         nvals = ARR_DIMS(vals_arr)[0];
    1349           63 :             int         nnums = ARR_DIMS(nums_arr)[0];
    1350              : 
    1351           63 :             if (nvals != nnums)
    1352              :             {
    1353            4 :                 ereport(WARNING,
    1354              :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1355              :                          errmsg("could not parse \"%s\": incorrect number of elements (same as \"%s\" required)",
    1356              :                                 "most_common_vals",
    1357              :                                 "most_common_freqs")));
    1358           16 :                 goto pg_statistic_error;
    1359              :             }
    1360              : 
    1361           59 :             statatt_set_slot(values, nulls, replaces,
    1362              :                              STATISTIC_KIND_MCV,
    1363              :                              typcache->eq_opr, typcoll,
    1364              :                              stanumbers, false, stavalues, false);
    1365              :         }
    1366              :         else
    1367           12 :             goto pg_statistic_error;
    1368              :     }
    1369              : 
    1370              :     /* STATISTIC_KIND_HISTOGRAM */
    1371          123 :     if (found[HISTOGRAM_BOUNDS_ELEM])
    1372              :     {
    1373              :         Datum       stavalues;
    1374           20 :         bool        val_ok = false;
    1375           20 :         char       *s = jbv_string_get_cstr(&val[HISTOGRAM_BOUNDS_ELEM]);
    1376              : 
    1377           20 :         stavalues = array_in_safe(array_in_fn, s, typid, typmod, exprnum,
    1378              :                                   extexprargname[HISTOGRAM_BOUNDS_ELEM],
    1379              :                                   &val_ok);
    1380           20 :         pfree(s);
    1381              : 
    1382           20 :         if (val_ok)
    1383           16 :             statatt_set_slot(values, nulls, replaces,
    1384              :                              STATISTIC_KIND_HISTOGRAM,
    1385              :                              typcache->lt_opr, typcoll,
    1386              :                              0, true, stavalues, false);
    1387              :         else
    1388            4 :             goto pg_statistic_error;
    1389              :     }
    1390              : 
    1391              :     /* STATISTIC_KIND_CORRELATION */
    1392          119 :     if (found[CORRELATION_ELEM])
    1393              :     {
    1394           43 :         Datum       corr[] = {(Datum) 0};
    1395              : 
    1396           43 :         if (jbv_to_infunc_datum(&val[CORRELATION_ELEM], float4in, exprnum,
    1397              :                                 extexprargname[CORRELATION_ELEM], &corr[0]))
    1398              :         {
    1399           39 :             ArrayType  *arry = construct_array_builtin(corr, 1, FLOAT4OID);
    1400           39 :             Datum       stanumbers = PointerGetDatum(arry);
    1401              : 
    1402           39 :             statatt_set_slot(values, nulls, replaces,
    1403              :                              STATISTIC_KIND_CORRELATION,
    1404              :                              typcache->lt_opr, typcoll,
    1405              :                              stanumbers, false, 0, true);
    1406              :         }
    1407              :         else
    1408            4 :             goto pg_statistic_error;
    1409              :     }
    1410              : 
    1411              :     /* STATISTIC_KIND_MCELEM */
    1412          115 :     if (found[MOST_COMMON_ELEMS_ELEM])
    1413              :     {
    1414              :         Datum       stavalues;
    1415              :         Datum       stanumbers;
    1416           16 :         bool        val_ok = false;
    1417           16 :         bool        num_ok = false;
    1418              :         char       *s;
    1419              : 
    1420           16 :         s = jbv_string_get_cstr(&val[MOST_COMMON_ELEMS_ELEM]);
    1421           16 :         stavalues = array_in_safe(array_in_fn, s, elemtypid, typmod, exprnum,
    1422              :                                   extexprargname[MOST_COMMON_ELEMS_ELEM],
    1423              :                                   &val_ok);
    1424           16 :         pfree(s);
    1425              : 
    1426              : 
    1427           16 :         s = jbv_string_get_cstr(&val[MOST_COMMON_ELEM_FREQS_ELEM]);
    1428           16 :         stanumbers = array_in_safe(array_in_fn, s, FLOAT4OID, -1, exprnum,
    1429              :                                    extexprargname[MOST_COMMON_ELEM_FREQS_ELEM],
    1430              :                                    &num_ok);
    1431           16 :         pfree(s);
    1432              : 
    1433              :         /* Only set the slot if both datums have been built */
    1434           16 :         if (val_ok && num_ok)
    1435            8 :             statatt_set_slot(values, nulls, replaces,
    1436              :                              STATISTIC_KIND_MCELEM,
    1437              :                              elemeqopr, typcoll,
    1438              :                              stanumbers, false, stavalues, false);
    1439              :         else
    1440            8 :             goto pg_statistic_error;
    1441              :     }
    1442              : 
    1443              :     /* STATISTIC_KIND_DECHIST */
    1444          107 :     if (found[ELEM_COUNT_HISTOGRAM_ELEM])
    1445              :     {
    1446              :         Datum       stanumbers;
    1447            8 :         bool        num_ok = false;
    1448              :         char       *s;
    1449              : 
    1450            8 :         s = jbv_string_get_cstr(&val[ELEM_COUNT_HISTOGRAM_ELEM]);
    1451            8 :         stanumbers = array_in_safe(array_in_fn, s, FLOAT4OID, -1, exprnum,
    1452              :                                    extexprargname[ELEM_COUNT_HISTOGRAM_ELEM],
    1453              :                                    &num_ok);
    1454            8 :         pfree(s);
    1455              : 
    1456            8 :         if (num_ok)
    1457            4 :             statatt_set_slot(values, nulls, replaces, STATISTIC_KIND_DECHIST,
    1458              :                              elemeqopr, typcoll, stanumbers, false, 0, true);
    1459              :         else
    1460            4 :             goto pg_statistic_error;
    1461              :     }
    1462              : 
    1463              :     /*
    1464              :      * STATISTIC_KIND_BOUNDS_HISTOGRAM
    1465              :      *
    1466              :      * This stakind appears before STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM even
    1467              :      * though it is numerically greater, and all other stakinds appear in
    1468              :      * numerical order.
    1469              :      */
    1470          103 :     if (found[RANGE_BOUNDS_HISTOGRAM_ELEM])
    1471              :     {
    1472              :         Datum       stavalues;
    1473            8 :         bool        val_ok = false;
    1474              :         char       *s;
    1475            8 :         Oid         rtypid = typid;
    1476              : 
    1477              :         /*
    1478              :          * If it's a multirange, step down to the range type, as is done by
    1479              :          * multirange_typanalyze().
    1480              :          */
    1481            8 :         if (type_is_multirange(typid))
    1482            8 :             rtypid = get_multirange_range(typid);
    1483              : 
    1484            8 :         s = jbv_string_get_cstr(&val[RANGE_BOUNDS_HISTOGRAM_ELEM]);
    1485              : 
    1486            8 :         stavalues = array_in_safe(array_in_fn, s, rtypid, typmod, exprnum,
    1487              :                                   extexprargname[RANGE_BOUNDS_HISTOGRAM_ELEM],
    1488              :                                   &val_ok);
    1489              : 
    1490            8 :         if (val_ok)
    1491            8 :             statatt_set_slot(values, nulls, replaces,
    1492              :                              STATISTIC_KIND_BOUNDS_HISTOGRAM,
    1493              :                              InvalidOid, InvalidOid,
    1494              :                              0, true, stavalues, false);
    1495              :         else
    1496            0 :             goto pg_statistic_error;
    1497              :     }
    1498              : 
    1499              :     /* STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM */
    1500          103 :     if (found[RANGE_LENGTH_HISTOGRAM_ELEM])
    1501              :     {
    1502            8 :         Datum       empty_frac[] = {(Datum) 0};
    1503              :         Datum       stavalues;
    1504              :         Datum       stanumbers;
    1505            8 :         bool        val_ok = false;
    1506              :         char       *s;
    1507              : 
    1508            8 :         if (jbv_to_infunc_datum(&val[RANGE_EMPTY_FRAC_ELEM], float4in, exprnum,
    1509              :                                 extexprargname[RANGE_EMPTY_FRAC_ELEM], &empty_frac[0]))
    1510              :         {
    1511            8 :             ArrayType  *arry = construct_array_builtin(empty_frac, 1, FLOAT4OID);
    1512              : 
    1513            8 :             stanumbers = PointerGetDatum(arry);
    1514              :         }
    1515              :         else
    1516            0 :             goto pg_statistic_error;
    1517              : 
    1518            8 :         s = jbv_string_get_cstr(&val[RANGE_LENGTH_HISTOGRAM_ELEM]);
    1519            8 :         stavalues = array_in_safe(array_in_fn, s, FLOAT8OID, -1, exprnum,
    1520              :                                   extexprargname[RANGE_LENGTH_HISTOGRAM_ELEM],
    1521              :                                   &val_ok);
    1522              : 
    1523            8 :         if (val_ok)
    1524            8 :             statatt_set_slot(values, nulls, replaces,
    1525              :                              STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
    1526              :                              Float8LessOperator, InvalidOid,
    1527              :                              stanumbers, false, stavalues, false);
    1528              :         else
    1529            0 :             goto pg_statistic_error;
    1530              :     }
    1531              : 
    1532          103 :     pgstup = heap_form_tuple(RelationGetDescr(pgsd), values, nulls);
    1533          103 :     pgstdat = heap_copy_tuple_as_datum(pgstup, RelationGetDescr(pgsd));
    1534              : 
    1535          103 :     heap_freetuple(pgstup);
    1536              : 
    1537          103 :     *pg_statistic_ok = true;
    1538              : 
    1539          103 :     return pgstdat;
    1540              : 
    1541          124 : pg_statistic_error:
    1542          124 :     return (Datum) 0;
    1543              : }
    1544              : 
    1545              : /*
    1546              :  * Create the stxdexpr datum, which is an array of pg_statistic rows with all
    1547              :  * of the object identification fields left at defaults, using the json array
    1548              :  * of objects/nulls referenced against the datatypes for the expressions.
    1549              :  *
    1550              :  * The exprs_is_perfect will be set to true if all pg_statistic rows were
    1551              :  * imported cleanly.  If any of them experienced a problem (and thus were
    1552              :  * set as if they were null), then the expression is kept but exprs_is_perfect
    1553              :  * will be marked as false.
    1554              :  *
    1555              :  * This datum is needed to fill out a complete pg_statistic_ext_data tuple.
    1556              :  */
    1557              : static Datum
    1558          177 : import_expressions(Relation pgsd, int numexprs,
    1559              :                    Oid *atttypids, int32 *atttypmods,
    1560              :                    Oid *atttypcolls, Jsonb *exprs_jsonb,
    1561              :                    bool *exprs_is_perfect)
    1562              : {
    1563          177 :     const char *argname = extarginfo[EXPRESSIONS_ARG].argname;
    1564          177 :     Oid         pgstypoid = get_rel_type_id(StatisticRelationId);
    1565          177 :     ArrayBuildState *astate = NULL;
    1566          177 :     Datum       result = (Datum) 0;
    1567          177 :     int         num_import_ok = 0;
    1568              :     JsonbContainer *root;
    1569              :     int         num_root_elements;
    1570              : 
    1571              :     FmgrInfo    array_in_fn;
    1572              : 
    1573          177 :     *exprs_is_perfect = false;
    1574              : 
    1575              :     /* Json schema must be [{expr},...] */
    1576          177 :     if (!JB_ROOT_IS_ARRAY(exprs_jsonb))
    1577              :     {
    1578            4 :         ereport(WARNING,
    1579              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1580              :                 errmsg("could not parse \"%s\": root-level array required", argname));
    1581            4 :         goto exprs_error;
    1582              :     }
    1583              : 
    1584          173 :     root = &exprs_jsonb->root;
    1585              : 
    1586              :     /*
    1587              :      * The number of elements in the array must match the number of
    1588              :      * expressions in the stats object definition.
    1589              :      */
    1590          173 :     num_root_elements = JsonContainerSize(root);
    1591          173 :     if (numexprs != num_root_elements)
    1592              :     {
    1593            8 :         ereport(WARNING,
    1594              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1595              :                 errmsg("could not parse \"%s\": incorrect number of elements (%d required)",
    1596              :                        argname, numexprs));
    1597            8 :         goto exprs_error;
    1598              :     }
    1599              : 
    1600          165 :     fmgr_info(F_ARRAY_IN, &array_in_fn);
    1601              : 
    1602              :     /*
    1603              :      * Iterate over each expected expression object in the array.  Some of
    1604              :      * them could be null.  If the element is a completely wrong data type,
    1605              :      * give a WARNING and then treat the element like a NULL element in the
    1606              :      * result array.
    1607              :      *
    1608              :      * Each expression *MUST* have a value appended in the result pg_statistic
    1609              :      * array.
    1610              :      */
    1611          420 :     for (int i = 0; i < numexprs; i++)
    1612              :     {
    1613          259 :         Datum       pgstdat = (Datum) 0;
    1614          259 :         bool        isnull = false;
    1615          259 :         AttrNumber  exprattnum = -1 - i;
    1616              : 
    1617          259 :         JsonbValue *elem = getIthJsonbValueFromContainer(root, i);
    1618              : 
    1619          259 :         switch (elem->type)
    1620              :         {
    1621          227 :             case jbvBinary:
    1622              :                 {
    1623          227 :                     bool        sta_ok = false;
    1624              : 
    1625              :                     /* a real stats object */
    1626          227 :                     pgstdat = import_pg_statistic(pgsd, elem->val.binary.data,
    1627              :                                                   exprattnum, &array_in_fn,
    1628          227 :                                                   atttypids[i], atttypmods[i],
    1629          227 :                                                   atttypcolls[i], &sta_ok);
    1630              : 
    1631              :                     /*
    1632              :                      * If some incorrect data has been found, assign NULL for
    1633              :                      * this expression as a mean to give up.
    1634              :                      */
    1635          227 :                     if (sta_ok)
    1636          103 :                         num_import_ok++;
    1637              :                     else
    1638              :                     {
    1639          124 :                         isnull = true;
    1640          124 :                         pgstdat = (Datum) 0;
    1641              :                     }
    1642              :                 }
    1643          227 :                 break;
    1644              : 
    1645           28 :             case jbvNull:
    1646              :                 /* NULL placeholder for invalid data, still fine */
    1647           28 :                 isnull = true;
    1648           28 :                 num_import_ok++;
    1649           28 :                 break;
    1650              : 
    1651            4 :             default:
    1652              :                 /* cannot possibly be valid */
    1653            4 :                 ereport(WARNING,
    1654              :                         errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1655              :                         errmsg("could not parse \"%s\": invalid element in expression %d",
    1656              :                                argname, exprattnum));
    1657            4 :                 goto exprs_error;
    1658              :         }
    1659              : 
    1660          255 :         astate = accumArrayResult(astate, pgstdat, isnull, pgstypoid,
    1661              :                                   CurrentMemoryContext);
    1662              :     }
    1663              : 
    1664              :     /*
    1665              :      * The expressions datum is perfect *if and only if* all of the
    1666              :      * pg_statistic elements were also ok, for a number of elements equal to
    1667              :      * the number of expressions.  Anything else means a failure in restoring
    1668              :      * the data of this statistics object.
    1669              :      */
    1670          161 :     *exprs_is_perfect = (num_import_ok == numexprs);
    1671              : 
    1672          161 :     if (astate != NULL)
    1673          161 :         result = makeArrayResult(astate, CurrentMemoryContext);
    1674              : 
    1675          161 :     return result;
    1676              : 
    1677           16 : exprs_error:
    1678           16 :     if (astate != NULL)
    1679            0 :         pfree(astate);
    1680           16 :     return (Datum) 0;
    1681              : };
    1682              : 
    1683              : /*
    1684              :  * Remove an existing pg_statistic_ext_data row for a given pg_statistic_ext
    1685              :  * row and "inherited" pair.
    1686              :  */
    1687              : static bool
    1688           12 : delete_pg_statistic_ext_data(Oid stxoid, bool inherited)
    1689              : {
    1690           12 :     Relation    sed = table_open(StatisticExtDataRelationId, RowExclusiveLock);
    1691              :     HeapTuple   oldtup;
    1692           12 :     bool        result = false;
    1693              : 
    1694              :     /* Is there already a pg_statistic_ext_data tuple for this attribute? */
    1695           12 :     oldtup = SearchSysCache2(STATEXTDATASTXOID,
    1696              :                              ObjectIdGetDatum(stxoid),
    1697              :                              BoolGetDatum(inherited));
    1698              : 
    1699           12 :     if (HeapTupleIsValid(oldtup))
    1700              :     {
    1701            8 :         CatalogTupleDelete(sed, &oldtup->t_self);
    1702            8 :         ReleaseSysCache(oldtup);
    1703            8 :         result = true;
    1704              :     }
    1705              : 
    1706           12 :     table_close(sed, RowExclusiveLock);
    1707              : 
    1708           12 :     CommandCounterIncrement();
    1709              : 
    1710           12 :     return result;
    1711              : }
    1712              : 
    1713              : /*
    1714              :  * Restore (insert or replace) statistics for the given statistics object.
    1715              :  *
    1716              :  * This function accepts variadic arguments in key-value pairs, which are
    1717              :  * given to stats_fill_fcinfo_from_arg_pairs to be mapped into positional
    1718              :  * arguments.
    1719              :  */
    1720              : Datum
    1721          312 : pg_restore_extended_stats(PG_FUNCTION_ARGS)
    1722              : {
    1723          312 :     LOCAL_FCINFO(positional_fcinfo, NUM_EXTENDED_STATS_ARGS);
    1724          312 :     bool        result = true;
    1725              : 
    1726          312 :     InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_EXTENDED_STATS_ARGS,
    1727              :                              InvalidOid, NULL, NULL);
    1728              : 
    1729          312 :     if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo, extarginfo))
    1730            0 :         result = false;
    1731              : 
    1732          312 :     if (!extended_statistics_update(positional_fcinfo))
    1733          212 :         result = false;
    1734              : 
    1735          280 :     PG_RETURN_BOOL(result);
    1736              : }
    1737              : 
    1738              : /*
    1739              :  * Delete statistics for the given statistics object.
    1740              :  */
    1741              : Datum
    1742           56 : pg_clear_extended_stats(PG_FUNCTION_ARGS)
    1743              : {
    1744              :     char       *relnspname;
    1745              :     char       *relname;
    1746              :     char       *nspname;
    1747              :     Oid         nspoid;
    1748              :     Oid         relid;
    1749              :     char       *stxname;
    1750              :     bool        inherited;
    1751              :     Relation    pg_stext;
    1752              :     HeapTuple   tup;
    1753              :     Form_pg_statistic_ext stxform;
    1754           56 :     Oid         locked_table = InvalidOid;
    1755              : 
    1756              :     /* relation arguments */
    1757           56 :     stats_check_required_arg(fcinfo, extarginfo, RELSCHEMA_ARG);
    1758           52 :     relnspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
    1759           52 :     stats_check_required_arg(fcinfo, extarginfo, RELNAME_ARG);
    1760           48 :     relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
    1761              : 
    1762              :     /* extended statistics arguments */
    1763           48 :     stats_check_required_arg(fcinfo, extarginfo, STATSCHEMA_ARG);
    1764           44 :     nspname = TextDatumGetCString(PG_GETARG_DATUM(STATSCHEMA_ARG));
    1765           44 :     stats_check_required_arg(fcinfo, extarginfo, STATNAME_ARG);
    1766           40 :     stxname = TextDatumGetCString(PG_GETARG_DATUM(STATNAME_ARG));
    1767           40 :     stats_check_required_arg(fcinfo, extarginfo, INHERITED_ARG);
    1768           36 :     inherited = PG_GETARG_BOOL(INHERITED_ARG);
    1769              : 
    1770           36 :     if (RecoveryInProgress())
    1771              :     {
    1772            0 :         ereport(WARNING,
    1773              :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1774              :                 errmsg("recovery is in progress"),
    1775              :                 errhint("Statistics cannot be modified during recovery."));
    1776            0 :         PG_RETURN_VOID();
    1777              :     }
    1778              : 
    1779              :     /*
    1780              :      * First open the relation where we expect to find the statistics.  This
    1781              :      * is similar to relation and attribute statistics, so as ACL checks are
    1782              :      * done before any locks are taken, even before any attempts related to
    1783              :      * the extended stats object.
    1784              :      */
    1785           36 :     relid = RangeVarGetRelidExtended(makeRangeVar(relnspname, relname, -1),
    1786              :                                      ShareUpdateExclusiveLock, 0,
    1787              :                                      RangeVarCallbackForStats, &locked_table);
    1788              : 
    1789              :     /* Now check if the namespace of the stats object exists. */
    1790           24 :     nspoid = get_namespace_oid(nspname, true);
    1791           24 :     if (nspoid == InvalidOid)
    1792              :     {
    1793            4 :         ereport(WARNING,
    1794              :                 errcode(ERRCODE_UNDEFINED_OBJECT),
    1795              :                 errmsg("could not find schema \"%s\"", nspname));
    1796            4 :         PG_RETURN_VOID();
    1797              :     }
    1798              : 
    1799           20 :     pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock);
    1800           20 :     tup = get_pg_statistic_ext(pg_stext, nspoid, stxname);
    1801              : 
    1802           20 :     if (!HeapTupleIsValid(tup))
    1803              :     {
    1804            4 :         table_close(pg_stext, RowExclusiveLock);
    1805            4 :         ereport(WARNING,
    1806              :                 errcode(ERRCODE_UNDEFINED_OBJECT),
    1807              :                 errmsg("could not find extended statistics object \"%s.%s\"",
    1808              :                        nspname, stxname));
    1809            4 :         PG_RETURN_VOID();
    1810              :     }
    1811              : 
    1812           16 :     stxform = (Form_pg_statistic_ext) GETSTRUCT(tup);
    1813              : 
    1814              :     /*
    1815              :      * This should be consistent, based on the lock taken on the table when we
    1816              :      * started.
    1817              :      */
    1818           16 :     if (stxform->stxrelid != relid)
    1819              :     {
    1820            4 :         heap_freetuple(tup);
    1821            4 :         table_close(pg_stext, RowExclusiveLock);
    1822            4 :         ereport(WARNING,
    1823              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1824              :                 errmsg("could not clear extended statistics object \"%s.%s\": incorrect relation \"%s.%s\" specified",
    1825              :                        get_namespace_name(nspoid), stxname,
    1826              :                        relnspname, relname));
    1827            4 :         PG_RETURN_VOID();
    1828              :     }
    1829              : 
    1830           12 :     delete_pg_statistic_ext_data(stxform->oid, inherited);
    1831           12 :     heap_freetuple(tup);
    1832              : 
    1833           12 :     table_close(pg_stext, RowExclusiveLock);
    1834              : 
    1835           12 :     PG_RETURN_VOID();
    1836              : }
        

Generated by: LCOV version 2.0-1