LCOV - differential code coverage report
Current view: top level - src/backend/statistics - stat_utils.c (source / functions) Coverage Total Hit LBC UBC GBC CBC
Current: a2387c32f2f8a1643c7d71b951587e6bcb2d4744 vs 371a302eecdc82274b0ae2967d18fd726a0aa6a1 Lines: 85.7 % 98 84 14 84
Current Date: 2025-10-26 12:31:50 -0700 Functions: 100.0 % 7 7 7
Baseline: lcov-20251027-010456-baseline Branches: 68.9 % 106 73 3 30 3 70
Baseline Date: 2025-10-26 11:01:32 +1300 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 72.4 % 29 21 8 21
(30,360] days: 88.9 % 9 8 1 8
(360..) days: 91.7 % 60 55 5 55
Function coverage date bins:
(7,30] days: 100.0 % 1 1 1
(30,360] days: 100.0 % 2 2 2
(360..) days: 100.0 % 4 4 4
Branch coverage date bins:
(7,30] days: 61.1 % 36 22 3 11 3 19
(30,360] days: 40.0 % 10 4 6 4
(360..) days: 78.3 % 60 47 13 47

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  * stat_utils.c
                                  3                 :                :  *
                                  4                 :                :  *    PostgreSQL statistics manipulation utilities.
                                  5                 :                :  *
                                  6                 :                :  * Code supporting the direct manipulation of statistics.
                                  7                 :                :  *
                                  8                 :                :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
                                  9                 :                :  * Portions Copyright (c) 1994, Regents of the University of California
                                 10                 :                :  *
                                 11                 :                :  * IDENTIFICATION
                                 12                 :                :  *       src/backend/statistics/stat_utils.c
                                 13                 :                :  *
                                 14                 :                :  *-------------------------------------------------------------------------
                                 15                 :                :  */
                                 16                 :                : 
                                 17                 :                : #include "postgres.h"
                                 18                 :                : 
                                 19                 :                : #include "access/htup_details.h"
                                 20                 :                : #include "access/relation.h"
                                 21                 :                : #include "catalog/index.h"
                                 22                 :                : #include "catalog/namespace.h"
                                 23                 :                : #include "catalog/pg_class.h"
                                 24                 :                : #include "catalog/pg_database.h"
                                 25                 :                : #include "funcapi.h"
                                 26                 :                : #include "miscadmin.h"
                                 27                 :                : #include "statistics/stat_utils.h"
                                 28                 :                : #include "storage/lmgr.h"
                                 29                 :                : #include "utils/acl.h"
                                 30                 :                : #include "utils/array.h"
                                 31                 :                : #include "utils/builtins.h"
                                 32                 :                : #include "utils/lsyscache.h"
                                 33                 :                : #include "utils/rel.h"
                                 34                 :                : #include "utils/syscache.h"
                                 35                 :                : 
                                 36                 :                : /*
                                 37                 :                :  * Ensure that a given argument is not null.
                                 38                 :                :  */
                                 39                 :                : void
  381 jdavis@postgresql.or       40                 :CBC        4563 : stats_check_required_arg(FunctionCallInfo fcinfo,
                                 41                 :                :                          struct StatsArgInfo *arginfo,
                                 42                 :                :                          int argnum)
                                 43                 :                : {
                                 44         [ +  + ]:           4563 :     if (PG_ARGISNULL(argnum))
                                 45         [ +  - ]:             24 :         ereport(ERROR,
                                 46                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 47                 :                :                  errmsg("argument \"%s\" must not be null",
                                 48                 :                :                         arginfo[argnum].argname)));
                                 49                 :           4539 : }
                                 50                 :                : 
                                 51                 :                : /*
                                 52                 :                :  * Check that argument is either NULL or a one dimensional array with no
                                 53                 :                :  * NULLs.
                                 54                 :                :  *
                                 55                 :                :  * If a problem is found, emit a WARNING, and return false. Otherwise return
                                 56                 :                :  * true.
                                 57                 :                :  */
                                 58                 :                : bool
  370                            59                 :           2295 : stats_check_arg_array(FunctionCallInfo fcinfo,
                                 60                 :                :                       struct StatsArgInfo *arginfo,
                                 61                 :                :                       int argnum)
                                 62                 :                : {
                                 63                 :                :     ArrayType  *arr;
                                 64                 :                : 
                                 65         [ +  + ]:           2295 :     if (PG_ARGISNULL(argnum))
                                 66                 :           1874 :         return true;
                                 67                 :                : 
                                 68                 :            421 :     arr = DatumGetArrayTypeP(PG_GETARG_DATUM(argnum));
                                 69                 :                : 
                                 70         [ -  + ]:            421 :     if (ARR_NDIM(arr) != 1)
                                 71                 :                :     {
  244 jdavis@postgresql.or       72         [ #  # ]:UBC           0 :         ereport(WARNING,
                                 73                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 74                 :                :                  errmsg("argument \"%s\" must not be a multidimensional array",
                                 75                 :                :                         arginfo[argnum].argname)));
  370                            76                 :              0 :         return false;
                                 77                 :                :     }
                                 78                 :                : 
  370 jdavis@postgresql.or       79         [ +  + ]:CBC         421 :     if (array_contains_nulls(arr))
                                 80                 :                :     {
  244                            81         [ +  - ]:              3 :         ereport(WARNING,
                                 82                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 83                 :                :                  errmsg("argument \"%s\" array must not contain null values",
                                 84                 :                :                         arginfo[argnum].argname)));
  370                            85                 :              3 :         return false;
                                 86                 :                :     }
                                 87                 :                : 
                                 88                 :            418 :     return true;
                                 89                 :                : }
                                 90                 :                : 
                                 91                 :                : /*
                                 92                 :                :  * Enforce parameter pairs that must be specified together (or not at all) for
                                 93                 :                :  * a particular stakind, such as most_common_vals and most_common_freqs for
                                 94                 :                :  * STATISTIC_KIND_MCV.
                                 95                 :                :  *
                                 96                 :                :  * If a problem is found, emit a WARNING, and return false. Otherwise return
                                 97                 :                :  * true.
                                 98                 :                :  */
                                 99                 :                : bool
                                100                 :           2295 : stats_check_arg_pair(FunctionCallInfo fcinfo,
                                101                 :                :                      struct StatsArgInfo *arginfo,
                                102                 :                :                      int argnum1, int argnum2)
                                103                 :                : {
                                104   [ +  +  +  + ]:           2295 :     if (PG_ARGISNULL(argnum1) && PG_ARGISNULL(argnum2))
                                105                 :           1865 :         return true;
                                106                 :                : 
                                107   [ +  +  +  + ]:            430 :     if (PG_ARGISNULL(argnum1) || PG_ARGISNULL(argnum2))
                                108                 :                :     {
                                109         [ +  + ]:             21 :         int         nullarg = PG_ARGISNULL(argnum1) ? argnum1 : argnum2;
                                110         [ +  + ]:             21 :         int         otherarg = PG_ARGISNULL(argnum1) ? argnum2 : argnum1;
                                111                 :                : 
  244                           112         [ +  - ]:             21 :         ereport(WARNING,
                                113                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                114                 :                :                  errmsg("argument \"%s\" must be specified when argument \"%s\" is specified",
                                115                 :                :                         arginfo[nullarg].argname,
                                116                 :                :                         arginfo[otherarg].argname)));
                                117                 :                : 
  370                           118                 :             21 :         return false;
                                119                 :                :     }
                                120                 :                : 
                                121                 :            409 :     return true;
                                122                 :                : }
                                123                 :                : 
                                124                 :                : /*
                                125                 :                :  * A role has privileges to set statistics on the relation if any of the
                                126                 :                :  * following are true:
                                127                 :                :  *   - the role owns the current database and the relation is not shared
                                128                 :                :  *   - the role has the MAINTAIN privilege on the relation
                                129                 :                :  */
                                130                 :                : void
   12 nathan@postgresql.or      131                 :           1876 : RangeVarCallbackForStats(const RangeVar *relation,
                                132                 :                :                          Oid relId, Oid oldRelId, void *arg)
                                133                 :                : {
                                134                 :           1876 :     Oid        *locked_oid = (Oid *) arg;
                                135                 :           1876 :     Oid         table_oid = relId;
                                136                 :                :     HeapTuple   tuple;
                                137                 :                :     Form_pg_class form;
                                138                 :                :     char        relkind;
                                139                 :                : 
                                140                 :                :     /*
                                141                 :                :      * If we previously locked some other index's heap, and the name we're
                                142                 :                :      * looking up no longer refers to that relation, release the now-useless
                                143                 :                :      * lock.
                                144                 :                :      */
                                145   [ +  +  -  + ]:           1876 :     if (relId != oldRelId && OidIsValid(*locked_oid))
                                146                 :                :     {
   12 nathan@postgresql.or      147                 :UBC           0 :         UnlockRelationOid(*locked_oid, ShareUpdateExclusiveLock);
                                148                 :              0 :         *locked_oid = InvalidOid;
                                149                 :                :     }
                                150                 :                : 
                                151                 :                :     /* If the relation does not exist, there's nothing more to do. */
   12 nathan@postgresql.or      152         [ +  + ]:CBC        1876 :     if (!OidIsValid(relId))
                                153                 :              6 :         return;
                                154                 :                : 
                                155                 :                :     /* If the relation does exist, check whether it's an index. */
                                156                 :           1870 :     relkind = get_rel_relkind(relId);
                                157   [ +  +  +  + ]:           1870 :     if (relkind == RELKIND_INDEX ||
                                158                 :                :         relkind == RELKIND_PARTITIONED_INDEX)
                                159                 :            295 :         table_oid = IndexGetRelation(relId, false);
                                160                 :                : 
                                161                 :                :     /*
                                162                 :                :      * If retrying yields the same OID, there are a couple of extremely
                                163                 :                :      * unlikely scenarios we need to handle.
                                164                 :                :      */
                                165         [ +  + ]:           1870 :     if (relId == oldRelId)
                                166                 :                :     {
                                167                 :                :         /*
                                168                 :                :          * If a previous lookup found an index, but the current lookup did
                                169                 :                :          * not, the index was dropped and the OID was reused for something
                                170                 :                :          * else between lookups.  In theory, we could simply drop our lock on
                                171                 :                :          * the index's parent table and proceed, but in the interest of
                                172                 :                :          * avoiding complexity, we just error.
                                173                 :                :          */
                                174   [ -  +  -  - ]:              1 :         if (table_oid == relId && OidIsValid(*locked_oid))
   12 nathan@postgresql.or      175         [ #  # ]:UBC           0 :             ereport(ERROR,
                                176                 :                :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
                                177                 :                :                      errmsg("index \"%s\" was concurrently dropped",
                                178                 :                :                             relation->relname)));
                                179                 :                : 
                                180                 :                :         /*
                                181                 :                :          * If the current lookup found an index but a previous lookup either
                                182                 :                :          * did not find an index or found one with a different parent
                                183                 :                :          * relation, the relation was dropped and the OID was reused for an
                                184                 :                :          * index between lookups.  RangeVarGetRelidExtended() will have
                                185                 :                :          * already locked the index at this point, so we can't just lock the
                                186                 :                :          * newly discovered parent table OID without risking deadlock.  As
                                187                 :                :          * above, we just error in this case.
                                188                 :                :          */
   12 nathan@postgresql.or      189   [ +  -  -  + ]:CBC           1 :         if (table_oid != relId && table_oid != *locked_oid)
   12 nathan@postgresql.or      190         [ #  # ]:UBC           0 :             ereport(ERROR,
                                191                 :                :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
                                192                 :                :                      errmsg("index \"%s\" was concurrently created",
                                193                 :                :                             relation->relname)));
                                194                 :                :     }
                                195                 :                : 
   12 nathan@postgresql.or      196                 :CBC        1870 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
                                197         [ -  + ]:           1870 :     if (!HeapTupleIsValid(tuple))
   12 nathan@postgresql.or      198         [ #  # ]:UBC           0 :         elog(ERROR, "cache lookup failed for OID %u", table_oid);
   12 nathan@postgresql.or      199                 :CBC        1870 :     form = (Form_pg_class) GETSTRUCT(tuple);
                                200                 :                : 
                                201                 :                :     /* the relkinds that can be used with ANALYZE */
                                202         [ +  + ]:           1870 :     switch (form->relkind)
                                203                 :                :     {
  381 jdavis@postgresql.or      204                 :           1861 :         case RELKIND_RELATION:
                                205                 :                :         case RELKIND_MATVIEW:
                                206                 :                :         case RELKIND_FOREIGN_TABLE:
                                207                 :                :         case RELKIND_PARTITIONED_TABLE:
                                208                 :           1861 :             break;
                                209                 :              9 :         default:
                                210         [ +  - ]:              9 :             ereport(ERROR,
                                211                 :                :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                212                 :                :                      errmsg("cannot modify statistics for relation \"%s\"",
                                213                 :                :                             NameStr(form->relname)),
                                214                 :                :                      errdetail_relkind_not_supported(form->relkind)));
                                215                 :                :     }
                                216                 :                : 
   12 nathan@postgresql.or      217         [ -  + ]:           1861 :     if (form->relisshared)
  381 jdavis@postgresql.or      218         [ #  # ]:UBC           0 :         ereport(ERROR,
                                219                 :                :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                220                 :                :                  errmsg("cannot modify statistics for shared relation")));
                                221                 :                : 
                                222                 :                :     /* Check permissions */
  381 jdavis@postgresql.or      223         [ -  + ]:CBC        1861 :     if (!object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()))
                                224                 :                :     {
   12 nathan@postgresql.or      225                 :UBC           0 :         AclResult   aclresult = pg_class_aclcheck(table_oid,
                                226                 :                :                                                   GetUserId(),
                                227                 :                :                                                   ACL_MAINTAIN);
                                228                 :                : 
  381 jdavis@postgresql.or      229         [ #  # ]:              0 :         if (aclresult != ACLCHECK_OK)
                                230                 :              0 :             aclcheck_error(aclresult,
   12 nathan@postgresql.or      231                 :              0 :                            get_relkind_objtype(form->relkind),
                                232                 :              0 :                            NameStr(form->relname));
                                233                 :                :     }
                                234                 :                : 
   12 nathan@postgresql.or      235                 :CBC        1861 :     ReleaseSysCache(tuple);
                                236                 :                : 
                                237                 :                :     /* Lock heap before index to avoid deadlock. */
                                238   [ +  +  +  + ]:           1861 :     if (relId != oldRelId && table_oid != relId)
                                239                 :                :     {
                                240                 :            294 :         LockRelationOid(table_oid, ShareUpdateExclusiveLock);
                                241                 :            294 :         *locked_oid = table_oid;
                                242                 :                :     }
                                243                 :                : }
                                244                 :                : 
                                245                 :                : 
                                246                 :                : /*
                                247                 :                :  * Find the argument number for the given argument name, returning -1 if not
                                248                 :                :  * found.
                                249                 :                :  */
                                250                 :                : static int
  244 jdavis@postgresql.or      251                 :          13646 : get_arg_by_name(const char *argname, struct StatsArgInfo *arginfo)
                                252                 :                : {
                                253                 :                :     int         argnum;
                                254                 :                : 
  368                           255         [ +  + ]:          66966 :     for (argnum = 0; arginfo[argnum].argname != NULL; argnum++)
                                256         [ +  + ]:          66960 :         if (pg_strcasecmp(argname, arginfo[argnum].argname) == 0)
                                257                 :          13640 :             return argnum;
                                258                 :                : 
  244                           259         [ +  - ]:              6 :     ereport(WARNING,
                                260                 :                :             (errmsg("unrecognized argument name: \"%s\"", argname)));
                                261                 :                : 
  368                           262                 :              6 :     return -1;
                                263                 :                : }
                                264                 :                : 
                                265                 :                : /*
                                266                 :                :  * Ensure that a given argument matched the expected type.
                                267                 :                :  */
                                268                 :                : static bool
  244                           269                 :          13640 : stats_check_arg_type(const char *argname, Oid argtype, Oid expectedtype)
                                270                 :                : {
  368                           271         [ +  + ]:          13640 :     if (argtype != expectedtype)
                                272                 :                :     {
  244                           273         [ +  - ]:             12 :         ereport(WARNING,
                                274                 :                :                 (errmsg("argument \"%s\" has type %s, expected type %s",
                                275                 :                :                         argname, format_type_be(argtype),
                                276                 :                :                         format_type_be(expectedtype))));
  368                           277                 :             12 :         return false;
                                278                 :                :     }
                                279                 :                : 
                                280                 :          13628 :     return true;
                                281                 :                : }
                                282                 :                : 
                                283                 :                : /*
                                284                 :                :  * Translate variadic argument pairs from 'pairs_fcinfo' into a
                                285                 :                :  * 'positional_fcinfo' appropriate for calling relation_statistics_update() or
                                286                 :                :  * attribute_statistics_update() with positional arguments.
                                287                 :                :  *
                                288                 :                :  * Caller should have already initialized positional_fcinfo with a size
                                289                 :                :  * appropriate for calling the intended positional function, and arginfo
                                290                 :                :  * should also match the intended positional function.
                                291                 :                :  */
                                292                 :                : bool
                                293                 :           1890 : stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
                                294                 :                :                                  FunctionCallInfo positional_fcinfo,
                                295                 :                :                                  struct StatsArgInfo *arginfo)
                                296                 :                : {
                                297                 :                :     Datum      *args;
                                298                 :                :     bool       *argnulls;
                                299                 :                :     Oid        *types;
                                300                 :                :     int         nargs;
                                301                 :           1890 :     bool        result = true;
                                302                 :                : 
                                303                 :                :     /* clear positional args */
                                304         [ +  + ]:          22806 :     for (int i = 0; arginfo[i].argname != NULL; i++)
                                305                 :                :     {
                                306                 :          20916 :         positional_fcinfo->args[i].value = (Datum) 0;
                                307                 :          20916 :         positional_fcinfo->args[i].isnull = true;
                                308                 :                :     }
                                309                 :                : 
                                310                 :           1890 :     nargs = extract_variadic_args(pairs_fcinfo, 0, true,
                                311                 :                :                                   &args, &types, &argnulls);
                                312                 :                : 
                                313         [ +  + ]:           1890 :     if (nargs % 2 != 0)
                                314         [ +  - ]:              3 :         ereport(ERROR,
                                315                 :                :                 errmsg("variadic arguments must be name/value pairs"),
                                316                 :                :                 errhint("Provide an even number of variadic arguments that can be divided into pairs."));
                                317                 :                : 
                                318                 :                :     /*
                                319                 :                :      * For each argument name/value pair, find corresponding positional
                                320                 :                :      * argument for the argument name, and assign the argument value to
                                321                 :                :      * positional_fcinfo.
                                322                 :                :      */
                                323         [ +  + ]:          17405 :     for (int i = 0; i < nargs; i += 2)
                                324                 :                :     {
                                325                 :                :         int         argnum;
                                326                 :                :         char       *argname;
                                327                 :                : 
                                328         [ +  + ]:          15521 :         if (argnulls[i])
                                329         [ +  - ]:              3 :             ereport(ERROR,
                                330                 :                :                     (errmsg("name at variadic position %d is null", i + 1)));
                                331                 :                : 
                                332         [ -  + ]:          15518 :         if (types[i] != TEXTOID)
  368 jdavis@postgresql.or      333         [ #  # ]:UBC           0 :             ereport(ERROR,
                                334                 :                :                     (errmsg("name at variadic position %d has type %s, expected type %s",
                                335                 :                :                             i + 1, format_type_be(types[i]),
                                336                 :                :                             format_type_be(TEXTOID))));
                                337                 :                : 
  368 jdavis@postgresql.or      338         [ +  + ]:CBC       15518 :         if (argnulls[i + 1])
                                339                 :            138 :             continue;
                                340                 :                : 
                                341                 :          15380 :         argname = TextDatumGetCString(args[i]);
                                342                 :                : 
                                343                 :                :         /*
                                344                 :                :          * The 'version' argument is a special case, not handled by arginfo
                                345                 :                :          * because it's not a valid positional argument.
                                346                 :                :          *
                                347                 :                :          * For now, 'version' is accepted but ignored. In the future it can be
                                348                 :                :          * used to interpret older statistics properly.
                                349                 :                :          */
                                350         [ +  + ]:          15380 :         if (pg_strcasecmp(argname, "version") == 0)
                                351                 :           1734 :             continue;
                                352                 :                : 
  244                           353                 :          13646 :         argnum = get_arg_by_name(argname, arginfo);
                                354                 :                : 
  368                           355   [ +  +  +  + ]:          27286 :         if (argnum < 0 || !stats_check_arg_type(argname, types[i + 1],
  244                           356                 :          13640 :                                                 arginfo[argnum].argtype))
                                357                 :                :         {
  368                           358                 :             18 :             result = false;
                                359                 :             18 :             continue;
                                360                 :                :         }
                                361                 :                : 
                                362                 :          13628 :         positional_fcinfo->args[argnum].value = args[i + 1];
                                363                 :          13628 :         positional_fcinfo->args[argnum].isnull = false;
                                364                 :                :     }
                                365                 :                : 
                                366                 :           1884 :     return result;
                                367                 :                : }
        

Generated by: LCOV version 2.4-beta