LCOV - differential code coverage report
Current view: top level - src/backend/commands - statscmds.c (source / functions) Coverage Total Hit UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 90.6 % 276 250 17 9 11 130 5 104 4 145 2 1
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 8 8 6 1 1 6
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a