LCOV - code coverage report
Current view: top level - src/backend/statistics - extended_stats_funcs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 95.2 % 291 277
Test Date: 2026-03-02 06:15:00 Functions: 100.0 % 9 9
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * extended_stats_funcs.c
       4              :  *    Functions for manipulating extended statistics.
       5              :  *
       6              :  * This file includes the set of facilities required to support the direct
       7              :  * manipulations of extended statistics objects.
       8              :  *
       9              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      10              :  * Portions Copyright (c) 1994, Regents of the University of California
      11              :  *
      12              :  * IDENTIFICATION
      13              :  *    src/backend/statistics/extended_stats_funcs.c
      14              :  *
      15              :  *-------------------------------------------------------------------------
      16              :  */
      17              : #include "postgres.h"
      18              : 
      19              : #include "access/heapam.h"
      20              : #include "catalog/indexing.h"
      21              : #include "catalog/namespace.h"
      22              : #include "catalog/pg_database.h"
      23              : #include "catalog/pg_statistic_ext.h"
      24              : #include "catalog/pg_statistic_ext_data.h"
      25              : #include "miscadmin.h"
      26              : #include "nodes/makefuncs.h"
      27              : #include "nodes/nodeFuncs.h"
      28              : #include "optimizer/optimizer.h"
      29              : #include "statistics/extended_stats_internal.h"
      30              : #include "statistics/stat_utils.h"
      31              : #include "utils/acl.h"
      32              : #include "utils/array.h"
      33              : #include "utils/builtins.h"
      34              : #include "utils/fmgroids.h"
      35              : #include "utils/lsyscache.h"
      36              : #include "utils/syscache.h"
      37              : 
      38              : 
      39              : /*
      40              :  * Index of the arguments for the SQL functions.
      41              :  */
      42              : enum extended_stats_argnum
      43              : {
      44              :     RELSCHEMA_ARG = 0,
      45              :     RELNAME_ARG,
      46              :     STATSCHEMA_ARG,
      47              :     STATNAME_ARG,
      48              :     INHERITED_ARG,
      49              :     NDISTINCT_ARG,
      50              :     DEPENDENCIES_ARG,
      51              :     MOST_COMMON_VALS_ARG,
      52              :     MOST_COMMON_FREQS_ARG,
      53              :     MOST_COMMON_BASE_FREQS_ARG,
      54              :     NUM_EXTENDED_STATS_ARGS,
      55              : };
      56              : 
      57              : /*
      58              :  * The argument names and type OIDs of the arguments for the SQL
      59              :  * functions.
      60              :  */
      61              : static struct StatsArgInfo extarginfo[] =
      62              : {
      63              :     [RELSCHEMA_ARG] = {"schemaname", TEXTOID},
      64              :     [RELNAME_ARG] = {"relname", TEXTOID},
      65              :     [STATSCHEMA_ARG] = {"statistics_schemaname", TEXTOID},
      66              :     [STATNAME_ARG] = {"statistics_name", TEXTOID},
      67              :     [INHERITED_ARG] = {"inherited", BOOLOID},
      68              :     [NDISTINCT_ARG] = {"n_distinct", PG_NDISTINCTOID},
      69              :     [DEPENDENCIES_ARG] = {"dependencies", PG_DEPENDENCIESOID},
      70              :     [MOST_COMMON_VALS_ARG] = {"most_common_vals", TEXTARRAYOID},
      71              :     [MOST_COMMON_FREQS_ARG] = {"most_common_freqs", FLOAT8ARRAYOID},
      72              :     [MOST_COMMON_BASE_FREQS_ARG] = {"most_common_base_freqs", FLOAT8ARRAYOID},
      73              :     [NUM_EXTENDED_STATS_ARGS] = {0},
      74              : };
      75              : 
      76              : static bool extended_statistics_update(FunctionCallInfo fcinfo);
      77              : 
      78              : static HeapTuple get_pg_statistic_ext(Relation pg_stext, Oid nspoid,
      79              :                                       const char *stxname);
      80              : static bool delete_pg_statistic_ext_data(Oid stxoid, bool inherited);
      81              : 
      82              : /*
      83              :  * Track the extended statistics kinds expected for a pg_statistic_ext
      84              :  * tuple.
      85              :  */
      86              : typedef struct
      87              : {
      88              :     bool        ndistinct;
      89              :     bool        dependencies;
      90              :     bool        mcv;
      91              :     bool        expressions;
      92              : } StakindFlags;
      93              : 
      94              : static void expand_stxkind(HeapTuple tup, StakindFlags *enabled);
      95              : static void upsert_pg_statistic_ext_data(const Datum *values,
      96              :                                          const bool *nulls,
      97              :                                          const bool *replaces);
      98              : 
      99              : static bool check_mcvlist_array(const ArrayType *arr, int argindex,
     100              :                                 int required_ndims, int mcv_length);
     101              : static Datum import_mcv(const ArrayType *mcv_arr,
     102              :                         const ArrayType *freqs_arr,
     103              :                         const ArrayType *base_freqs_arr,
     104              :                         Oid *atttypids, int32 *atttypmods,
     105              :                         Oid *atttypcolls, int numattrs,
     106              :                         bool *ok);
     107              : 
     108              : 
     109              : /*
     110              :  * Fetch a pg_statistic_ext row by name and namespace OID.
     111              :  */
     112              : static HeapTuple
     113           94 : get_pg_statistic_ext(Relation pg_stext, Oid nspoid, const char *stxname)
     114              : {
     115              :     ScanKeyData key[2];
     116              :     SysScanDesc scan;
     117              :     HeapTuple   tup;
     118           94 :     Oid         stxoid = InvalidOid;
     119              : 
     120           94 :     ScanKeyInit(&key[0],
     121              :                 Anum_pg_statistic_ext_stxname,
     122              :                 BTEqualStrategyNumber,
     123              :                 F_NAMEEQ,
     124              :                 CStringGetDatum(stxname));
     125           94 :     ScanKeyInit(&key[1],
     126              :                 Anum_pg_statistic_ext_stxnamespace,
     127              :                 BTEqualStrategyNumber,
     128              :                 F_OIDEQ,
     129              :                 ObjectIdGetDatum(nspoid));
     130              : 
     131              :     /*
     132              :      * Try to find matching pg_statistic_ext row.
     133              :      */
     134           94 :     scan = systable_beginscan(pg_stext,
     135              :                               StatisticExtNameIndexId,
     136              :                               true,
     137              :                               NULL,
     138              :                               2,
     139              :                               key);
     140              : 
     141              :     /* Lookup is based on a unique index, so we get either 0 or 1 tuple. */
     142           94 :     tup = systable_getnext(scan);
     143              : 
     144           94 :     if (HeapTupleIsValid(tup))
     145           88 :         stxoid = ((Form_pg_statistic_ext) GETSTRUCT(tup))->oid;
     146              : 
     147           94 :     systable_endscan(scan);
     148              : 
     149           94 :     if (!OidIsValid(stxoid))
     150            6 :         return NULL;
     151              : 
     152           88 :     return SearchSysCacheCopy1(STATEXTOID, ObjectIdGetDatum(stxoid));
     153              : }
     154              : 
     155              : /*
     156              :  * Decode the stxkind column so that we know which stats types to expect,
     157              :  * returning a StakindFlags set depending on the stats kinds expected by
     158              :  * a pg_statistic_ext tuple.
     159              :  */
     160              : static void
     161           73 : expand_stxkind(HeapTuple tup, StakindFlags *enabled)
     162              : {
     163              :     Datum       datum;
     164              :     ArrayType  *arr;
     165              :     char       *kinds;
     166              : 
     167           73 :     datum = SysCacheGetAttrNotNull(STATEXTOID,
     168              :                                    tup,
     169              :                                    Anum_pg_statistic_ext_stxkind);
     170           73 :     arr = DatumGetArrayTypeP(datum);
     171           73 :     if (ARR_NDIM(arr) != 1 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != CHAROID)
     172            0 :         elog(ERROR, "stxkind is not a one-dimension char array");
     173              : 
     174           73 :     kinds = (char *) ARR_DATA_PTR(arr);
     175              : 
     176          189 :     for (int i = 0; i < ARR_DIMS(arr)[0]; i++)
     177              :     {
     178          116 :         switch (kinds[i])
     179              :         {
     180           24 :             case STATS_EXT_NDISTINCT:
     181           24 :                 enabled->ndistinct = true;
     182           24 :                 break;
     183           28 :             case STATS_EXT_DEPENDENCIES:
     184           28 :                 enabled->dependencies = true;
     185           28 :                 break;
     186           39 :             case STATS_EXT_MCV:
     187           39 :                 enabled->mcv = true;
     188           39 :                 break;
     189           25 :             case STATS_EXT_EXPRESSIONS:
     190           25 :                 enabled->expressions = true;
     191           25 :                 break;
     192            0 :             default:
     193            0 :                 elog(ERROR, "incorrect stxkind %c found", kinds[i]);
     194              :                 break;
     195              :         }
     196              :     }
     197           73 : }
     198              : 
     199              : /*
     200              :  * Perform the actual storage of a pg_statistic_ext_data tuple.
     201              :  */
     202              : static void
     203           73 : upsert_pg_statistic_ext_data(const Datum *values, const bool *nulls,
     204              :                              const bool *replaces)
     205              : {
     206              :     Relation    pg_stextdata;
     207              :     HeapTuple   stxdtup;
     208              :     HeapTuple   newtup;
     209              : 
     210           73 :     pg_stextdata = table_open(StatisticExtDataRelationId, RowExclusiveLock);
     211              : 
     212           73 :     stxdtup = SearchSysCache2(STATEXTDATASTXOID,
     213              :                               values[Anum_pg_statistic_ext_data_stxoid - 1],
     214           73 :                               values[Anum_pg_statistic_ext_data_stxdinherit - 1]);
     215              : 
     216           73 :     if (HeapTupleIsValid(stxdtup))
     217              :     {
     218           63 :         newtup = heap_modify_tuple(stxdtup,
     219              :                                    RelationGetDescr(pg_stextdata),
     220              :                                    values,
     221              :                                    nulls,
     222              :                                    replaces);
     223           63 :         CatalogTupleUpdate(pg_stextdata, &newtup->t_self, newtup);
     224           63 :         ReleaseSysCache(stxdtup);
     225              :     }
     226              :     else
     227              :     {
     228           10 :         newtup = heap_form_tuple(RelationGetDescr(pg_stextdata), values, nulls);
     229           10 :         CatalogTupleInsert(pg_stextdata, newtup);
     230              :     }
     231              : 
     232           73 :     heap_freetuple(newtup);
     233              : 
     234           73 :     CommandCounterIncrement();
     235              : 
     236           73 :     table_close(pg_stextdata, RowExclusiveLock);
     237           73 : }
     238              : 
     239              : /*
     240              :  * Insert or update an extended statistics object.
     241              :  *
     242              :  * Major errors, such as the table not existing or permission errors, are
     243              :  * reported as ERRORs.  There are a couple of paths that generate a WARNING,
     244              :  * like when the statistics object or its schema do not exist, a conversion
     245              :  * failure on one statistic kind, or when other statistic kinds may still
     246              :  * be updated.
     247              :  */
     248              : static bool
     249          106 : extended_statistics_update(FunctionCallInfo fcinfo)
     250              : {
     251              :     char       *relnspname;
     252              :     char       *relname;
     253              :     Oid         nspoid;
     254              :     char       *nspname;
     255              :     char       *stxname;
     256              :     bool        inherited;
     257          106 :     Relation    pg_stext = NULL;
     258          106 :     HeapTuple   tup = NULL;
     259              : 
     260          106 :     StakindFlags enabled = {false, false, false, false};
     261          106 :     StakindFlags has = {false, false, false, false};
     262              : 
     263              :     Form_pg_statistic_ext stxform;
     264              : 
     265          106 :     Datum       values[Natts_pg_statistic_ext_data] = {0};
     266          106 :     bool        nulls[Natts_pg_statistic_ext_data] = {0};
     267          106 :     bool        replaces[Natts_pg_statistic_ext_data] = {0};
     268          106 :     bool        success = true;
     269              :     Datum       exprdatum;
     270              :     bool        isnull;
     271          106 :     List       *exprs = NIL;
     272          106 :     int         numattnums = 0;
     273          106 :     int         numexprs = 0;
     274          106 :     int         numattrs = 0;
     275              : 
     276              :     /* arrays of type info, if we need them */
     277          106 :     Oid        *atttypids = NULL;
     278          106 :     int32      *atttypmods = NULL;
     279          106 :     Oid        *atttypcolls = NULL;
     280              :     Oid         relid;
     281          106 :     Oid         locked_table = InvalidOid;
     282              : 
     283              :     /*
     284              :      * Fill out the StakindFlags "has" structure based on which parameters
     285              :      * were provided to the function.
     286              :      *
     287              :      * The MCV stats composite value is an array of record type, but this is
     288              :      * externally represented as three arrays that must be interleaved into
     289              :      * the array of records (pg_stats_ext stores four arrays,
     290              :      * most_common_val_nulls is built from the contents of most_common_vals).
     291              :      * Therefore, none of the three array values is meaningful unless the
     292              :      * other two are also present and in sync in terms of array length.
     293              :      */
     294          248 :     has.mcv = (!PG_ARGISNULL(MOST_COMMON_VALS_ARG) &&
     295          139 :                !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) &&
     296           33 :                !PG_ARGISNULL(MOST_COMMON_BASE_FREQS_ARG));
     297          106 :     has.ndistinct = !PG_ARGISNULL(NDISTINCT_ARG);
     298          106 :     has.dependencies = !PG_ARGISNULL(DEPENDENCIES_ARG);
     299              : 
     300          106 :     if (RecoveryInProgress())
     301              :     {
     302            0 :         ereport(WARNING,
     303              :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     304              :                 errmsg("recovery is in progress"),
     305              :                 errhint("Statistics cannot be modified during recovery."));
     306            0 :         return false;
     307              :     }
     308              : 
     309              :     /* relation arguments */
     310          106 :     stats_check_required_arg(fcinfo, extarginfo, RELSCHEMA_ARG);
     311          103 :     relnspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
     312          103 :     stats_check_required_arg(fcinfo, extarginfo, RELNAME_ARG);
     313          100 :     relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
     314              : 
     315              :     /* extended statistics arguments */
     316          100 :     stats_check_required_arg(fcinfo, extarginfo, STATSCHEMA_ARG);
     317           97 :     nspname = TextDatumGetCString(PG_GETARG_DATUM(STATSCHEMA_ARG));
     318           97 :     stats_check_required_arg(fcinfo, extarginfo, STATNAME_ARG);
     319           94 :     stxname = TextDatumGetCString(PG_GETARG_DATUM(STATNAME_ARG));
     320           94 :     stats_check_required_arg(fcinfo, extarginfo, INHERITED_ARG);
     321           91 :     inherited = PG_GETARG_BOOL(INHERITED_ARG);
     322              : 
     323              :     /*
     324              :      * First open the relation where we expect to find the statistics.  This
     325              :      * is similar to relation and attribute statistics, so as ACL checks are
     326              :      * done before any locks are taken, even before any attempts related to
     327              :      * the extended stats object.
     328              :      */
     329           91 :     relid = RangeVarGetRelidExtended(makeRangeVar(relnspname, relname, -1),
     330              :                                      ShareUpdateExclusiveLock, 0,
     331              :                                      RangeVarCallbackForStats, &locked_table);
     332              : 
     333           82 :     nspoid = get_namespace_oid(nspname, true);
     334           82 :     if (nspoid == InvalidOid)
     335              :     {
     336            3 :         ereport(WARNING,
     337              :                 errcode(ERRCODE_UNDEFINED_OBJECT),
     338              :                 errmsg("could not find schema \"%s\"", nspname));
     339            3 :         success = false;
     340            3 :         goto cleanup;
     341              :     }
     342              : 
     343           79 :     pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock);
     344           79 :     tup = get_pg_statistic_ext(pg_stext, nspoid, stxname);
     345              : 
     346           79 :     if (!HeapTupleIsValid(tup))
     347              :     {
     348            3 :         ereport(WARNING,
     349              :                 errcode(ERRCODE_UNDEFINED_OBJECT),
     350              :                 errmsg("could not find extended statistics object \"%s.%s\"",
     351              :                        nspname, stxname));
     352            3 :         success = false;
     353            3 :         goto cleanup;
     354              :     }
     355              : 
     356           76 :     stxform = (Form_pg_statistic_ext) GETSTRUCT(tup);
     357              : 
     358              :     /*
     359              :      * The relation tracked by the stats object has to match with the relation
     360              :      * we have already locked.
     361              :      */
     362           76 :     if (stxform->stxrelid != relid)
     363              :     {
     364            3 :         ereport(WARNING,
     365              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     366              :                 errmsg("could not restore extended statistics object \"%s.%s\": incorrect relation \"%s.%s\" specified",
     367              :                        nspname, stxname,
     368              :                        relnspname, relname));
     369              : 
     370            3 :         success = false;
     371            3 :         goto cleanup;
     372              :     }
     373              : 
     374              :     /* Find out what extended statistics kinds we should expect. */
     375           73 :     expand_stxkind(tup, &enabled);
     376           73 :     numattnums = stxform->stxkeys.dim1;
     377              : 
     378              :     /* decode expression (if any) */
     379           73 :     exprdatum = SysCacheGetAttr(STATEXTOID,
     380              :                                 tup,
     381              :                                 Anum_pg_statistic_ext_stxexprs,
     382              :                                 &isnull);
     383           73 :     if (!isnull)
     384              :     {
     385              :         char       *s;
     386              : 
     387           25 :         s = TextDatumGetCString(exprdatum);
     388           25 :         exprs = (List *) stringToNode(s);
     389           25 :         pfree(s);
     390              : 
     391              :         /*
     392              :          * Run the expressions through eval_const_expressions().  This is not
     393              :          * just an optimization, but is necessary, because the planner will be
     394              :          * comparing them to similarly-processed qual clauses, and may fail to
     395              :          * detect valid matches without this.
     396              :          *
     397              :          * We must not use canonicalize_qual(), however, since these are not
     398              :          * qual expressions.
     399              :          */
     400           25 :         exprs = (List *) eval_const_expressions(NULL, (Node *) exprs);
     401              : 
     402              :         /* May as well fix opfuncids too */
     403           25 :         fix_opfuncids((Node *) exprs);
     404              : 
     405              :         /* Compute the number of expression, for input validation. */
     406           25 :         numexprs = list_length(exprs);
     407              :     }
     408              : 
     409           73 :     numattrs = numattnums + numexprs;
     410              : 
     411              :     /*
     412              :      * If the object cannot support ndistinct, we should not have data for it.
     413              :      */
     414           73 :     if (has.ndistinct && !enabled.ndistinct)
     415              :     {
     416            3 :         ereport(WARNING,
     417              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     418              :                 errmsg("cannot specify parameter \"%s\"",
     419              :                        extarginfo[NDISTINCT_ARG].argname),
     420              :                 errhint("Extended statistics object \"%s.%s\" does not support statistics of this type.",
     421              :                         nspname, stxname));
     422              : 
     423            3 :         has.ndistinct = false;
     424            3 :         success = false;
     425              :     }
     426              : 
     427              :     /*
     428              :      * If the object cannot support dependencies, we should not have data for
     429              :      * it.
     430              :      */
     431           73 :     if (has.dependencies && !enabled.dependencies)
     432              :     {
     433            3 :         ereport(WARNING,
     434              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     435              :                 errmsg("cannot specify parameter \"%s\"",
     436              :                        extarginfo[DEPENDENCIES_ARG].argname),
     437              :                 errhint("Extended statistics object \"%s.%s\" does not support statistics of this type.",
     438              :                         nspname, stxname));
     439            3 :         has.dependencies = false;
     440            3 :         success = false;
     441              :     }
     442              : 
     443              :     /*
     444              :      * If the object cannot hold an MCV value, but any of the MCV parameters
     445              :      * are set, then issue a WARNING and ensure that we do not try to load MCV
     446              :      * stats later.  In pg_stats_ext, most_common_val_nulls, most_common_freqs
     447              :      * and most_common_base_freqs are NULL if most_common_vals is NULL.
     448              :      */
     449           73 :     if (!enabled.mcv)
     450              :     {
     451           34 :         if (!PG_ARGISNULL(MOST_COMMON_VALS_ARG) ||
     452           31 :             !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) ||
     453           31 :             !PG_ARGISNULL(MOST_COMMON_BASE_FREQS_ARG))
     454              :         {
     455            3 :             ereport(WARNING,
     456              :                     errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     457              :                     errmsg("cannot specify parameters \"%s\", \"%s\" or \"%s\"",
     458              :                            extarginfo[MOST_COMMON_VALS_ARG].argname,
     459              :                            extarginfo[MOST_COMMON_FREQS_ARG].argname,
     460              :                            extarginfo[MOST_COMMON_BASE_FREQS_ARG].argname),
     461              :                     errhint("Extended statistics object \"%s.%s\" does not support statistics of this type.",
     462              :                             nspname, stxname));
     463              : 
     464            3 :             has.mcv = false;
     465            3 :             success = false;
     466              :         }
     467              :     }
     468           39 :     else if (!has.mcv)
     469              :     {
     470              :         /*
     471              :          * If we do not have all of the MCV arrays set while the extended
     472              :          * statistics object expects something, something is wrong.  This
     473              :          * issues a WARNING if a partial input has been provided.
     474              :          */
     475           12 :         if (!PG_ARGISNULL(MOST_COMMON_VALS_ARG) ||
     476            6 :             !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) ||
     477            3 :             !PG_ARGISNULL(MOST_COMMON_BASE_FREQS_ARG))
     478              :         {
     479            9 :             ereport(WARNING,
     480              :                     errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     481              :                     errmsg("could not use \"%s\", \"%s\" and \"%s\": missing one or more parameters",
     482              :                            extarginfo[MOST_COMMON_VALS_ARG].argname,
     483              :                            extarginfo[MOST_COMMON_FREQS_ARG].argname,
     484              :                            extarginfo[MOST_COMMON_BASE_FREQS_ARG].argname));
     485            9 :             success = false;
     486              :         }
     487              :     }
     488              : 
     489              :     /*
     490              :      * Either of these statistic types requires that we supply a semi-filled
     491              :      * VacAttrStatP array.
     492              :      *
     493              :      * It is not possible to use the existing lookup_var_attr_stats() and
     494              :      * examine_attribute() because these functions will skip attributes where
     495              :      * attstattarget is 0, and we may have statistics data to import for those
     496              :      * attributes.
     497              :      */
     498           73 :     if (has.mcv)
     499              :     {
     500           27 :         atttypids = palloc0_array(Oid, numattrs);
     501           27 :         atttypmods = palloc0_array(int32, numattrs);
     502           27 :         atttypcolls = palloc0_array(Oid, numattrs);
     503              : 
     504              :         /*
     505              :          * The leading stxkeys are attribute numbers up through numattnums.
     506              :          * These keys must be in ascending AttNumber order, but we do not rely
     507              :          * on that.
     508              :          */
     509           78 :         for (int i = 0; i < numattnums; i++)
     510              :         {
     511           51 :             AttrNumber  attnum = stxform->stxkeys.values[i];
     512           51 :             HeapTuple   atup = SearchSysCache2(ATTNUM,
     513              :                                                ObjectIdGetDatum(relid),
     514              :                                                Int16GetDatum(attnum));
     515              : 
     516              :             Form_pg_attribute attr;
     517              : 
     518              :             /* Attribute not found */
     519           51 :             if (!HeapTupleIsValid(atup))
     520            0 :                 elog(ERROR, "stxkeys references nonexistent attnum %d", attnum);
     521              : 
     522           51 :             attr = (Form_pg_attribute) GETSTRUCT(atup);
     523              : 
     524           51 :             if (attr->attisdropped)
     525            0 :                 elog(ERROR, "stxkeys references dropped attnum %d", attnum);
     526              : 
     527           51 :             atttypids[i] = attr->atttypid;
     528           51 :             atttypmods[i] = attr->atttypmod;
     529           51 :             atttypcolls[i] = attr->attcollation;
     530           51 :             ReleaseSysCache(atup);
     531              :         }
     532              : 
     533              :         /*
     534              :          * After all the positive number attnums in stxkeys come the negative
     535              :          * numbers (if any) which represent expressions in the order that they
     536              :          * appear in stxdexpr.  Because the expressions are always
     537              :          * monotonically decreasing from -1, there is no point in looking at
     538              :          * the values in stxkeys, it's enough to know how many of them there
     539              :          * are.
     540              :          */
     541           42 :         for (int i = numattnums; i < numattrs; i++)
     542              :         {
     543           15 :             Node       *expr = list_nth(exprs, i - numattnums);
     544              : 
     545           15 :             atttypids[i] = exprType(expr);
     546           15 :             atttypmods[i] = exprTypmod(expr);
     547           15 :             atttypcolls[i] = exprCollation(expr);
     548              :         }
     549              :     }
     550              : 
     551              :     /*
     552              :      * Populate the pg_statistic_ext_data result tuple.
     553              :      */
     554              : 
     555              :     /* Primary Key: cannot be NULL or replaced. */
     556           73 :     values[Anum_pg_statistic_ext_data_stxoid - 1] = ObjectIdGetDatum(stxform->oid);
     557           73 :     nulls[Anum_pg_statistic_ext_data_stxoid - 1] = false;
     558           73 :     values[Anum_pg_statistic_ext_data_stxdinherit - 1] = BoolGetDatum(inherited);
     559           73 :     nulls[Anum_pg_statistic_ext_data_stxdinherit - 1] = false;
     560              : 
     561              :     /* All unspecified parameters will be left unmodified */
     562           73 :     nulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true;
     563           73 :     nulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = true;
     564           73 :     nulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
     565           73 :     nulls[Anum_pg_statistic_ext_data_stxdexpr - 1] = true;
     566              : 
     567              :     /*
     568              :      * For each stats kind, deserialize the data at hand and perform a round
     569              :      * of validation.  The resulting tuple is filled with a set of updated
     570              :      * values.
     571              :      */
     572              : 
     573           73 :     if (has.ndistinct)
     574              :     {
     575           21 :         Datum       ndistinct_datum = PG_GETARG_DATUM(NDISTINCT_ARG);
     576           21 :         bytea      *data = DatumGetByteaPP(ndistinct_datum);
     577           21 :         MVNDistinct *ndistinct = statext_ndistinct_deserialize(data);
     578              : 
     579           21 :         if (statext_ndistinct_validate(ndistinct, &stxform->stxkeys,
     580              :                                        numexprs, WARNING))
     581              :         {
     582           15 :             values[Anum_pg_statistic_ext_data_stxdndistinct - 1] = ndistinct_datum;
     583           15 :             nulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = false;
     584           15 :             replaces[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true;
     585              :         }
     586              :         else
     587            6 :             success = false;
     588              : 
     589           21 :         statext_ndistinct_free(ndistinct);
     590              :     }
     591              : 
     592           73 :     if (has.dependencies)
     593              :     {
     594           19 :         Datum       dependencies_datum = PG_GETARG_DATUM(DEPENDENCIES_ARG);
     595           19 :         bytea      *data = DatumGetByteaPP(dependencies_datum);
     596           19 :         MVDependencies *dependencies = statext_dependencies_deserialize(data);
     597              : 
     598           19 :         if (statext_dependencies_validate(dependencies, &stxform->stxkeys,
     599              :                                           numexprs, WARNING))
     600              :         {
     601           13 :             values[Anum_pg_statistic_ext_data_stxddependencies - 1] = dependencies_datum;
     602           13 :             nulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = false;
     603           13 :             replaces[Anum_pg_statistic_ext_data_stxddependencies - 1] = true;
     604              :         }
     605              :         else
     606            6 :             success = false;
     607              : 
     608           19 :         statext_dependencies_free(dependencies);
     609              :     }
     610              : 
     611           73 :     if (has.mcv)
     612              :     {
     613              :         Datum       datum;
     614           27 :         bool        val_ok = false;
     615              : 
     616           27 :         datum = import_mcv(PG_GETARG_ARRAYTYPE_P(MOST_COMMON_VALS_ARG),
     617           27 :                            PG_GETARG_ARRAYTYPE_P(MOST_COMMON_FREQS_ARG),
     618           27 :                            PG_GETARG_ARRAYTYPE_P(MOST_COMMON_BASE_FREQS_ARG),
     619              :                            atttypids, atttypmods, atttypcolls, numattrs,
     620              :                            &val_ok);
     621              : 
     622           27 :         if (val_ok)
     623              :         {
     624              :             Assert(datum != (Datum) 0);
     625           15 :             values[Anum_pg_statistic_ext_data_stxdmcv - 1] = datum;
     626           15 :             nulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = false;
     627           15 :             replaces[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
     628              :         }
     629              :         else
     630           12 :             success = false;
     631              :     }
     632              : 
     633           73 :     upsert_pg_statistic_ext_data(values, nulls, replaces);
     634              : 
     635           82 : cleanup:
     636           82 :     if (HeapTupleIsValid(tup))
     637           76 :         heap_freetuple(tup);
     638           82 :     if (pg_stext != NULL)
     639           79 :         table_close(pg_stext, RowExclusiveLock);
     640           82 :     if (atttypids != NULL)
     641           27 :         pfree(atttypids);
     642           82 :     if (atttypmods != NULL)
     643           27 :         pfree(atttypmods);
     644           82 :     if (atttypcolls != NULL)
     645           27 :         pfree(atttypcolls);
     646           82 :     return success;
     647              : }
     648              : 
     649              : /*
     650              :  * Consistency checks to ensure that other mcvlist arrays are in alignment
     651              :  * with the mcv array.
     652              :  */
     653              : static bool
     654           39 : check_mcvlist_array(const ArrayType *arr, int argindex, int required_ndims,
     655              :                     int mcv_length)
     656              : {
     657           39 :     if (ARR_NDIM(arr) != required_ndims)
     658              :     {
     659            0 :         ereport(WARNING,
     660              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     661              :                 errmsg("could not parse array \"%s\": incorrect number of dimensions (%d required)",
     662              :                        extarginfo[argindex].argname, required_ndims));
     663            0 :         return false;
     664              :     }
     665              : 
     666           39 :     if (array_contains_nulls(arr))
     667              :     {
     668            0 :         ereport(WARNING,
     669              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     670              :                 errmsg("could not parse array \"%s\": NULL value found",
     671              :                        extarginfo[argindex].argname));
     672            0 :         return false;
     673              :     }
     674              : 
     675           39 :     if (ARR_DIMS(arr)[0] != mcv_length)
     676              :     {
     677            6 :         ereport(WARNING,
     678              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     679              :                 errmsg("could not parse array \"%s\": incorrect number of elements (same as \"%s\" required)",
     680              :                        extarginfo[argindex].argname,
     681              :                        extarginfo[MOST_COMMON_VALS_ARG].argname));
     682            6 :         return false;
     683              :     }
     684              : 
     685           33 :     return true;
     686              : }
     687              : 
     688              : /*
     689              :  * Create the stxdmcv datum from the equal-sized arrays of most common values,
     690              :  * their null flags, and the frequency and base frequency associated with
     691              :  * each value.
     692              :  */
     693              : static Datum
     694           27 : import_mcv(const ArrayType *mcv_arr, const ArrayType *freqs_arr,
     695              :            const ArrayType *base_freqs_arr, Oid *atttypids, int32 *atttypmods,
     696              :            Oid *atttypcolls, int numattrs, bool *ok)
     697              : {
     698              :     int         nitems;
     699              :     Datum      *mcv_elems;
     700              :     bool       *mcv_nulls;
     701              :     int         check_nummcv;
     702           27 :     Datum       mcv = (Datum) 0;
     703              : 
     704           27 :     *ok = false;
     705              : 
     706              :     /*
     707              :      * mcv_arr is an array of arrays.  Each inner array must have the same
     708              :      * number of elements "numattrs".
     709              :      */
     710           27 :     if (ARR_NDIM(mcv_arr) != 2)
     711              :     {
     712            3 :         ereport(WARNING,
     713              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     714              :                 errmsg("could not parse array \"%s\": incorrect number of dimensions (%d required)",
     715              :                        extarginfo[MOST_COMMON_VALS_ARG].argname, 2));
     716            3 :         goto mcv_error;
     717              :     }
     718              : 
     719           24 :     if (ARR_DIMS(mcv_arr)[1] != numattrs)
     720              :     {
     721            3 :         ereport(WARNING,
     722              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     723              :                 errmsg("could not parse array \"%s\": found %d attributes but expected %d",
     724              :                        extarginfo[MOST_COMMON_VALS_ARG].argname,
     725              :                        ARR_DIMS(mcv_arr)[1], numattrs));
     726            3 :         goto mcv_error;
     727              :     }
     728              : 
     729              :     /*
     730              :      * "most_common_freqs" and "most_common_base_freqs" arrays must be of the
     731              :      * same length, one-dimension and cannot contain NULLs.  We use mcv_arr as
     732              :      * the reference array for determining their length.
     733              :      */
     734           21 :     nitems = ARR_DIMS(mcv_arr)[0];
     735           21 :     if (!check_mcvlist_array(freqs_arr, MOST_COMMON_FREQS_ARG, 1, nitems) ||
     736           18 :         !check_mcvlist_array(base_freqs_arr, MOST_COMMON_BASE_FREQS_ARG, 1, nitems))
     737              :     {
     738              :         /* inconsistent input arrays found */
     739            6 :         goto mcv_error;
     740              :     }
     741              : 
     742              :     /*
     743              :      * This part builds the contents for "most_common_val_nulls", based on the
     744              :      * values from "most_common_vals".
     745              :      */
     746           15 :     deconstruct_array_builtin(mcv_arr, TEXTOID, &mcv_elems,
     747              :                               &mcv_nulls, &check_nummcv);
     748              : 
     749           15 :     mcv = statext_mcv_import(WARNING, numattrs,
     750              :                              atttypids, atttypmods, atttypcolls,
     751              :                              nitems, mcv_elems, mcv_nulls,
     752           15 :                              (float8 *) ARR_DATA_PTR(freqs_arr),
     753           15 :                              (float8 *) ARR_DATA_PTR(base_freqs_arr));
     754              : 
     755           15 :     *ok = (mcv != (Datum) 0);
     756              : 
     757           27 : mcv_error:
     758           27 :     return mcv;
     759              : }
     760              : 
     761              : /*
     762              :  * Remove an existing pg_statistic_ext_data row for a given pg_statistic_ext
     763              :  * row and "inherited" pair.
     764              :  */
     765              : static bool
     766            9 : delete_pg_statistic_ext_data(Oid stxoid, bool inherited)
     767              : {
     768            9 :     Relation    sed = table_open(StatisticExtDataRelationId, RowExclusiveLock);
     769              :     HeapTuple   oldtup;
     770            9 :     bool        result = false;
     771              : 
     772              :     /* Is there already a pg_statistic_ext_data tuple for this attribute? */
     773            9 :     oldtup = SearchSysCache2(STATEXTDATASTXOID,
     774              :                              ObjectIdGetDatum(stxoid),
     775              :                              BoolGetDatum(inherited));
     776              : 
     777            9 :     if (HeapTupleIsValid(oldtup))
     778              :     {
     779            6 :         CatalogTupleDelete(sed, &oldtup->t_self);
     780            6 :         ReleaseSysCache(oldtup);
     781            6 :         result = true;
     782              :     }
     783              : 
     784            9 :     table_close(sed, RowExclusiveLock);
     785              : 
     786            9 :     CommandCounterIncrement();
     787              : 
     788            9 :     return result;
     789              : }
     790              : 
     791              : /*
     792              :  * Restore (insert or replace) statistics for the given statistics object.
     793              :  *
     794              :  * This function accepts variadic arguments in key-value pairs, which are
     795              :  * given to stats_fill_fcinfo_from_arg_pairs to be mapped into positional
     796              :  * arguments.
     797              :  */
     798              : Datum
     799          106 : pg_restore_extended_stats(PG_FUNCTION_ARGS)
     800              : {
     801          106 :     LOCAL_FCINFO(positional_fcinfo, NUM_EXTENDED_STATS_ARGS);
     802          106 :     bool        result = true;
     803              : 
     804          106 :     InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_EXTENDED_STATS_ARGS,
     805              :                              InvalidOid, NULL, NULL);
     806              : 
     807          106 :     if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo, extarginfo))
     808            0 :         result = false;
     809              : 
     810          106 :     if (!extended_statistics_update(positional_fcinfo))
     811           51 :         result = false;
     812              : 
     813           82 :     PG_RETURN_BOOL(result);
     814              : }
     815              : 
     816              : /*
     817              :  * Delete statistics for the given statistics object.
     818              :  */
     819              : Datum
     820           42 : pg_clear_extended_stats(PG_FUNCTION_ARGS)
     821              : {
     822              :     char       *relnspname;
     823              :     char       *relname;
     824              :     char       *nspname;
     825              :     Oid         nspoid;
     826              :     Oid         relid;
     827              :     char       *stxname;
     828              :     bool        inherited;
     829              :     Relation    pg_stext;
     830              :     HeapTuple   tup;
     831              :     Form_pg_statistic_ext stxform;
     832           42 :     Oid         locked_table = InvalidOid;
     833              : 
     834              :     /* relation arguments */
     835           42 :     stats_check_required_arg(fcinfo, extarginfo, RELSCHEMA_ARG);
     836           39 :     relnspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG));
     837           39 :     stats_check_required_arg(fcinfo, extarginfo, RELNAME_ARG);
     838           36 :     relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG));
     839              : 
     840              :     /* extended statistics arguments */
     841           36 :     stats_check_required_arg(fcinfo, extarginfo, STATSCHEMA_ARG);
     842           33 :     nspname = TextDatumGetCString(PG_GETARG_DATUM(STATSCHEMA_ARG));
     843           33 :     stats_check_required_arg(fcinfo, extarginfo, STATNAME_ARG);
     844           30 :     stxname = TextDatumGetCString(PG_GETARG_DATUM(STATNAME_ARG));
     845           30 :     stats_check_required_arg(fcinfo, extarginfo, INHERITED_ARG);
     846           27 :     inherited = PG_GETARG_BOOL(INHERITED_ARG);
     847              : 
     848           27 :     if (RecoveryInProgress())
     849              :     {
     850            0 :         ereport(WARNING,
     851              :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     852              :                 errmsg("recovery is in progress"),
     853              :                 errhint("Statistics cannot be modified during recovery."));
     854            0 :         PG_RETURN_VOID();
     855              :     }
     856              : 
     857              :     /*
     858              :      * First open the relation where we expect to find the statistics.  This
     859              :      * is similar to relation and attribute statistics, so as ACL checks are
     860              :      * done before any locks are taken, even before any attempts related to
     861              :      * the extended stats object.
     862              :      */
     863           27 :     relid = RangeVarGetRelidExtended(makeRangeVar(relnspname, relname, -1),
     864              :                                      ShareUpdateExclusiveLock, 0,
     865              :                                      RangeVarCallbackForStats, &locked_table);
     866              : 
     867              :     /* Now check if the namespace of the stats object exists. */
     868           18 :     nspoid = get_namespace_oid(nspname, true);
     869           18 :     if (nspoid == InvalidOid)
     870              :     {
     871            3 :         ereport(WARNING,
     872              :                 errcode(ERRCODE_UNDEFINED_OBJECT),
     873              :                 errmsg("could not find schema \"%s\"", nspname));
     874            3 :         PG_RETURN_VOID();
     875              :     }
     876              : 
     877           15 :     pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock);
     878           15 :     tup = get_pg_statistic_ext(pg_stext, nspoid, stxname);
     879              : 
     880           15 :     if (!HeapTupleIsValid(tup))
     881              :     {
     882            3 :         table_close(pg_stext, RowExclusiveLock);
     883            3 :         ereport(WARNING,
     884              :                 errcode(ERRCODE_UNDEFINED_OBJECT),
     885              :                 errmsg("could not find extended statistics object \"%s.%s\"",
     886              :                        nspname, stxname));
     887            3 :         PG_RETURN_VOID();
     888              :     }
     889              : 
     890           12 :     stxform = (Form_pg_statistic_ext) GETSTRUCT(tup);
     891              : 
     892              :     /*
     893              :      * This should be consistent, based on the lock taken on the table when we
     894              :      * started.
     895              :      */
     896           12 :     if (stxform->stxrelid != relid)
     897              :     {
     898            3 :         table_close(pg_stext, RowExclusiveLock);
     899            3 :         ereport(WARNING,
     900              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     901              :                 errmsg("could not clear extended statistics object \"%s.%s\": incorrect relation \"%s.%s\" specified",
     902              :                        get_namespace_name(nspoid), stxname,
     903              :                        relnspname, relname));
     904            3 :         PG_RETURN_VOID();
     905              :     }
     906              : 
     907            9 :     delete_pg_statistic_ext_data(stxform->oid, inherited);
     908            9 :     heap_freetuple(tup);
     909              : 
     910            9 :     table_close(pg_stext, RowExclusiveLock);
     911              : 
     912            9 :     PG_RETURN_VOID();
     913              : }
        

Generated by: LCOV version 2.0-1