LCOV - code coverage report
Current view: top level - src/backend/commands - statscmds.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 92.5 % 292 270
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 72.5 % 240 174

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

Generated by: LCOV version 2.0-1