LCOV - code coverage report
Current view: top level - src/backend/commands - statscmds.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 92.4 % 290 268
Test Date: 2026-04-16 17:16:33 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * statscmds.c
       4              :  *    Commands for creating and altering extended statistics objects
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  *
      10              :  * IDENTIFICATION
      11              :  *    src/backend/commands/statscmds.c
      12              :  *
      13              :  *-------------------------------------------------------------------------
      14              :  */
      15              : #include "postgres.h"
      16              : 
      17              : #include "access/htup_details.h"
      18              : #include "access/relation.h"
      19              : #include "access/table.h"
      20              : #include "catalog/catalog.h"
      21              : #include "catalog/dependency.h"
      22              : #include "catalog/indexing.h"
      23              : #include "catalog/namespace.h"
      24              : #include "catalog/objectaccess.h"
      25              : #include "catalog/pg_namespace.h"
      26              : #include "catalog/pg_statistic_ext.h"
      27              : #include "catalog/pg_statistic_ext_data.h"
      28              : #include "commands/comment.h"
      29              : #include "commands/defrem.h"
      30              : #include "miscadmin.h"
      31              : #include "nodes/makefuncs.h"
      32              : #include "nodes/nodeFuncs.h"
      33              : #include "optimizer/optimizer.h"
      34              : #include "statistics/statistics.h"
      35              : #include "utils/acl.h"
      36              : #include "utils/builtins.h"
      37              : #include "utils/inval.h"
      38              : #include "utils/lsyscache.h"
      39              : #include "utils/rel.h"
      40              : #include "utils/syscache.h"
      41              : #include "utils/typcache.h"
      42              : 
      43              : 
      44              : static char *ChooseExtendedStatisticName(const char *name1, const char *name2,
      45              :                                          const char *label, Oid namespaceid);
      46              : static char *ChooseExtendedStatisticNameAddition(List *exprs);
      47              : 
      48              : 
      49              : /* qsort comparator for the attnums in CreateStatistics */
      50              : static int
      51          506 : compare_int16(const void *a, const void *b)
      52              : {
      53          506 :     int         av = *(const int16 *) a;
      54          506 :     int         bv = *(const int16 *) b;
      55              : 
      56              :     /* this can't overflow if int is wider than int16 */
      57          506 :     return (av - bv);
      58              : }
      59              : 
      60              : /*
      61              :  *      CREATE STATISTICS
      62              :  */
      63              : ObjectAddress
      64          649 : CreateStatistics(CreateStatsStmt *stmt, bool check_rights)
      65              : {
      66              :     int16       attnums[STATS_MAX_DIMENSIONS];
      67          649 :     int         nattnums = 0;
      68              :     int         numcols;
      69              :     char       *namestr;
      70              :     NameData    stxname;
      71              :     Oid         statoid;
      72              :     Oid         namespaceId;
      73          649 :     Oid         stxowner = GetUserId();
      74              :     HeapTuple   htup;
      75              :     Datum       values[Natts_pg_statistic_ext];
      76              :     bool        nulls[Natts_pg_statistic_ext];
      77              :     int2vector *stxkeys;
      78          649 :     List       *stxexprs = NIL;
      79              :     Datum       exprsDatum;
      80              :     Relation    statrel;
      81          649 :     Relation    rel = NULL;
      82              :     Oid         relid;
      83              :     ObjectAddress parentobject,
      84              :                 myself;
      85              :     Datum       types[4];       /* one for each possible type of statistic */
      86              :     int         ntypes;
      87              :     ArrayType  *stxkind;
      88              :     bool        build_ndistinct;
      89              :     bool        build_dependencies;
      90              :     bool        build_mcv;
      91              :     bool        build_expressions;
      92          649 :     bool        requested_type = false;
      93              :     int         i;
      94              :     ListCell   *cell;
      95              :     ListCell   *cell2;
      96              : 
      97              :     Assert(IsA(stmt, CreateStatsStmt));
      98              : 
      99              :     /*
     100              :      * Examine the FROM clause.  Currently, we only allow it to be a single
     101              :      * simple table, but later we'll probably allow multiple tables and JOIN
     102              :      * syntax.  The grammar is already prepared for that, so we have to check
     103              :      * here that what we got is what we can support.
     104              :      */
     105          649 :     if (list_length(stmt->relations) != 1)
     106            0 :         ereport(ERROR,
     107              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     108              :                  errmsg("only a single relation is allowed in CREATE STATISTICS")));
     109              : 
     110         1278 :     foreach(cell, stmt->relations)
     111              :     {
     112          649 :         Node       *rln = (Node *) lfirst(cell);
     113              : 
     114          649 :         if (!IsA(rln, RangeVar))
     115            0 :             ereport(ERROR,
     116              :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     117              :                      errmsg("only a single relation is allowed in CREATE STATISTICS")));
     118              : 
     119              :         /*
     120              :          * CREATE STATISTICS will influence future execution plans but does
     121              :          * not interfere with currently executing plans.  So it should be
     122              :          * enough to take only ShareUpdateExclusiveLock on relation,
     123              :          * conflicting with ANALYZE and other DDL that sets statistical
     124              :          * information, but not with normal queries.
     125              :          */
     126          649 :         rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
     127              : 
     128              :         /* Restrict to allowed relation types */
     129          649 :         if (rel->rd_rel->relkind != RELKIND_RELATION &&
     130           53 :             rel->rd_rel->relkind != RELKIND_MATVIEW &&
     131           49 :             rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
     132           40 :             rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
     133           20 :             ereport(ERROR,
     134              :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     135              :                      errmsg("cannot define statistics for relation \"%s\"",
     136              :                             RelationGetRelationName(rel)),
     137              :                      errdetail_relkind_not_supported(rel->rd_rel->relkind)));
     138              : 
     139              :         /*
     140              :          * You must own the relation to create stats on it.
     141              :          *
     142              :          * NB: Concurrent changes could cause this function's lookup to find a
     143              :          * different relation than a previous lookup by the caller, so we must
     144              :          * perform this check even when check_rights == false.
     145              :          */
     146          629 :         if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), stxowner))
     147            0 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
     148            0 :                            RelationGetRelationName(rel));
     149              : 
     150              :         /* Creating statistics on system catalogs is not allowed */
     151          629 :         if (!allowSystemTableMods && IsSystemRelation(rel))
     152            0 :             ereport(ERROR,
     153              :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     154              :                      errmsg("permission denied: \"%s\" is a system catalog",
     155              :                             RelationGetRelationName(rel))));
     156              :     }
     157              : 
     158              :     Assert(rel);
     159          629 :     relid = RelationGetRelid(rel);
     160              : 
     161              :     /*
     162              :      * If the node has a name, split it up and determine creation namespace.
     163              :      * If not, put the object in the same namespace as the relation, and cons
     164              :      * up a name for it.  (This can happen either via "CREATE STATISTICS ..."
     165              :      * or via "CREATE TABLE ... (LIKE)".)
     166              :      */
     167          629 :     if (stmt->defnames)
     168          553 :         namespaceId = QualifiedNameGetCreationNamespace(stmt->defnames,
     169              :                                                         &namestr);
     170              :     else
     171              :     {
     172           76 :         namespaceId = RelationGetNamespace(rel);
     173           76 :         namestr = ChooseExtendedStatisticName(RelationGetRelationName(rel),
     174           76 :                                               ChooseExtendedStatisticNameAddition(stmt->exprs),
     175              :                                               "stat",
     176              :                                               namespaceId);
     177              :     }
     178          629 :     namestrcpy(&stxname, namestr);
     179              : 
     180              :     /*
     181              :      * Check we have creation rights in target namespace.  Skip check if
     182              :      * caller doesn't want it.
     183              :      */
     184          629 :     if (check_rights)
     185              :     {
     186              :         AclResult   aclresult;
     187              : 
     188          576 :         aclresult = object_aclcheck(NamespaceRelationId, namespaceId,
     189              :                                     GetUserId(), ACL_CREATE);
     190          576 :         if (aclresult != ACLCHECK_OK)
     191           16 :             aclcheck_error(aclresult, OBJECT_SCHEMA,
     192           16 :                            get_namespace_name(namespaceId));
     193              :     }
     194              : 
     195              :     /*
     196              :      * Deal with the possibility that the statistics object already exists.
     197              :      */
     198          613 :     if (SearchSysCacheExists2(STATEXTNAMENSP,
     199              :                               CStringGetDatum(namestr),
     200              :                               ObjectIdGetDatum(namespaceId)))
     201              :     {
     202            4 :         if (stmt->if_not_exists)
     203              :         {
     204              :             /*
     205              :              * Since stats objects aren't members of extensions (see comments
     206              :              * below), no need for checkMembershipInCurrentExtension here.
     207              :              */
     208            4 :             ereport(NOTICE,
     209              :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
     210              :                      errmsg("statistics object \"%s\" already exists, skipping",
     211              :                             namestr)));
     212            4 :             relation_close(rel, NoLock);
     213            4 :             return InvalidObjectAddress;
     214              :         }
     215              : 
     216            0 :         ereport(ERROR,
     217              :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     218              :                  errmsg("statistics object \"%s\" already exists", namestr)));
     219              :     }
     220              : 
     221              :     /*
     222              :      * Make sure no more than STATS_MAX_DIMENSIONS columns are used. There
     223              :      * might be duplicates and so on, but we'll deal with those later.
     224              :      */
     225          609 :     numcols = list_length(stmt->exprs);
     226          609 :     if (numcols > STATS_MAX_DIMENSIONS)
     227           12 :         ereport(ERROR,
     228              :                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
     229              :                  errmsg("cannot have more than %d columns in statistics",
     230              :                         STATS_MAX_DIMENSIONS)));
     231              : 
     232              :     /*
     233              :      * Convert the expression list to a simple array of attnums, but also keep
     234              :      * a list of more complex expressions.  While at it, enforce some
     235              :      * constraints - we don't allow extended statistics on system attributes,
     236              :      * and we require the data type to have a less-than operator, if we're
     237              :      * building multivariate statistics.
     238              :      *
     239              :      * There are many ways to "mask" a simple attribute reference as an
     240              :      * expression, for example "(a+0)" etc. We can't possibly detect all of
     241              :      * them, but we handle at least the simple case with the attribute in
     242              :      * parens. There'll always be a way around this, if the user is determined
     243              :      * (like the "(a+0)" example), but this makes it somewhat consistent with
     244              :      * how indexes treat attributes/expressions.
     245              :      */
     246         1919 :     foreach(cell, stmt->exprs)
     247              :     {
     248         1346 :         StatsElem  *selem = lfirst_node(StatsElem, cell);
     249              : 
     250         1346 :         if (selem->name)     /* column reference */
     251              :         {
     252              :             char       *attname;
     253              :             HeapTuple   atttuple;
     254              :             Form_pg_attribute attForm;
     255              :             TypeCacheEntry *type;
     256              : 
     257          963 :             attname = selem->name;
     258              : 
     259          963 :             atttuple = SearchSysCacheAttName(relid, attname);
     260          963 :             if (!HeapTupleIsValid(atttuple))
     261            4 :                 ereport(ERROR,
     262              :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
     263              :                          errmsg("column \"%s\" does not exist",
     264              :                                 attname)));
     265          959 :             attForm = (Form_pg_attribute) GETSTRUCT(atttuple);
     266              : 
     267              :             /* Disallow use of system attributes in extended stats */
     268          959 :             if (attForm->attnum <= 0)
     269            8 :                 ereport(ERROR,
     270              :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     271              :                          errmsg("statistics creation on system columns is not supported")));
     272              : 
     273              :             /*
     274              :              * Disallow data types without a less-than operator in
     275              :              * multivariate statistics.
     276              :              */
     277          951 :             if (numcols > 1)
     278              :             {
     279          943 :                 type = lookup_type_cache(attForm->atttypid, TYPECACHE_LT_OPR);
     280          943 :                 if (type->lt_opr == InvalidOid)
     281            4 :                     ereport(ERROR,
     282              :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     283              :                              errmsg("cannot create multivariate statistics on column \"%s\"",
     284              :                                     attname),
     285              :                              errdetail("The type %s has no default btree operator class.",
     286              :                                        format_type_be(attForm->atttypid))));
     287              :             }
     288              : 
     289              :             /* Treat virtual generated columns as expressions */
     290          947 :             if (attForm->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
     291              :             {
     292              :                 Node       *expr;
     293              : 
     294           16 :                 expr = (Node *) makeVar(1,
     295           16 :                                         attForm->attnum,
     296              :                                         attForm->atttypid,
     297              :                                         attForm->atttypmod,
     298              :                                         attForm->attcollation,
     299              :                                         0);
     300           16 :                 stxexprs = lappend(stxexprs, expr);
     301              :             }
     302              :             else
     303              :             {
     304          931 :                 attnums[nattnums] = attForm->attnum;
     305          931 :                 nattnums++;
     306              :             }
     307          947 :             ReleaseSysCache(atttuple);
     308              :         }
     309          383 :         else if (IsA(selem->expr, Var)) /* column reference in parens */
     310              :         {
     311           10 :             Var        *var = (Var *) selem->expr;
     312              :             TypeCacheEntry *type;
     313              : 
     314              :             /* Disallow use of system attributes in extended stats */
     315           10 :             if (var->varattno <= 0)
     316            4 :                 ereport(ERROR,
     317              :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     318              :                          errmsg("statistics creation on system columns is not supported")));
     319              : 
     320              :             /*
     321              :              * Disallow data types without a less-than operator in
     322              :              * multivariate statistics.
     323              :              */
     324            6 :             if (numcols > 1)
     325              :             {
     326            2 :                 type = lookup_type_cache(var->vartype, TYPECACHE_LT_OPR);
     327            2 :                 if (type->lt_opr == InvalidOid)
     328            0 :                     ereport(ERROR,
     329              :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     330              :                              errmsg("cannot create multivariate statistics on column \"%s\"",
     331              :                                     get_attname(relid, var->varattno, false)),
     332              :                              errdetail("The type %s has no default btree operator class.",
     333              :                                        format_type_be(var->vartype))));
     334              :             }
     335              : 
     336              :             /* Treat virtual generated columns as expressions */
     337            6 :             if (get_attgenerated(relid, var->varattno) == ATTRIBUTE_GENERATED_VIRTUAL)
     338              :             {
     339            0 :                 stxexprs = lappend(stxexprs, (Node *) var);
     340              :             }
     341              :             else
     342              :             {
     343            6 :                 attnums[nattnums] = var->varattno;
     344            6 :                 nattnums++;
     345              :             }
     346              :         }
     347              :         else                    /* expression */
     348              :         {
     349          373 :             Node       *expr = selem->expr;
     350              :             Oid         atttype;
     351              :             TypeCacheEntry *type;
     352          373 :             Bitmapset  *attnums = NULL;
     353              :             int         k;
     354              : 
     355              :             Assert(expr != NULL);
     356              : 
     357          373 :             pull_varattnos(expr, 1, &attnums);
     358              : 
     359          373 :             k = -1;
     360          838 :             while ((k = bms_next_member(attnums, k)) >= 0)
     361              :             {
     362          469 :                 AttrNumber  attnum = k + FirstLowInvalidHeapAttributeNumber;
     363              : 
     364              :                 /* Disallow expressions referencing system attributes. */
     365          469 :                 if (attnum <= 0)
     366            4 :                     ereport(ERROR,
     367              :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     368              :                              errmsg("statistics creation on system columns is not supported")));
     369              :             }
     370              : 
     371              :             /*
     372              :              * Disallow data types without a less-than operator in
     373              :              * multivariate statistics.
     374              :              */
     375          369 :             if (numcols > 1)
     376              :             {
     377          284 :                 atttype = exprType(expr);
     378          284 :                 type = lookup_type_cache(atttype, TYPECACHE_LT_OPR);
     379          284 :                 if (type->lt_opr == InvalidOid)
     380            0 :                     ereport(ERROR,
     381              :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     382              :                              errmsg("cannot create multivariate statistics on this expression"),
     383              :                              errdetail("The type %s has no default btree operator class.",
     384              :                                        format_type_be(atttype))));
     385              :             }
     386              : 
     387          369 :             stxexprs = lappend(stxexprs, expr);
     388              :         }
     389              :     }
     390              : 
     391              :     /*
     392              :      * Check that at least two columns were specified in the statement, or
     393              :      * that we're building statistics on a single expression (or virtual
     394              :      * generated column).
     395              :      */
     396          573 :     if (numcols < 2 && list_length(stxexprs) != 1)
     397            4 :         ereport(ERROR,
     398              :                 errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
     399              :                 errmsg("cannot create extended statistics on a single non-virtual column"),
     400              :                 errdetail("Univariate statistics are already built for each individual non-virtual table column."));
     401              : 
     402              :     /*
     403              :      * Parse the statistics kinds (not allowed when building univariate
     404              :      * statistics).
     405              :      */
     406          569 :     if (numcols == 1 && stmt->stat_types != NIL)
     407            4 :         ereport(ERROR,
     408              :                 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     409              :                 errmsg("cannot specify statistics kinds when building univariate statistics"));
     410              : 
     411          565 :     build_ndistinct = false;
     412          565 :     build_dependencies = false;
     413          565 :     build_mcv = false;
     414          866 :     foreach(cell, stmt->stat_types)
     415              :     {
     416          305 :         char       *type = strVal(lfirst(cell));
     417              : 
     418          305 :         if (strcmp(type, "ndistinct") == 0)
     419              :         {
     420           81 :             build_ndistinct = true;
     421           81 :             requested_type = true;
     422              :         }
     423          224 :         else if (strcmp(type, "dependencies") == 0)
     424              :         {
     425           88 :             build_dependencies = true;
     426           88 :             requested_type = true;
     427              :         }
     428          136 :         else if (strcmp(type, "mcv") == 0)
     429              :         {
     430          132 :             build_mcv = true;
     431          132 :             requested_type = true;
     432              :         }
     433              :         else
     434            4 :             ereport(ERROR,
     435              :                     (errcode(ERRCODE_SYNTAX_ERROR),
     436              :                      errmsg("unrecognized statistics kind \"%s\"",
     437              :                             type)));
     438              :     }
     439              : 
     440              :     /*
     441              :      * If no statistic type was specified, build them all (but only when the
     442              :      * statistics is defined on more than one column/expression).
     443              :      */
     444          561 :     if ((!requested_type) && (numcols >= 2))
     445              :     {
     446          219 :         build_ndistinct = true;
     447          219 :         build_dependencies = true;
     448          219 :         build_mcv = true;
     449              :     }
     450              : 
     451              :     /*
     452              :      * When there are non-trivial expressions, build the expression stats
     453              :      * automatically. This allows calculating good estimates for stats that
     454              :      * consider per-clause estimates (e.g. functional dependencies).
     455              :      */
     456          561 :     build_expressions = (stxexprs != NIL);
     457              : 
     458              :     /*
     459              :      * Sort the attnums, which makes detecting duplicates somewhat easier, and
     460              :      * it does not hurt (it does not matter for the contents, unlike for
     461              :      * indexes, for example).
     462              :      */
     463          561 :     qsort(attnums, nattnums, sizeof(int16), compare_int16);
     464              : 
     465              :     /*
     466              :      * Check for duplicates in the list of columns. The attnums are sorted so
     467              :      * just check consecutive elements.
     468              :      */
     469         1059 :     for (i = 1; i < nattnums; i++)
     470              :     {
     471          502 :         if (attnums[i] == attnums[i - 1])
     472            4 :             ereport(ERROR,
     473              :                     (errcode(ERRCODE_DUPLICATE_COLUMN),
     474              :                      errmsg("duplicate column name in statistics definition")));
     475              :     }
     476              : 
     477              :     /*
     478              :      * Check for duplicate expressions. We do two loops, counting the
     479              :      * occurrences of each expression. This is O(N^2) but we only allow small
     480              :      * number of expressions and it's not executed often.
     481              :      *
     482              :      * XXX We don't cross-check attributes and expressions, because it does
     483              :      * not seem worth it. In principle we could check that expressions don't
     484              :      * contain trivial attribute references like "(a)", but the reasoning is
     485              :      * similar to why we don't bother with extracting columns from
     486              :      * expressions. It's either expensive or very easy to defeat for
     487              :      * determined user, and there's no risk if we allow such statistics (the
     488              :      * statistics is useless, but harmless).
     489              :      */
     490          930 :     foreach(cell, stxexprs)
     491              :     {
     492          377 :         Node       *expr1 = (Node *) lfirst(cell);
     493          377 :         int         cnt = 0;
     494              : 
     495         1158 :         foreach(cell2, stxexprs)
     496              :         {
     497          781 :             Node       *expr2 = (Node *) lfirst(cell2);
     498              : 
     499          781 :             if (equal(expr1, expr2))
     500          381 :                 cnt += 1;
     501              :         }
     502              : 
     503              :         /* every expression should find at least itself */
     504              :         Assert(cnt >= 1);
     505              : 
     506          377 :         if (cnt > 1)
     507            4 :             ereport(ERROR,
     508              :                     (errcode(ERRCODE_DUPLICATE_COLUMN),
     509              :                      errmsg("duplicate expression in statistics definition")));
     510              :     }
     511              : 
     512              :     /* Form an int2vector representation of the sorted column list */
     513          553 :     stxkeys = buildint2vector(attnums, nattnums);
     514              : 
     515              :     /* construct the char array of enabled statistic types */
     516          553 :     ntypes = 0;
     517          553 :     if (build_ndistinct)
     518          292 :         types[ntypes++] = CharGetDatum(STATS_EXT_NDISTINCT);
     519          553 :     if (build_dependencies)
     520          299 :         types[ntypes++] = CharGetDatum(STATS_EXT_DEPENDENCIES);
     521          553 :     if (build_mcv)
     522          343 :         types[ntypes++] = CharGetDatum(STATS_EXT_MCV);
     523          553 :     if (build_expressions)
     524          230 :         types[ntypes++] = CharGetDatum(STATS_EXT_EXPRESSIONS);
     525              :     Assert(ntypes > 0 && ntypes <= lengthof(types));
     526          553 :     stxkind = construct_array_builtin(types, ntypes, CHAROID);
     527              : 
     528              :     /* convert the expressions (if any) to a text datum */
     529          553 :     if (stxexprs != NIL)
     530              :     {
     531              :         char       *exprsString;
     532              : 
     533          230 :         exprsString = nodeToString(stxexprs);
     534          230 :         exprsDatum = CStringGetTextDatum(exprsString);
     535          230 :         pfree(exprsString);
     536              :     }
     537              :     else
     538          323 :         exprsDatum = (Datum) 0;
     539              : 
     540          553 :     statrel = table_open(StatisticExtRelationId, RowExclusiveLock);
     541              : 
     542              :     /*
     543              :      * Everything seems fine, so let's build the pg_statistic_ext tuple.
     544              :      */
     545          553 :     memset(values, 0, sizeof(values));
     546          553 :     memset(nulls, false, sizeof(nulls));
     547              : 
     548          553 :     statoid = GetNewOidWithIndex(statrel, StatisticExtOidIndexId,
     549              :                                  Anum_pg_statistic_ext_oid);
     550          553 :     values[Anum_pg_statistic_ext_oid - 1] = ObjectIdGetDatum(statoid);
     551          553 :     values[Anum_pg_statistic_ext_stxrelid - 1] = ObjectIdGetDatum(relid);
     552          553 :     values[Anum_pg_statistic_ext_stxname - 1] = NameGetDatum(&stxname);
     553          553 :     values[Anum_pg_statistic_ext_stxnamespace - 1] = ObjectIdGetDatum(namespaceId);
     554          553 :     values[Anum_pg_statistic_ext_stxowner - 1] = ObjectIdGetDatum(stxowner);
     555          553 :     values[Anum_pg_statistic_ext_stxkeys - 1] = PointerGetDatum(stxkeys);
     556          553 :     nulls[Anum_pg_statistic_ext_stxstattarget - 1] = true;
     557          553 :     values[Anum_pg_statistic_ext_stxkind - 1] = PointerGetDatum(stxkind);
     558              : 
     559          553 :     values[Anum_pg_statistic_ext_stxexprs - 1] = exprsDatum;
     560          553 :     if (exprsDatum == (Datum) 0)
     561          323 :         nulls[Anum_pg_statistic_ext_stxexprs - 1] = true;
     562              : 
     563              :     /* insert it into pg_statistic_ext */
     564          553 :     htup = heap_form_tuple(statrel->rd_att, values, nulls);
     565          553 :     CatalogTupleInsert(statrel, htup);
     566          553 :     heap_freetuple(htup);
     567              : 
     568          553 :     relation_close(statrel, RowExclusiveLock);
     569              : 
     570              :     /*
     571              :      * We used to create the pg_statistic_ext_data tuple too, but it's not
     572              :      * clear what value should the stxdinherit flag have (it depends on
     573              :      * whether the rel is partitioned, contains data, etc.)
     574              :      */
     575              : 
     576          553 :     InvokeObjectPostCreateHook(StatisticExtRelationId, statoid, 0);
     577              : 
     578              :     /*
     579              :      * Invalidate relcache so that others see the new statistics object.
     580              :      */
     581          553 :     CacheInvalidateRelcache(rel);
     582              : 
     583          553 :     relation_close(rel, NoLock);
     584              : 
     585              :     /*
     586              :      * Add an AUTO dependency on each column used in the stats, so that the
     587              :      * stats object goes away if any or all of them get dropped.
     588              :      */
     589          553 :     ObjectAddressSet(myself, StatisticExtRelationId, statoid);
     590              : 
     591              :     /* add dependencies for plain column references */
     592         1458 :     for (i = 0; i < nattnums; i++)
     593              :     {
     594          905 :         ObjectAddressSubSet(parentobject, RelationRelationId, relid, attnums[i]);
     595          905 :         recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
     596              :     }
     597              : 
     598              :     /*
     599              :      * If there are no dependencies on a column, give the statistics object an
     600              :      * auto dependency on the whole table.  In most cases, this will be
     601              :      * redundant, but it might not be if the statistics expressions contain no
     602              :      * Vars (which might seem strange but possible). This is consistent with
     603              :      * what we do for indexes in index_create.
     604              :      *
     605              :      * XXX We intentionally don't consider the expressions before adding this
     606              :      * dependency, because recordDependencyOnSingleRelExpr may not create any
     607              :      * dependencies for whole-row Vars.
     608              :      */
     609          553 :     if (!nattnums)
     610              :     {
     611          146 :         ObjectAddressSet(parentobject, RelationRelationId, relid);
     612          146 :         recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
     613              :     }
     614              : 
     615              :     /*
     616              :      * Store dependencies on anything mentioned in statistics expressions,
     617              :      * just like we do for index expressions.
     618              :      */
     619          553 :     if (stxexprs)
     620          230 :         recordDependencyOnSingleRelExpr(&myself,
     621              :                                         (Node *) stxexprs,
     622              :                                         relid,
     623              :                                         DEPENDENCY_NORMAL,
     624              :                                         DEPENDENCY_AUTO, false);
     625              : 
     626              :     /*
     627              :      * Also add dependencies on namespace and owner.  These are required
     628              :      * because the stats object might have a different namespace and/or owner
     629              :      * than the underlying table(s).
     630              :      */
     631          553 :     ObjectAddressSet(parentobject, NamespaceRelationId, namespaceId);
     632          553 :     recordDependencyOn(&myself, &parentobject, DEPENDENCY_NORMAL);
     633              : 
     634          553 :     recordDependencyOnOwner(StatisticExtRelationId, statoid, stxowner);
     635              : 
     636              :     /*
     637              :      * XXX probably there should be a recordDependencyOnCurrentExtension call
     638              :      * here too, but we'd have to add support for ALTER EXTENSION ADD/DROP
     639              :      * STATISTICS, which is more work than it seems worth.
     640              :      */
     641              : 
     642              :     /* Add any requested comment */
     643          553 :     if (stmt->stxcomment != NULL)
     644           24 :         CreateComments(statoid, StatisticExtRelationId, 0,
     645           24 :                        stmt->stxcomment);
     646              : 
     647              :     /* Return stats object's address */
     648          553 :     return myself;
     649              : }
     650              : 
     651              : /*
     652              :  *      ALTER STATISTICS
     653              :  */
     654              : ObjectAddress
     655           17 : AlterStatistics(AlterStatsStmt *stmt)
     656              : {
     657              :     Relation    rel;
     658              :     Oid         stxoid;
     659              :     HeapTuple   oldtup;
     660              :     HeapTuple   newtup;
     661              :     Datum       repl_val[Natts_pg_statistic_ext];
     662              :     bool        repl_null[Natts_pg_statistic_ext];
     663              :     bool        repl_repl[Natts_pg_statistic_ext];
     664              :     ObjectAddress address;
     665           17 :     int         newtarget = 0;
     666              :     bool        newtarget_default;
     667              : 
     668              :     /* -1 was used in previous versions for the default setting */
     669           17 :     if (stmt->stxstattarget && intVal(stmt->stxstattarget) != -1)
     670              :     {
     671           13 :         newtarget = intVal(stmt->stxstattarget);
     672           13 :         newtarget_default = false;
     673              :     }
     674              :     else
     675            4 :         newtarget_default = true;
     676              : 
     677           17 :     if (!newtarget_default)
     678              :     {
     679              :         /* Limit statistics target to a sane range */
     680           13 :         if (newtarget < 0)
     681              :         {
     682            0 :             ereport(ERROR,
     683              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     684              :                      errmsg("statistics target %d is too low",
     685              :                             newtarget)));
     686              :         }
     687           13 :         else if (newtarget > MAX_STATISTICS_TARGET)
     688              :         {
     689            0 :             newtarget = MAX_STATISTICS_TARGET;
     690            0 :             ereport(WARNING,
     691              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     692              :                      errmsg("lowering statistics target to %d",
     693              :                             newtarget)));
     694              :         }
     695              :     }
     696              : 
     697              :     /* lookup OID of the statistics object */
     698           17 :     stxoid = get_statistics_object_oid(stmt->defnames, stmt->missing_ok);
     699              : 
     700              :     /*
     701              :      * If we got here and the OID is not valid, it means the statistics object
     702              :      * does not exist, but the command specified IF EXISTS. So report this as
     703              :      * a simple NOTICE and we're done.
     704              :      */
     705           13 :     if (!OidIsValid(stxoid))
     706              :     {
     707              :         char       *schemaname;
     708              :         char       *statname;
     709              : 
     710              :         Assert(stmt->missing_ok);
     711              : 
     712            4 :         DeconstructQualifiedName(stmt->defnames, &schemaname, &statname);
     713              : 
     714            4 :         if (schemaname)
     715            0 :             ereport(NOTICE,
     716              :                     (errmsg("statistics object \"%s.%s\" does not exist, skipping",
     717              :                             schemaname, statname)));
     718              :         else
     719            4 :             ereport(NOTICE,
     720              :                     (errmsg("statistics object \"%s\" does not exist, skipping",
     721              :                             statname)));
     722              : 
     723            4 :         return InvalidObjectAddress;
     724              :     }
     725              : 
     726              :     /* Search pg_statistic_ext */
     727            9 :     rel = table_open(StatisticExtRelationId, RowExclusiveLock);
     728              : 
     729            9 :     oldtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stxoid));
     730            9 :     if (!HeapTupleIsValid(oldtup))
     731            0 :         elog(ERROR, "cache lookup failed for extended statistics object %u", stxoid);
     732              : 
     733              :     /* Must be owner of the existing statistics object */
     734            9 :     if (!object_ownercheck(StatisticExtRelationId, stxoid, GetUserId()))
     735            0 :         aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_STATISTIC_EXT,
     736            0 :                        NameListToString(stmt->defnames));
     737              : 
     738              :     /* Build new tuple. */
     739            9 :     memset(repl_val, 0, sizeof(repl_val));
     740            9 :     memset(repl_null, false, sizeof(repl_null));
     741            9 :     memset(repl_repl, false, sizeof(repl_repl));
     742              : 
     743              :     /* replace the stxstattarget column */
     744            9 :     repl_repl[Anum_pg_statistic_ext_stxstattarget - 1] = true;
     745            9 :     if (!newtarget_default)
     746            5 :         repl_val[Anum_pg_statistic_ext_stxstattarget - 1] = Int16GetDatum(newtarget);
     747              :     else
     748            4 :         repl_null[Anum_pg_statistic_ext_stxstattarget - 1] = true;
     749              : 
     750            9 :     newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
     751              :                                repl_val, repl_null, repl_repl);
     752              : 
     753              :     /* Update system catalog. */
     754            9 :     CatalogTupleUpdate(rel, &newtup->t_self, newtup);
     755              : 
     756            9 :     InvokeObjectPostAlterHook(StatisticExtRelationId, stxoid, 0);
     757              : 
     758            9 :     ObjectAddressSet(address, StatisticExtRelationId, stxoid);
     759              : 
     760              :     /*
     761              :      * NOTE: because we only support altering the statistics target, not the
     762              :      * other fields, there is no need to update dependencies.
     763              :      */
     764              : 
     765            9 :     heap_freetuple(newtup);
     766            9 :     ReleaseSysCache(oldtup);
     767              : 
     768            9 :     table_close(rel, RowExclusiveLock);
     769              : 
     770            9 :     return address;
     771              : }
     772              : 
     773              : /*
     774              :  * Delete entry in pg_statistic_ext_data catalog. We don't know if the row
     775              :  * exists, so don't error out.
     776              :  */
     777              : void
     778         1408 : RemoveStatisticsDataById(Oid statsOid, bool inh)
     779              : {
     780              :     Relation    relation;
     781              :     HeapTuple   tup;
     782              : 
     783         1408 :     relation = table_open(StatisticExtDataRelationId, RowExclusiveLock);
     784              : 
     785         1408 :     tup = SearchSysCache2(STATEXTDATASTXOID, ObjectIdGetDatum(statsOid),
     786              :                           BoolGetDatum(inh));
     787              : 
     788              :     /* We don't know if the data row for inh value exists. */
     789         1408 :     if (HeapTupleIsValid(tup))
     790              :     {
     791          363 :         CatalogTupleDelete(relation, &tup->t_self);
     792              : 
     793          363 :         ReleaseSysCache(tup);
     794              :     }
     795              : 
     796         1408 :     table_close(relation, RowExclusiveLock);
     797         1408 : }
     798              : 
     799              : /*
     800              :  * Guts of statistics object deletion.
     801              :  */
     802              : void
     803          519 : RemoveStatisticsById(Oid statsOid)
     804              : {
     805              :     Relation    relation;
     806              :     Relation    rel;
     807              :     HeapTuple   tup;
     808              :     Form_pg_statistic_ext statext;
     809              :     Oid         relid;
     810              : 
     811              :     /*
     812              :      * Delete the pg_statistic_ext tuple.  Also send out a cache inval on the
     813              :      * associated table, so that dependent plans will be rebuilt.
     814              :      */
     815          519 :     relation = table_open(StatisticExtRelationId, RowExclusiveLock);
     816              : 
     817          519 :     tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid));
     818              : 
     819          519 :     if (!HeapTupleIsValid(tup)) /* should not happen */
     820            0 :         elog(ERROR, "cache lookup failed for statistics object %u", statsOid);
     821              : 
     822          519 :     statext = (Form_pg_statistic_ext) GETSTRUCT(tup);
     823          519 :     relid = statext->stxrelid;
     824              : 
     825              :     /*
     826              :      * Delete the pg_statistic_ext_data tuples holding the actual statistical
     827              :      * data. There might be data with/without inheritance, so attempt deleting
     828              :      * both. We lock the user table first, to prevent other processes (e.g.
     829              :      * DROP STATISTICS) from removing the row concurrently.
     830              :      */
     831          519 :     rel = table_open(relid, ShareUpdateExclusiveLock);
     832              : 
     833          519 :     RemoveStatisticsDataById(statsOid, true);
     834          519 :     RemoveStatisticsDataById(statsOid, false);
     835              : 
     836          519 :     CacheInvalidateRelcacheByRelid(relid);
     837              : 
     838          519 :     CatalogTupleDelete(relation, &tup->t_self);
     839              : 
     840          519 :     ReleaseSysCache(tup);
     841              : 
     842              :     /* Keep lock until the end of the transaction. */
     843          519 :     table_close(rel, NoLock);
     844              : 
     845          519 :     table_close(relation, RowExclusiveLock);
     846          519 : }
     847              : 
     848              : /*
     849              :  * Select a nonconflicting name for a new statistics object.
     850              :  *
     851              :  * name1, name2, and label are used the same way as for makeObjectName(),
     852              :  * except that the label can't be NULL; digits will be appended to the label
     853              :  * if needed to create a name that is unique within the specified namespace.
     854              :  *
     855              :  * Returns a palloc'd string.
     856              :  *
     857              :  * Note: it is theoretically possible to get a collision anyway, if someone
     858              :  * else chooses the same name concurrently.  This is fairly unlikely to be
     859              :  * a problem in practice, especially if one is holding a share update
     860              :  * exclusive lock on the relation identified by name1.  However, if choosing
     861              :  * multiple names within a single command, you'd better create the new object
     862              :  * and do CommandCounterIncrement before choosing the next one!
     863              :  */
     864              : static char *
     865           76 : ChooseExtendedStatisticName(const char *name1, const char *name2,
     866              :                             const char *label, Oid namespaceid)
     867              : {
     868           76 :     int         pass = 0;
     869           76 :     char       *stxname = NULL;
     870              :     char        modlabel[NAMEDATALEN];
     871              : 
     872              :     /* try the unmodified label first */
     873           76 :     strlcpy(modlabel, label, sizeof(modlabel));
     874              : 
     875              :     for (;;)
     876           24 :     {
     877              :         Oid         existingstats;
     878              : 
     879          100 :         stxname = makeObjectName(name1, name2, modlabel);
     880              : 
     881          100 :         existingstats = GetSysCacheOid2(STATEXTNAMENSP, Anum_pg_statistic_ext_oid,
     882              :                                         PointerGetDatum(stxname),
     883              :                                         ObjectIdGetDatum(namespaceid));
     884          100 :         if (!OidIsValid(existingstats))
     885           76 :             break;
     886              : 
     887              :         /* found a conflict, so try a new name component */
     888           24 :         pfree(stxname);
     889           24 :         snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
     890              :     }
     891              : 
     892           76 :     return stxname;
     893              : }
     894              : 
     895              : /*
     896              :  * Generate "name2" for a new statistics object given the list of column
     897              :  * names for it.  This will be passed to ChooseExtendedStatisticName along
     898              :  * with the parent table name and a suitable label.
     899              :  *
     900              :  * We know that less than NAMEDATALEN characters will actually be used,
     901              :  * so we can truncate the result once we've generated that many.
     902              :  *
     903              :  * XXX see also ChooseForeignKeyConstraintNameAddition and
     904              :  * ChooseIndexNameAddition.
     905              :  */
     906              : static char *
     907           76 : ChooseExtendedStatisticNameAddition(List *exprs)
     908              : {
     909              :     char        buf[NAMEDATALEN * 2];
     910           76 :     int         buflen = 0;
     911              :     ListCell   *lc;
     912              : 
     913           76 :     buf[0] = '\0';
     914          244 :     foreach(lc, exprs)
     915              :     {
     916          168 :         StatsElem  *selem = (StatsElem *) lfirst(lc);
     917              :         const char *name;
     918              : 
     919              :         /* It should be one of these, but just skip if it happens not to be */
     920          168 :         if (!IsA(selem, StatsElem))
     921            0 :             continue;
     922              : 
     923          168 :         name = selem->name;
     924              : 
     925          168 :         if (buflen > 0)
     926           92 :             buf[buflen++] = '_';    /* insert _ between names */
     927              : 
     928              :         /*
     929              :          * We use fixed 'expr' for expressions, which have empty column names.
     930              :          * For indexes this is handled in ChooseIndexColumnNames, but we have
     931              :          * no such function for stats and it does not seem worth adding. If a
     932              :          * better name is needed, the user can specify it explicitly.
     933              :          */
     934          168 :         if (!name)
     935           40 :             name = "expr";
     936              : 
     937              :         /*
     938              :          * At this point we have buflen <= NAMEDATALEN.  name should be less
     939              :          * than NAMEDATALEN already, but use strlcpy for paranoia.
     940              :          */
     941          168 :         strlcpy(buf + buflen, name, NAMEDATALEN);
     942          168 :         buflen += strlen(buf + buflen);
     943          168 :         if (buflen >= NAMEDATALEN)
     944            0 :             break;
     945              :     }
     946           76 :     return pstrdup(buf);
     947              : }
     948              : 
     949              : /*
     950              :  * StatisticsGetRelation: given a statistics object's OID, get the OID of
     951              :  * the relation it is defined on.  Uses the system cache.
     952              :  */
     953              : Oid
     954           53 : StatisticsGetRelation(Oid statId, bool missing_ok)
     955              : {
     956              :     HeapTuple   tuple;
     957              :     Form_pg_statistic_ext stx;
     958              :     Oid         result;
     959              : 
     960           53 :     tuple = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statId));
     961           53 :     if (!HeapTupleIsValid(tuple))
     962              :     {
     963            0 :         if (missing_ok)
     964            0 :             return InvalidOid;
     965            0 :         elog(ERROR, "cache lookup failed for statistics object %u", statId);
     966              :     }
     967           53 :     stx = (Form_pg_statistic_ext) GETSTRUCT(tuple);
     968              :     Assert(stx->oid == statId);
     969              : 
     970           53 :     result = stx->stxrelid;
     971           53 :     ReleaseSysCache(tuple);
     972           53 :     return result;
     973              : }
        

Generated by: LCOV version 2.0-1