LCOV - code coverage report
Current view: top level - src/backend/commands - statscmds.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 93.5 % 398 372
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 62.5 % 320 200

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

Generated by: LCOV version 2.3.2-1