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

Generated by: LCOV version 1.14