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.4 % 562 542
Test Date: 2026-03-22 18:16:46 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          280 : get_pg_statistic_ext(Relation pg_stext, Oid nspoid, const char *stxname)
     179              : {
     180              :     ScanKeyData key[2];
     181              :     SysScanDesc scan;
     182              :     HeapTuple   tup;
     183          280 :     Oid         stxoid = InvalidOid;
     184              : 
     185          280 :     ScanKeyInit(&key[0],
     186              :                 Anum_pg_statistic_ext_stxname,
     187              :                 BTEqualStrategyNumber,
     188              :                 F_NAMEEQ,
     189              :                 CStringGetDatum(stxname));
     190          280 :     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          280 :     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          280 :     tup = systable_getnext(scan);
     208              : 
     209          280 :     if (HeapTupleIsValid(tup))
     210          272 :         stxoid = ((Form_pg_statistic_ext) GETSTRUCT(tup))->oid;
     211              : 
     212          280 :     systable_endscan(scan);
     213              : 
     214          280 :     if (!OidIsValid(stxoid))
     215            8 :         return NULL;
     216              : 
     217          272 :     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          252 : expand_stxkind(HeapTuple tup, StakindFlags *enabled)
     227              : {
     228              :     Datum       datum;
     229              :     ArrayType  *arr;
     230              :     char       *kinds;
     231              : 
     232          252 :     datum = SysCacheGetAttrNotNull(STATEXTOID,
     233              :                                    tup,
     234              :                                    Anum_pg_statistic_ext_stxkind);
     235          252 :     arr = DatumGetArrayTypeP(datum);
     236          252 :     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          252 :     kinds = (char *) ARR_DATA_PTR(arr);
     240              : 
     241         1017 :     for (int i = 0; i < ARR_DIMS(arr)[0]; i++)
     242              :     {
     243          765 :         switch (kinds[i])
     244              :         {
     245          188 :             case STATS_EXT_NDISTINCT:
     246          188 :                 enabled->ndistinct = true;
     247          188 :                 break;
     248          189 :             case STATS_EXT_DEPENDENCIES:
     249          189 :                 enabled->dependencies = true;
     250          189 :                 break;
     251          203 :             case STATS_EXT_MCV:
     252          203 :                 enabled->mcv = true;
     253          203 :                 break;
     254          185 :             case STATS_EXT_EXPRESSIONS:
     255          185 :                 enabled->expressions = true;
     256          185 :                 break;
     257            0 :             default:
     258            0 :                 elog(ERROR, "incorrect stxkind %c found", kinds[i]);
     259              :                 break;
     260              :         }
     261              :     }
     262          252 : }
     263              : 
     264              : /*
     265              :  * Perform the actual storage of a pg_statistic_ext_data tuple.
     266              :  */
     267              : static void
     268          252 : 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          252 :     pg_stextdata = table_open(StatisticExtDataRelationId, RowExclusiveLock);
     276              : 
     277          252 :     stxdtup = SearchSysCache2(STATEXTDATASTXOID,
     278              :                               values[Anum_pg_statistic_ext_data_stxoid - 1],
     279          252 :                               values[Anum_pg_statistic_ext_data_stxdinherit - 1]);
     280              : 
     281          252 :     if (HeapTupleIsValid(stxdtup))
     282              :     {
     283          228 :         newtup = heap_modify_tuple(stxdtup,
     284              :                                    RelationGetDescr(pg_stextdata),
     285              :                                    values,
     286              :                                    nulls,
     287              :                                    replaces);
     288          228 :         CatalogTupleUpdate(pg_stextdata, &newtup->t_self, newtup);
     289          228 :         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          252 :     heap_freetuple(newtup);
     298              : 
     299          252 :     CommandCounterIncrement();
     300              : 
     301          252 :     table_close(pg_stextdata, RowExclusiveLock);
     302          252 : }
     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          296 : 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          296 :     Relation    pg_stext = NULL;
     323          296 :     HeapTuple   tup = NULL;
     324              : 
     325          296 :     StakindFlags enabled = {false, false, false, false};
     326          296 :     StakindFlags has = {false, false, false, false};
     327              : 
     328              :     Form_pg_statistic_ext stxform;
     329              : 
     330          296 :     Datum       values[Natts_pg_statistic_ext_data] = {0};
     331          296 :     bool        nulls[Natts_pg_statistic_ext_data] = {0};
     332          296 :     bool        replaces[Natts_pg_statistic_ext_data] = {0};
     333          296 :     bool        success = true;
     334              :     Datum       exprdatum;
     335              :     bool        isnull;
     336          296 :     List       *exprs = NIL;
     337          296 :     int         numattnums = 0;
     338          296 :     int         numexprs = 0;
     339          296 :     int         numattrs = 0;
     340              : 
     341              :     /* arrays of type info, if we need them */
     342          296 :     Oid        *atttypids = NULL;
     343          296 :     int32      *atttypmods = NULL;
     344          296 :     Oid        *atttypcolls = NULL;
     345              :     Oid         relid;
     346          296 :     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          643 :     has.mcv = (!PG_ARGISNULL(MOST_COMMON_VALS_ARG) &&
     360          343 :                !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) &&
     361           47 :                !PG_ARGISNULL(MOST_COMMON_BASE_FREQS_ARG));
     362          296 :     has.ndistinct = !PG_ARGISNULL(NDISTINCT_ARG);
     363          296 :     has.dependencies = !PG_ARGISNULL(DEPENDENCIES_ARG);
     364          296 :     has.expressions = !PG_ARGISNULL(EXPRESSIONS_ARG);
     365              : 
     366          296 :     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          296 :     stats_check_required_arg(fcinfo, extarginfo, RELSCHEMA_ARG);
     377          292 :     relnspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
     378          292 :     stats_check_required_arg(fcinfo, extarginfo, RELNAME_ARG);
     379          288 :     relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
     380              : 
     381              :     /* extended statistics arguments */
     382          288 :     stats_check_required_arg(fcinfo, extarginfo, STATSCHEMA_ARG);
     383          284 :     nspname = TextDatumGetCString(PG_GETARG_DATUM(STATSCHEMA_ARG));
     384          284 :     stats_check_required_arg(fcinfo, extarginfo, STATNAME_ARG);
     385          280 :     stxname = TextDatumGetCString(PG_GETARG_DATUM(STATNAME_ARG));
     386          280 :     stats_check_required_arg(fcinfo, extarginfo, INHERITED_ARG);
     387          276 :     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          276 :     relid = RangeVarGetRelidExtended(makeRangeVar(relnspname, relname, -1),
     396              :                                      ShareUpdateExclusiveLock, 0,
     397              :                                      RangeVarCallbackForStats, &locked_table);
     398              : 
     399          264 :     nspoid = get_namespace_oid(nspname, true);
     400          264 :     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          260 :     pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock);
     410          260 :     tup = get_pg_statistic_ext(pg_stext, nspoid, stxname);
     411              : 
     412          260 :     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          256 :     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          256 :     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          252 :     expand_stxkind(tup, &enabled);
     442          252 :     numattnums = stxform->stxkeys.dim1;
     443              : 
     444              :     /* decode expression (if any) */
     445          252 :     exprdatum = SysCacheGetAttr(STATEXTOID,
     446              :                                 tup,
     447              :                                 Anum_pg_statistic_ext_stxexprs,
     448              :                                 &isnull);
     449          252 :     if (!isnull)
     450              :     {
     451              :         char       *s;
     452              : 
     453          185 :         s = TextDatumGetCString(exprdatum);
     454          185 :         exprs = (List *) stringToNode(s);
     455          185 :         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          185 :         exprs = (List *) eval_const_expressions(NULL, (Node *) exprs);
     467              : 
     468              :         /* May as well fix opfuncids too */
     469          185 :         fix_opfuncids((Node *) exprs);
     470              : 
     471              :         /* Compute the number of expression, for input validation. */
     472          185 :         numexprs = list_length(exprs);
     473              :     }
     474              : 
     475          252 :     numattrs = numattnums + numexprs;
     476              : 
     477              :     /*
     478              :      * If the object cannot support ndistinct, we should not have data for it.
     479              :      */
     480          252 :     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          252 :     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          252 :     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          203 :     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          164 :         if (!PG_ARGISNULL(MOST_COMMON_VALS_ARG) ||
     542          156 :             !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) ||
     543          152 :             !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          252 :     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              :      * VacAttrStatP 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          252 :     if (has.mcv || has.expressions)
     582              :     {
     583          188 :         atttypids = palloc0_array(Oid, numattrs);
     584          188 :         atttypmods = palloc0_array(int32, numattrs);
     585          188 :         atttypcolls = palloc0_array(Oid, numattrs);
     586              : 
     587              :         /*
     588              :          * The leading stxkeys are attribute numbers up through numattnums.
     589              :          * These keys must be in ascending AttNumber order, but we do not rely
     590              :          * on that.
     591              :          */
     592          509 :         for (int i = 0; i < numattnums; i++)
     593              :         {
     594          321 :             AttrNumber  attnum = stxform->stxkeys.values[i];
     595          321 :             HeapTuple   atup = SearchSysCache2(ATTNUM,
     596              :                                                ObjectIdGetDatum(relid),
     597              :                                                Int16GetDatum(attnum));
     598              : 
     599              :             Form_pg_attribute attr;
     600              : 
     601              :             /* Attribute not found */
     602          321 :             if (!HeapTupleIsValid(atup))
     603            0 :                 elog(ERROR, "stxkeys references nonexistent attnum %d", attnum);
     604              : 
     605          321 :             attr = (Form_pg_attribute) GETSTRUCT(atup);
     606              : 
     607          321 :             if (attr->attisdropped)
     608            0 :                 elog(ERROR, "stxkeys references dropped attnum %d", attnum);
     609              : 
     610          321 :             atttypids[i] = attr->atttypid;
     611          321 :             atttypmods[i] = attr->atttypmod;
     612          321 :             atttypcolls[i] = attr->attcollation;
     613          321 :             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          455 :         for (int i = numattnums; i < numattrs; i++)
     625              :         {
     626          267 :             Node       *expr = list_nth(exprs, i - numattnums);
     627              : 
     628          267 :             atttypids[i] = exprType(expr);
     629          267 :             atttypmods[i] = exprTypmod(expr);
     630          267 :             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          252 :     values[Anum_pg_statistic_ext_data_stxoid - 1] = ObjectIdGetDatum(stxform->oid);
     640          252 :     nulls[Anum_pg_statistic_ext_data_stxoid - 1] = false;
     641          252 :     values[Anum_pg_statistic_ext_data_stxdinherit - 1] = BoolGetDatum(inherited);
     642          252 :     nulls[Anum_pg_statistic_ext_data_stxdinherit - 1] = false;
     643              : 
     644              :     /* All unspecified parameters will be left unmodified */
     645          252 :     nulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true;
     646          252 :     nulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = true;
     647          252 :     nulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
     648          252 :     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          252 :     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          252 :     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          252 :     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          252 :     if (has.expressions)
     717              :     {
     718              :         Datum       datum;
     719              :         Relation    pgsd;
     720          161 :         bool        ok = false;
     721              : 
     722          161 :         pgsd = table_open(StatisticRelationId, RowExclusiveLock);
     723              : 
     724              :         /*
     725              :          * Generate the expressions array.
     726              :          *
     727              :          * The attytypids, attytypmods, 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          161 :         datum = import_expressions(pgsd, numexprs,
     733          161 :                                    &atttypids[numattnums],
     734          161 :                                    &atttypmods[numattnums],
     735          161 :                                    &atttypcolls[numattnums],
     736              :                                    PG_GETARG_JSONB_P(EXPRESSIONS_ARG),
     737              :                                    &ok);
     738              : 
     739          161 :         table_close(pgsd, RowExclusiveLock);
     740              : 
     741          161 :         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          124 :             success = false;
     750              :     }
     751              : 
     752          252 :     upsert_pg_statistic_ext_data(values, nulls, replaces);
     753              : 
     754          264 : cleanup:
     755          264 :     if (HeapTupleIsValid(tup))
     756          256 :         heap_freetuple(tup);
     757          264 :     if (pg_stext != NULL)
     758          260 :         table_close(pg_stext, RowExclusiveLock);
     759          264 :     if (atttypids != NULL)
     760          188 :         pfree(atttypids);
     761          264 :     if (atttypmods != NULL)
     762          188 :         pfree(atttypmods);
     763          264 :     if (atttypcolls != NULL)
     764          188 :         pfree(atttypcolls);
     765          264 :     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          638 : key_in_expr_argnames(JsonbValue *key)
     885              : {
     886              :     Assert(key->type == jbvString);
     887         3762 :     for (int i = 0; i < NUM_ATTRIBUTE_STATS_ELEMS; i++)
     888              :     {
     889         3758 :         if (strncmp(extexprargname[i], key->val.string.val, key->val.string.len) == 0)
     890          634 :             return true;
     891              :     }
     892            4 :     return false;
     893              : }
     894              : 
     895              : /*
     896              :  * Verify that all of the keys in the object are valid argnames.
     897              :  */
     898              : static bool
     899          199 : check_all_expr_argnames_valid(JsonbContainer *cont, AttrNumber exprnum)
     900              : {
     901          199 :     bool        all_keys_valid = true;
     902              : 
     903              :     JsonbIterator *jbit;
     904              :     JsonbIteratorToken jitok;
     905              :     JsonbValue  jkey;
     906              : 
     907              :     Assert(JsonContainerIsObject(cont));
     908              : 
     909          199 :     jbit = JsonbIteratorInit(cont);
     910              : 
     911              :     /* We always start off with a BEGIN OBJECT */
     912          199 :     jitok = JsonbIteratorNext(&jbit, &jkey, false);
     913              :     Assert(jitok == WJB_BEGIN_OBJECT);
     914              : 
     915              :     while (true)
     916          674 :     {
     917              :         JsonbValue  jval;
     918              : 
     919          873 :         jitok = JsonbIteratorNext(&jbit, &jkey, false);
     920              : 
     921              :         /*
     922              :          * We have run of keys. This is the only condition where it is
     923              :          * memory-safe to break out of the loop.
     924              :          */
     925          873 :         if (jitok == WJB_END_OBJECT)
     926          199 :             break;
     927              : 
     928              :         /* We can only find keys inside an object */
     929              :         Assert(jitok == WJB_KEY);
     930              :         Assert(jkey.type == jbvString);
     931              : 
     932              :         /* A value must follow the key */
     933          674 :         jitok = JsonbIteratorNext(&jbit, &jval, false);
     934              :         Assert(jitok == WJB_VALUE);
     935              : 
     936              :         /*
     937              :          * If we have already found an invalid key, there is no point in
     938              :          * looking for more, because additional WARNINGs are just clutter. We
     939              :          * must continue iterating over the json to ensure that we clean up
     940              :          * all allocated memory.
     941              :          */
     942          674 :         if (!all_keys_valid)
     943           36 :             continue;
     944              : 
     945          638 :         if (!key_in_expr_argnames(&jkey))
     946              :         {
     947            4 :             char       *bad_element_name = jbv_string_get_cstr(&jkey);
     948              : 
     949            4 :             ereport(WARNING,
     950              :                     errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     951              :                     errmsg("could not import element in expression %d: invalid key name",
     952              :                            exprnum));
     953              : 
     954            4 :             pfree(bad_element_name);
     955            4 :             all_keys_valid = false;
     956              :         }
     957              :     }
     958          199 :     return all_keys_valid;
     959              : }
     960              : 
     961              : /*
     962              :  * Simple conversion of jbvString to cstring
     963              :  */
     964              : static char *
     965          430 : jbv_string_get_cstr(JsonbValue *jval)
     966              : {
     967              :     char       *s;
     968              : 
     969              :     Assert(jval->type == jbvString);
     970              : 
     971          430 :     s = palloc0(jval->val.string.len + 1);
     972          430 :     memcpy(s, jval->val.string.val, jval->val.string.len);
     973              : 
     974          430 :     return s;
     975              : }
     976              : 
     977              : /*
     978              :  * Apply a jbvString value to a safe scalar input function.
     979              :  */
     980              : static bool
     981          232 : jbv_to_infunc_datum(JsonbValue *jval, PGFunction func, AttrNumber exprnum,
     982              :                     const char *argname, Datum *datum)
     983              : {
     984          232 :     ErrorSaveContext escontext = {
     985              :         .type = T_ErrorSaveContext,
     986              :         .details_wanted = true
     987              :     };
     988              : 
     989          232 :     char       *s = jbv_string_get_cstr(jval);
     990              :     bool        ok;
     991              : 
     992          232 :     ok = DirectInputFunctionCallSafe(func, s, InvalidOid, -1,
     993              :                                      (Node *) &escontext, datum);
     994              : 
     995              :     /*
     996              :      * If we got a type import error, use the report generated and add an
     997              :      * error hint before throwing a warning.
     998              :      */
     999          232 :     if (!ok)
    1000              :     {
    1001              :         StringInfoData hint_str;
    1002              : 
    1003           16 :         initStringInfo(&hint_str);
    1004           16 :         appendStringInfo(&hint_str,
    1005              :                          "Element \"%s\" in expression %d could not be parsed.",
    1006              :                          argname, exprnum);
    1007              : 
    1008           16 :         escontext.error_data->elevel = WARNING;
    1009           16 :         escontext.error_data->hint = hint_str.data;
    1010              : 
    1011           16 :         ThrowErrorData(escontext.error_data);
    1012           16 :         pfree(hint_str.data);
    1013              :     }
    1014              : 
    1015          232 :     pfree(s);
    1016          232 :     return ok;
    1017              : }
    1018              : 
    1019              : /*
    1020              :  * Build an array datum with element type elemtypid from a text datum, used as
    1021              :  * value of an attribute in a pg_statistic tuple.
    1022              :  *
    1023              :  * If an error is encountered, capture it, and reduce the elevel to WARNING.
    1024              :  *
    1025              :  * This is an adaptation of statatt_build_stavalues().
    1026              :  */
    1027              : static Datum
    1028          194 : array_in_safe(FmgrInfo *array_in, const char *s, Oid typid, int32 typmod,
    1029              :               AttrNumber exprnum, const char *element_name, bool *ok)
    1030              : {
    1031          194 :     LOCAL_FCINFO(fcinfo, 3);
    1032              :     Datum       result;
    1033              : 
    1034          194 :     ErrorSaveContext escontext = {
    1035              :         .type = T_ErrorSaveContext,
    1036              :         .details_wanted = true
    1037              :     };
    1038              : 
    1039          194 :     *ok = false;
    1040          194 :     InitFunctionCallInfoData(*fcinfo, array_in, 3, InvalidOid,
    1041              :                              (Node *) &escontext, NULL);
    1042              : 
    1043          194 :     fcinfo->args[0].value = CStringGetDatum(s);
    1044          194 :     fcinfo->args[0].isnull = false;
    1045          194 :     fcinfo->args[1].value = ObjectIdGetDatum(typid);
    1046          194 :     fcinfo->args[1].isnull = false;
    1047          194 :     fcinfo->args[2].value = Int32GetDatum(typmod);
    1048          194 :     fcinfo->args[2].isnull = false;
    1049              : 
    1050          194 :     result = FunctionCallInvoke(fcinfo);
    1051              : 
    1052              :     /*
    1053              :      * If the array_in function returned an error, we will want to report that
    1054              :      * ERROR as a WARNING, and add some location context to the error message.
    1055              :      * Overwriting the existing hint (if any) is not ideal, and an error
    1056              :      * context would only work for level >= ERROR.
    1057              :      */
    1058          194 :     if (escontext.error_occurred)
    1059              :     {
    1060              :         StringInfoData hint_str;
    1061              : 
    1062           24 :         initStringInfo(&hint_str);
    1063           24 :         appendStringInfo(&hint_str,
    1064              :                          "Element \"%s\" in expression %d could not be parsed.",
    1065              :                          element_name, exprnum);
    1066           24 :         escontext.error_data->elevel = WARNING;
    1067           24 :         escontext.error_data->hint = hint_str.data;
    1068           24 :         ThrowErrorData(escontext.error_data);
    1069           24 :         pfree(hint_str.data);
    1070           24 :         return (Datum) 0;
    1071              :     }
    1072              : 
    1073          170 :     if (array_contains_nulls(DatumGetArrayTypeP(result)))
    1074              :     {
    1075            0 :         ereport(WARNING,
    1076              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1077              :                 errmsg("could not import element \"%s\" in expression %d: null value found",
    1078              :                        element_name, exprnum));
    1079            0 :         return (Datum) 0;
    1080              :     }
    1081              : 
    1082          170 :     *ok = true;
    1083          170 :     return result;
    1084              : }
    1085              : 
    1086              : /*
    1087              :  * Create a pg_statistic tuple from an expression JSONB container.
    1088              :  *
    1089              :  * The pg_statistic tuple is pre-populated with acceptable defaults, therefore
    1090              :  * even if there is an issue with all of the keys in the container, we can
    1091              :  * still return a legit tuple datum.
    1092              :  *
    1093              :  * Set pg_statistic_ok to true if all of the values found in the container
    1094              :  * were imported without issue.  pg_statistic_ok is swicthed to "true" once
    1095              :  * the full pg_statistic tuple has been built and validated.
    1096              :  */
    1097              : static Datum
    1098          207 : import_pg_statistic(Relation pgsd, JsonbContainer *cont,
    1099              :                     AttrNumber exprnum, FmgrInfo *array_in_fn,
    1100              :                     Oid typid, int32 typmod, Oid typcoll,
    1101              :                     bool *pg_statistic_ok)
    1102              : {
    1103          207 :     const char *argname = extarginfo[EXPRESSIONS_ARG].argname;
    1104              :     TypeCacheEntry *typcache;
    1105              :     Datum       values[Natts_pg_statistic];
    1106              :     bool        nulls[Natts_pg_statistic];
    1107              :     bool        replaces[Natts_pg_statistic];
    1108          207 :     HeapTuple   pgstup = NULL;
    1109          207 :     Datum       pgstdat = (Datum) 0;
    1110          207 :     Oid         elemtypid = InvalidOid;
    1111          207 :     Oid         elemeqopr = InvalidOid;
    1112          207 :     bool        found[NUM_ATTRIBUTE_STATS_ELEMS] = {0};
    1113          207 :     JsonbValue  val[NUM_ATTRIBUTE_STATS_ELEMS] = {0};
    1114              : 
    1115              :     /* Assume the worst by default. */
    1116          207 :     *pg_statistic_ok = false;
    1117              : 
    1118          207 :     if (!JsonContainerIsObject(cont))
    1119              :     {
    1120            4 :         ereport(WARNING,
    1121              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1122              :                 errmsg("could not parse \"%s\": invalid element in expression %d",
    1123              :                        argname, exprnum));
    1124            4 :         goto pg_statistic_error;
    1125              :     }
    1126              : 
    1127              :     /*
    1128              :      * Loop through all keys that we need to look up.  If any value found is
    1129              :      * neither a string nor a NULL, there is not much we can do, so just give
    1130              :      * on the entire tuple for this expression.
    1131              :      */
    1132         2790 :     for (int i = 0; i < NUM_ATTRIBUTE_STATS_ELEMS; i++)
    1133              :     {
    1134         2591 :         const char *s = extexprargname[i];
    1135         2591 :         int         len = strlen(s);
    1136              : 
    1137         2591 :         if (getKeyJsonValueFromContainer(cont, s, len, &val[i]) == NULL)
    1138         1917 :             continue;
    1139              : 
    1140          674 :         switch (val[i].type)
    1141              :         {
    1142          550 :             case jbvString:
    1143          550 :                 found[i] = true;
    1144          550 :                 break;
    1145              : 
    1146          120 :             case jbvNull:
    1147          120 :                 break;
    1148              : 
    1149            4 :             default:
    1150            4 :                 ereport(WARNING,
    1151              :                         errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1152              :                         errmsg("could not parse \"%s\": invalid element in expression %d", argname, exprnum),
    1153              :                         errhint("Value of element \"%s\" must be type a null or a string.", s));
    1154            4 :                 goto pg_statistic_error;
    1155              :         }
    1156              :     }
    1157              : 
    1158              :     /* Look for invalid keys */
    1159          199 :     if (!check_all_expr_argnames_valid(cont, exprnum))
    1160            4 :         goto pg_statistic_error;
    1161              : 
    1162              :     /*
    1163              :      * There are two arg pairs, MCV+MCF and MCEV+MCEF.  Both values must
    1164              :      * either be found or not be found.  Any disagreement is a warning.  Once
    1165              :      * we have ruled out disagreeing pairs, we can use either found flag as a
    1166              :      * proxy for the other.
    1167              :      */
    1168          195 :     if (found[MOST_COMMON_VALS_ELEM] != found[MOST_COMMON_FREQS_ELEM])
    1169              :     {
    1170           16 :         ereport(WARNING,
    1171              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1172              :                 errmsg("could not parse \"%s\": invalid element in expression %d",
    1173              :                        argname, exprnum),
    1174              :                 errhint("\"%s\" and \"%s\" must be both either strings or nulls.",
    1175              :                         extexprargname[MOST_COMMON_VALS_ELEM],
    1176              :                         extexprargname[MOST_COMMON_FREQS_ELEM]));
    1177           16 :         goto pg_statistic_error;
    1178              :     }
    1179          179 :     if (found[MOST_COMMON_ELEMS_ELEM] != found[MOST_COMMON_ELEM_FREQS_ELEM])
    1180              :     {
    1181           16 :         ereport(WARNING,
    1182              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1183              :                 errmsg("could not parse \"%s\": invalid element in expression %d",
    1184              :                        argname, exprnum),
    1185              :                 errhint("\"%s\" and \"%s\" must be both either strings or nulls.",
    1186              :                         extexprargname[MOST_COMMON_ELEMS_ELEM],
    1187              :                         extexprargname[MOST_COMMON_ELEM_FREQS_ELEM]));
    1188           16 :         goto pg_statistic_error;
    1189              :     }
    1190              : 
    1191              :     /*
    1192              :      * Range types may expect three values to be set.  All three of them must
    1193              :      * either be found or not be found.  Any disagreement is a warning.
    1194              :      */
    1195          163 :     if (found[RANGE_LENGTH_HISTOGRAM_ELEM] != found[RANGE_EMPTY_FRAC_ELEM] ||
    1196          155 :         found[RANGE_LENGTH_HISTOGRAM_ELEM] != found[RANGE_BOUNDS_HISTOGRAM_ELEM])
    1197              :     {
    1198           16 :         ereport(WARNING,
    1199              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1200              :                 errmsg("could not parse \"%s\": invalid element in expression %d",
    1201              :                        argname, exprnum),
    1202              :                 errhint("\"%s\", \"%s\", and \"%s\" must be all either strings or all nulls.",
    1203              :                         extexprargname[RANGE_LENGTH_HISTOGRAM_ELEM],
    1204              :                         extexprargname[RANGE_EMPTY_FRAC_ELEM],
    1205              :                         extexprargname[RANGE_BOUNDS_HISTOGRAM_ELEM]));
    1206           16 :         goto pg_statistic_error;
    1207              :     }
    1208              : 
    1209              :     /* This finds the right operators even if atttypid is a domain */
    1210          147 :     typcache = lookup_type_cache(typid, TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR);
    1211              : 
    1212          147 :     statatt_init_empty_tuple(InvalidOid, InvalidAttrNumber, false,
    1213              :                              values, nulls, replaces);
    1214              : 
    1215              :     /*
    1216              :      * Special case: collation for tsvector is DEFAULT_COLLATION_OID. See
    1217              :      * compute_tsvector_stats().
    1218              :      */
    1219          147 :     if (typid == TSVECTOROID)
    1220            4 :         typcoll = DEFAULT_COLLATION_OID;
    1221              : 
    1222              :     /*
    1223              :      * We only need to fetch element type and eq operator if we have a stat of
    1224              :      * type MCELEM or DECHIST, otherwise the values are unnecessary and not
    1225              :      * meaningful.
    1226              :      */
    1227          147 :     if (found[MOST_COMMON_ELEMS_ELEM] || found[ELEM_COUNT_HISTOGRAM_ELEM])
    1228              :     {
    1229           28 :         if (!statatt_get_elem_type(typid, typcache->typtype,
    1230              :                                    &elemtypid, &elemeqopr))
    1231              :         {
    1232            8 :             ereport(WARNING,
    1233              :                     errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1234              :                     errmsg("could not parse \"%s\": invalid element type in expression %d",
    1235              :                            argname, exprnum));
    1236            8 :             goto pg_statistic_error;
    1237              :         }
    1238              :     }
    1239              : 
    1240              :     /*
    1241              :      * These three fields can only be set if dealing with a range or
    1242              :      * multi-range type.
    1243              :      */
    1244          139 :     if (found[RANGE_LENGTH_HISTOGRAM_ELEM] ||
    1245          127 :         found[RANGE_EMPTY_FRAC_ELEM] ||
    1246          127 :         found[RANGE_BOUNDS_HISTOGRAM_ELEM])
    1247              :     {
    1248           12 :         if (typcache->typtype != TYPTYPE_RANGE &&
    1249           12 :             typcache->typtype != TYPTYPE_MULTIRANGE)
    1250              :         {
    1251            4 :             ereport(WARNING,
    1252              :                     errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1253              :                     errmsg("could not parse \"%s\": invalid data in expression %d",
    1254              :                            argname, exprnum),
    1255              :                     errhint("\"%s\", \"%s\", and \"%s\" can only be set for a range type.",
    1256              :                             extexprargname[RANGE_LENGTH_HISTOGRAM_ELEM],
    1257              :                             extexprargname[RANGE_EMPTY_FRAC_ELEM],
    1258              :                             extexprargname[RANGE_BOUNDS_HISTOGRAM_ELEM]));
    1259            4 :             goto pg_statistic_error;
    1260              :         }
    1261              :     }
    1262              : 
    1263              :     /* null_frac */
    1264          135 :     if (found[NULL_FRAC_ELEM])
    1265              :     {
    1266              :         Datum       datum;
    1267              : 
    1268           63 :         if (jbv_to_infunc_datum(&val[NULL_FRAC_ELEM], float4in, exprnum,
    1269              :                                 extexprargname[NULL_FRAC_ELEM], &datum))
    1270           59 :             values[Anum_pg_statistic_stanullfrac - 1] = datum;
    1271              :         else
    1272            4 :             goto pg_statistic_error;
    1273              :     }
    1274              : 
    1275              :     /* avg_width */
    1276          131 :     if (found[AVG_WIDTH_ELEM])
    1277              :     {
    1278              :         Datum       datum;
    1279              : 
    1280           59 :         if (jbv_to_infunc_datum(&val[AVG_WIDTH_ELEM], int4in, exprnum,
    1281              :                                 extexprargname[AVG_WIDTH_ELEM], &datum))
    1282           55 :             values[Anum_pg_statistic_stawidth - 1] = datum;
    1283              :         else
    1284            4 :             goto pg_statistic_error;
    1285              :     }
    1286              : 
    1287              :     /* n_distinct */
    1288          127 :     if (found[N_DISTINCT_ELEM])
    1289              :     {
    1290              :         Datum       datum;
    1291              : 
    1292           59 :         if (jbv_to_infunc_datum(&val[N_DISTINCT_ELEM], float4in, exprnum,
    1293              :                                 extexprargname[N_DISTINCT_ELEM], &datum))
    1294           55 :             values[Anum_pg_statistic_stadistinct - 1] = datum;
    1295              :         else
    1296            4 :             goto pg_statistic_error;
    1297              :     }
    1298              : 
    1299              :     /*
    1300              :      * The STAKIND statistics are the same as the ones found in attribute
    1301              :      * stats.  However, these are all derived from json strings, whereas the
    1302              :      * ones derived for attribute stats are a mix of datatypes.  This limits
    1303              :      * the opportunities for code sharing between the two.
    1304              :      *
    1305              :      * Some statistic kinds have both a stanumbers and a stavalues components.
    1306              :      * In those cases, both values must either be NOT NULL or both NULL, and
    1307              :      * if they aren't then we need to reject that stakind completely.
    1308              :      * Currently we go a step further and reject the expression array
    1309              :      * completely.
    1310              :      *
    1311              :      * Once it is established that the pairs are in NULL/NOT-NULL alignment,
    1312              :      * we can test either expr_nulls[] value to see if the stakind has
    1313              :      * value(s) that we can set or not.
    1314              :      */
    1315              : 
    1316          123 :     if (found[MOST_COMMON_VALS_ELEM])
    1317              :     {
    1318              :         Datum       stavalues;
    1319              :         Datum       stanumbers;
    1320           59 :         bool        val_ok = false;
    1321           59 :         bool        num_ok = false;
    1322              :         char       *s;
    1323              : 
    1324           59 :         s = jbv_string_get_cstr(&val[MOST_COMMON_VALS_ELEM]);
    1325           59 :         stavalues = array_in_safe(array_in_fn, s, typid, typmod, exprnum,
    1326              :                                   extexprargname[MOST_COMMON_VALS_ELEM],
    1327              :                                   &val_ok);
    1328              : 
    1329           59 :         pfree(s);
    1330              : 
    1331           59 :         s = jbv_string_get_cstr(&val[MOST_COMMON_FREQS_ELEM]);
    1332           59 :         stanumbers = array_in_safe(array_in_fn, s, FLOAT4OID, -1, exprnum,
    1333              :                                    extexprargname[MOST_COMMON_FREQS_ELEM],
    1334              :                                    &num_ok);
    1335           59 :         pfree(s);
    1336              : 
    1337              :         /* Only set the slot if both datums have been built */
    1338           59 :         if (val_ok && num_ok)
    1339           51 :             statatt_set_slot(values, nulls, replaces,
    1340              :                              STATISTIC_KIND_MCV,
    1341              :                              typcache->eq_opr, typcoll,
    1342              :                              stanumbers, false, stavalues, false);
    1343              :         else
    1344            8 :             goto pg_statistic_error;
    1345              :     }
    1346              : 
    1347              :     /* STATISTIC_KIND_HISTOGRAM */
    1348          115 :     if (found[HISTOGRAM_BOUNDS_ELEM])
    1349              :     {
    1350              :         Datum       stavalues;
    1351           20 :         bool        val_ok = false;
    1352           20 :         char       *s = jbv_string_get_cstr(&val[HISTOGRAM_BOUNDS_ELEM]);
    1353              : 
    1354           20 :         stavalues = array_in_safe(array_in_fn, s, typid, typmod, exprnum,
    1355              :                                   extexprargname[HISTOGRAM_BOUNDS_ELEM],
    1356              :                                   &val_ok);
    1357           20 :         pfree(s);
    1358              : 
    1359           20 :         if (val_ok)
    1360           16 :             statatt_set_slot(values, nulls, replaces,
    1361              :                              STATISTIC_KIND_HISTOGRAM,
    1362              :                              typcache->lt_opr, typcoll,
    1363              :                              0, true, stavalues, false);
    1364              :         else
    1365            4 :             goto pg_statistic_error;
    1366              :     }
    1367              : 
    1368              :     /* STATISTIC_KIND_CORRELATION */
    1369          111 :     if (found[CORRELATION_ELEM])
    1370              :     {
    1371           43 :         Datum       corr[] = {(Datum) 0};
    1372              : 
    1373           43 :         if (jbv_to_infunc_datum(&val[CORRELATION_ELEM], float4in, exprnum,
    1374              :                                 extexprargname[CORRELATION_ELEM], &corr[0]))
    1375              :         {
    1376           39 :             ArrayType  *arry = construct_array_builtin(corr, 1, FLOAT4OID);
    1377           39 :             Datum       stanumbers = PointerGetDatum(arry);
    1378              : 
    1379           39 :             statatt_set_slot(values, nulls, replaces,
    1380              :                              STATISTIC_KIND_CORRELATION,
    1381              :                              typcache->lt_opr, typcoll,
    1382              :                              stanumbers, false, 0, true);
    1383              :         }
    1384              :         else
    1385            4 :             goto pg_statistic_error;
    1386              :     }
    1387              : 
    1388              :     /* STATISTIC_KIND_MCELEM */
    1389          107 :     if (found[MOST_COMMON_ELEMS_ELEM])
    1390              :     {
    1391              :         Datum       stavalues;
    1392              :         Datum       stanumbers;
    1393           16 :         bool        val_ok = false;
    1394           16 :         bool        num_ok = false;
    1395              :         char       *s;
    1396              : 
    1397           16 :         s = jbv_string_get_cstr(&val[MOST_COMMON_ELEMS_ELEM]);
    1398           16 :         stavalues = array_in_safe(array_in_fn, s, elemtypid, typmod, exprnum,
    1399              :                                   extexprargname[MOST_COMMON_ELEMS_ELEM],
    1400              :                                   &val_ok);
    1401           16 :         pfree(s);
    1402              : 
    1403              : 
    1404           16 :         s = jbv_string_get_cstr(&val[MOST_COMMON_ELEM_FREQS_ELEM]);
    1405           16 :         stanumbers = array_in_safe(array_in_fn, s, FLOAT4OID, -1, exprnum,
    1406              :                                    extexprargname[MOST_COMMON_ELEM_FREQS_ELEM],
    1407              :                                    &num_ok);
    1408           16 :         pfree(s);
    1409              : 
    1410              :         /* Only set the slot if both datums have been built */
    1411           16 :         if (val_ok && num_ok)
    1412            8 :             statatt_set_slot(values, nulls, replaces,
    1413              :                              STATISTIC_KIND_MCELEM,
    1414              :                              elemeqopr, typcoll,
    1415              :                              stanumbers, false, stavalues, false);
    1416              :         else
    1417            8 :             goto pg_statistic_error;
    1418              :     }
    1419              : 
    1420              :     /* STATISTIC_KIND_DECHIST */
    1421           99 :     if (found[ELEM_COUNT_HISTOGRAM_ELEM])
    1422              :     {
    1423              :         Datum       stanumbers;
    1424            8 :         bool        num_ok = false;
    1425              :         char       *s;
    1426              : 
    1427            8 :         s = jbv_string_get_cstr(&val[ELEM_COUNT_HISTOGRAM_ELEM]);
    1428            8 :         stanumbers = array_in_safe(array_in_fn, s, FLOAT4OID, -1, exprnum,
    1429              :                                    extexprargname[ELEM_COUNT_HISTOGRAM_ELEM],
    1430              :                                    &num_ok);
    1431            8 :         pfree(s);
    1432              : 
    1433            8 :         if (num_ok)
    1434            4 :             statatt_set_slot(values, nulls, replaces, STATISTIC_KIND_DECHIST,
    1435              :                              elemeqopr, typcoll, stanumbers, false, 0, true);
    1436              :         else
    1437            4 :             goto pg_statistic_error;
    1438              :     }
    1439              : 
    1440              :     /*
    1441              :      * STATISTIC_KIND_BOUNDS_HISTOGRAM
    1442              :      *
    1443              :      * This stakind appears before STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM even
    1444              :      * though it is numerically greater, and all other stakinds appear in
    1445              :      * numerical order.
    1446              :      */
    1447           95 :     if (found[RANGE_BOUNDS_HISTOGRAM_ELEM])
    1448              :     {
    1449              :         Datum       stavalues;
    1450            8 :         bool        val_ok = false;
    1451              :         char       *s;
    1452            8 :         Oid         rtypid = typid;
    1453              : 
    1454              :         /*
    1455              :          * If it's a multirange, step down to the range type, as is done by
    1456              :          * multirange_typanalyze().
    1457              :          */
    1458            8 :         if (type_is_multirange(typid))
    1459            8 :             rtypid = get_multirange_range(typid);
    1460              : 
    1461            8 :         s = jbv_string_get_cstr(&val[RANGE_BOUNDS_HISTOGRAM_ELEM]);
    1462              : 
    1463            8 :         stavalues = array_in_safe(array_in_fn, s, rtypid, typmod, exprnum,
    1464              :                                   extexprargname[RANGE_BOUNDS_HISTOGRAM_ELEM],
    1465              :                                   &val_ok);
    1466              : 
    1467            8 :         if (val_ok)
    1468            8 :             statatt_set_slot(values, nulls, replaces,
    1469              :                              STATISTIC_KIND_BOUNDS_HISTOGRAM,
    1470              :                              InvalidOid, InvalidOid,
    1471              :                              0, true, stavalues, false);
    1472              :         else
    1473            0 :             goto pg_statistic_error;
    1474              :     }
    1475              : 
    1476              :     /* STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM */
    1477           95 :     if (found[RANGE_LENGTH_HISTOGRAM_ELEM])
    1478              :     {
    1479            8 :         Datum       empty_frac[] = {(Datum) 0};
    1480              :         Datum       stavalues;
    1481              :         Datum       stanumbers;
    1482            8 :         bool        val_ok = false;
    1483              :         char       *s;
    1484              : 
    1485            8 :         if (jbv_to_infunc_datum(&val[RANGE_EMPTY_FRAC_ELEM], float4in, exprnum,
    1486              :                                 extexprargname[RANGE_EMPTY_FRAC_ELEM], &empty_frac[0]))
    1487              :         {
    1488            8 :             ArrayType  *arry = construct_array_builtin(empty_frac, 1, FLOAT4OID);
    1489              : 
    1490            8 :             stanumbers = PointerGetDatum(arry);
    1491              :         }
    1492              :         else
    1493            0 :             goto pg_statistic_error;
    1494              : 
    1495            8 :         s = jbv_string_get_cstr(&val[RANGE_LENGTH_HISTOGRAM_ELEM]);
    1496            8 :         stavalues = array_in_safe(array_in_fn, s, FLOAT8OID, -1, exprnum,
    1497              :                                   extexprargname[RANGE_LENGTH_HISTOGRAM_ELEM],
    1498              :                                   &val_ok);
    1499              : 
    1500            8 :         if (val_ok)
    1501            8 :             statatt_set_slot(values, nulls, replaces,
    1502              :                              STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
    1503              :                              Float8LessOperator, InvalidOid,
    1504              :                              stanumbers, false, stavalues, false);
    1505              :         else
    1506            0 :             goto pg_statistic_error;
    1507              :     }
    1508              : 
    1509           95 :     pgstup = heap_form_tuple(RelationGetDescr(pgsd), values, nulls);
    1510           95 :     pgstdat = heap_copy_tuple_as_datum(pgstup, RelationGetDescr(pgsd));
    1511              : 
    1512           95 :     heap_freetuple(pgstup);
    1513              : 
    1514           95 :     *pg_statistic_ok = true;
    1515              : 
    1516           95 :     return pgstdat;
    1517              : 
    1518          112 : pg_statistic_error:
    1519          112 :     return (Datum) 0;
    1520              : }
    1521              : 
    1522              : /*
    1523              :  * Create the stxdexpr datum, which is an array of pg_statistic rows with all
    1524              :  * of the object identification fields left at defaults, using the json array
    1525              :  * of objects/nulls referenced against the datatypes for the expressions.
    1526              :  *
    1527              :  * The exprs_is_perfect will be set to true if all pg_statistic rows were
    1528              :  * imported cleanly.  If any of them experienced a problem (and thus were
    1529              :  * set as if they were null), then the expression is kept but exprs_is_perfect
    1530              :  * will be marked as false.
    1531              :  *
    1532              :  * This datum is needed to fill out a complete pg_statistic_ext_data tuple.
    1533              :  */
    1534              : static Datum
    1535          161 : import_expressions(Relation pgsd, int numexprs,
    1536              :                    Oid *atttypids, int32 *atttypmods,
    1537              :                    Oid *atttypcolls, Jsonb *exprs_jsonb,
    1538              :                    bool *exprs_is_perfect)
    1539              : {
    1540          161 :     const char *argname = extarginfo[EXPRESSIONS_ARG].argname;
    1541          161 :     Oid         pgstypoid = get_rel_type_id(StatisticRelationId);
    1542          161 :     ArrayBuildState *astate = NULL;
    1543          161 :     Datum       result = (Datum) 0;
    1544          161 :     int         num_import_ok = 0;
    1545              :     JsonbContainer *root;
    1546              :     int         num_root_elements;
    1547              : 
    1548              :     FmgrInfo    array_in_fn;
    1549              : 
    1550          161 :     *exprs_is_perfect = false;
    1551              : 
    1552              :     /* Json schema must be [{expr},...] */
    1553          161 :     if (!JB_ROOT_IS_ARRAY(exprs_jsonb))
    1554              :     {
    1555            4 :         ereport(WARNING,
    1556              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1557              :                 errmsg("could not parse \"%s\": root-level array required", argname));
    1558            4 :         goto exprs_error;
    1559              :     }
    1560              : 
    1561          157 :     root = &exprs_jsonb->root;
    1562              : 
    1563              :     /*
    1564              :      * The number of elements in the array must match the number of
    1565              :      * expressions in the stats object definition.
    1566              :      */
    1567          157 :     num_root_elements = JsonContainerSize(root);
    1568          157 :     if (numexprs != num_root_elements)
    1569              :     {
    1570            4 :         ereport(WARNING,
    1571              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1572              :                 errmsg("could not parse \"%s\": incorrect number of elements (%d required)",
    1573              :                        argname, num_root_elements));
    1574            4 :         goto exprs_error;
    1575              :     }
    1576              : 
    1577          153 :     fmgr_info(F_ARRAY_IN, &array_in_fn);
    1578              : 
    1579              :     /*
    1580              :      * Iterate over each expected expression object in the array.  Some of
    1581              :      * them could be null.  If the element is a completely wrong data type,
    1582              :      * give a WARNING and then treat the element like a NULL element in the
    1583              :      * result array.
    1584              :      *
    1585              :      * Each expression *MUST* have a value appended in the result pg_statistic
    1586              :      * array.
    1587              :      */
    1588          388 :     for (int i = 0; i < numexprs; i++)
    1589              :     {
    1590          239 :         Datum       pgstdat = (Datum) 0;
    1591          239 :         bool        isnull = false;
    1592          239 :         AttrNumber  exprattnum = -1 - i;
    1593              : 
    1594          239 :         JsonbValue *elem = getIthJsonbValueFromContainer(root, i);
    1595              : 
    1596          239 :         switch (elem->type)
    1597              :         {
    1598          207 :             case jbvBinary:
    1599              :                 {
    1600          207 :                     bool        sta_ok = false;
    1601              : 
    1602              :                     /* a real stats object */
    1603          207 :                     pgstdat = import_pg_statistic(pgsd, elem->val.binary.data,
    1604              :                                                   exprattnum, &array_in_fn,
    1605          207 :                                                   atttypids[i], atttypmods[i],
    1606          207 :                                                   atttypcolls[i], &sta_ok);
    1607              : 
    1608              :                     /*
    1609              :                      * If some incorrect data has been found, assign NULL for
    1610              :                      * this expression as a mean to give up.
    1611              :                      */
    1612          207 :                     if (sta_ok)
    1613           95 :                         num_import_ok++;
    1614              :                     else
    1615              :                     {
    1616          112 :                         isnull = true;
    1617          112 :                         pgstdat = (Datum) 0;
    1618              :                     }
    1619              :                 }
    1620          207 :                 break;
    1621              : 
    1622           28 :             case jbvNull:
    1623              :                 /* NULL placeholder for invalid data, still fine */
    1624           28 :                 isnull = true;
    1625           28 :                 num_import_ok++;
    1626           28 :                 break;
    1627              : 
    1628            4 :             default:
    1629              :                 /* cannot possibly be valid */
    1630            4 :                 ereport(WARNING,
    1631              :                         errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1632              :                         errmsg("could not parse \"%s\": invalid element in expression %d",
    1633              :                                argname, exprattnum));
    1634            4 :                 goto exprs_error;
    1635              :         }
    1636              : 
    1637          235 :         astate = accumArrayResult(astate, pgstdat, isnull, pgstypoid,
    1638              :                                   CurrentMemoryContext);
    1639              :     }
    1640              : 
    1641              :     /*
    1642              :      * The expressions datum is perfect *if and only if* all of the
    1643              :      * pg_statistic elements were also ok, for a number of elements equal to
    1644              :      * the number of expressions.  Anything else means a failure in restoring
    1645              :      * the data of this statistics object.
    1646              :      */
    1647          149 :     *exprs_is_perfect = (num_import_ok == numexprs);
    1648              : 
    1649          149 :     if (astate != NULL)
    1650          149 :         result = makeArrayResult(astate, CurrentMemoryContext);
    1651              : 
    1652          149 :     return result;
    1653              : 
    1654           12 : exprs_error:
    1655           12 :     if (astate != NULL)
    1656            0 :         pfree(astate);
    1657           12 :     return (Datum) 0;
    1658              : };
    1659              : 
    1660              : /*
    1661              :  * Remove an existing pg_statistic_ext_data row for a given pg_statistic_ext
    1662              :  * row and "inherited" pair.
    1663              :  */
    1664              : static bool
    1665           12 : delete_pg_statistic_ext_data(Oid stxoid, bool inherited)
    1666              : {
    1667           12 :     Relation    sed = table_open(StatisticExtDataRelationId, RowExclusiveLock);
    1668              :     HeapTuple   oldtup;
    1669           12 :     bool        result = false;
    1670              : 
    1671              :     /* Is there already a pg_statistic_ext_data tuple for this attribute? */
    1672           12 :     oldtup = SearchSysCache2(STATEXTDATASTXOID,
    1673              :                              ObjectIdGetDatum(stxoid),
    1674              :                              BoolGetDatum(inherited));
    1675              : 
    1676           12 :     if (HeapTupleIsValid(oldtup))
    1677              :     {
    1678            8 :         CatalogTupleDelete(sed, &oldtup->t_self);
    1679            8 :         ReleaseSysCache(oldtup);
    1680            8 :         result = true;
    1681              :     }
    1682              : 
    1683           12 :     table_close(sed, RowExclusiveLock);
    1684              : 
    1685           12 :     CommandCounterIncrement();
    1686              : 
    1687           12 :     return result;
    1688              : }
    1689              : 
    1690              : /*
    1691              :  * Restore (insert or replace) statistics for the given statistics object.
    1692              :  *
    1693              :  * This function accepts variadic arguments in key-value pairs, which are
    1694              :  * given to stats_fill_fcinfo_from_arg_pairs to be mapped into positional
    1695              :  * arguments.
    1696              :  */
    1697              : Datum
    1698          296 : pg_restore_extended_stats(PG_FUNCTION_ARGS)
    1699              : {
    1700          296 :     LOCAL_FCINFO(positional_fcinfo, NUM_EXTENDED_STATS_ARGS);
    1701          296 :     bool        result = true;
    1702              : 
    1703          296 :     InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_EXTENDED_STATS_ARGS,
    1704              :                              InvalidOid, NULL, NULL);
    1705              : 
    1706          296 :     if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo, extarginfo))
    1707            0 :         result = false;
    1708              : 
    1709          296 :     if (!extended_statistics_update(positional_fcinfo))
    1710          196 :         result = false;
    1711              : 
    1712          264 :     PG_RETURN_BOOL(result);
    1713              : }
    1714              : 
    1715              : /*
    1716              :  * Delete statistics for the given statistics object.
    1717              :  */
    1718              : Datum
    1719           56 : pg_clear_extended_stats(PG_FUNCTION_ARGS)
    1720              : {
    1721              :     char       *relnspname;
    1722              :     char       *relname;
    1723              :     char       *nspname;
    1724              :     Oid         nspoid;
    1725              :     Oid         relid;
    1726              :     char       *stxname;
    1727              :     bool        inherited;
    1728              :     Relation    pg_stext;
    1729              :     HeapTuple   tup;
    1730              :     Form_pg_statistic_ext stxform;
    1731           56 :     Oid         locked_table = InvalidOid;
    1732              : 
    1733              :     /* relation arguments */
    1734           56 :     stats_check_required_arg(fcinfo, extarginfo, RELSCHEMA_ARG);
    1735           52 :     relnspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
    1736           52 :     stats_check_required_arg(fcinfo, extarginfo, RELNAME_ARG);
    1737           48 :     relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
    1738              : 
    1739              :     /* extended statistics arguments */
    1740           48 :     stats_check_required_arg(fcinfo, extarginfo, STATSCHEMA_ARG);
    1741           44 :     nspname = TextDatumGetCString(PG_GETARG_DATUM(STATSCHEMA_ARG));
    1742           44 :     stats_check_required_arg(fcinfo, extarginfo, STATNAME_ARG);
    1743           40 :     stxname = TextDatumGetCString(PG_GETARG_DATUM(STATNAME_ARG));
    1744           40 :     stats_check_required_arg(fcinfo, extarginfo, INHERITED_ARG);
    1745           36 :     inherited = PG_GETARG_BOOL(INHERITED_ARG);
    1746              : 
    1747           36 :     if (RecoveryInProgress())
    1748              :     {
    1749            0 :         ereport(WARNING,
    1750              :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    1751              :                 errmsg("recovery is in progress"),
    1752              :                 errhint("Statistics cannot be modified during recovery."));
    1753            0 :         PG_RETURN_VOID();
    1754              :     }
    1755              : 
    1756              :     /*
    1757              :      * First open the relation where we expect to find the statistics.  This
    1758              :      * is similar to relation and attribute statistics, so as ACL checks are
    1759              :      * done before any locks are taken, even before any attempts related to
    1760              :      * the extended stats object.
    1761              :      */
    1762           36 :     relid = RangeVarGetRelidExtended(makeRangeVar(relnspname, relname, -1),
    1763              :                                      ShareUpdateExclusiveLock, 0,
    1764              :                                      RangeVarCallbackForStats, &locked_table);
    1765              : 
    1766              :     /* Now check if the namespace of the stats object exists. */
    1767           24 :     nspoid = get_namespace_oid(nspname, true);
    1768           24 :     if (nspoid == InvalidOid)
    1769              :     {
    1770            4 :         ereport(WARNING,
    1771              :                 errcode(ERRCODE_UNDEFINED_OBJECT),
    1772              :                 errmsg("could not find schema \"%s\"", nspname));
    1773            4 :         PG_RETURN_VOID();
    1774              :     }
    1775              : 
    1776           20 :     pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock);
    1777           20 :     tup = get_pg_statistic_ext(pg_stext, nspoid, stxname);
    1778              : 
    1779           20 :     if (!HeapTupleIsValid(tup))
    1780              :     {
    1781            4 :         table_close(pg_stext, RowExclusiveLock);
    1782            4 :         ereport(WARNING,
    1783              :                 errcode(ERRCODE_UNDEFINED_OBJECT),
    1784              :                 errmsg("could not find extended statistics object \"%s.%s\"",
    1785              :                        nspname, stxname));
    1786            4 :         PG_RETURN_VOID();
    1787              :     }
    1788              : 
    1789           16 :     stxform = (Form_pg_statistic_ext) GETSTRUCT(tup);
    1790              : 
    1791              :     /*
    1792              :      * This should be consistent, based on the lock taken on the table when we
    1793              :      * started.
    1794              :      */
    1795           16 :     if (stxform->stxrelid != relid)
    1796              :     {
    1797            4 :         table_close(pg_stext, RowExclusiveLock);
    1798            4 :         ereport(WARNING,
    1799              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1800              :                 errmsg("could not clear extended statistics object \"%s.%s\": incorrect relation \"%s.%s\" specified",
    1801              :                        get_namespace_name(nspoid), stxname,
    1802              :                        relnspname, relname));
    1803            4 :         PG_RETURN_VOID();
    1804              :     }
    1805              : 
    1806           12 :     delete_pg_statistic_ext_data(stxform->oid, inherited);
    1807           12 :     heap_freetuple(tup);
    1808              : 
    1809           12 :     table_close(pg_stext, RowExclusiveLock);
    1810              : 
    1811           12 :     PG_RETURN_VOID();
    1812              : }
        

Generated by: LCOV version 2.0-1