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

Generated by: LCOV version 1.14