LCOV - code coverage report
Current view: top level - src/backend/statistics - extended_stats_funcs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 96.7 % 569 550
Test Date: 2026-07-23 04:15:41 Functions: 100.0 % 16 16
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 78.2 % 335 262

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

Generated by: LCOV version 2.0-1