LCOV - differential code coverage report
Current view: top level - src/backend/statistics - stat_utils.c (source / functions) Coverage Total Hit UNC UBC GNC CBC
Current: 1a1248818fb8b8b0353c292c632d6a6af77e8209 vs 8e82d6610e7280d7870c0c36ff1c797e3cfb3e09 Lines: 92.4 % 262 242 1 19 22 220
Current Date: 2026-10-12 11:56:10 +0200 Functions: 100.0 % 15 15 1 14
Baseline: lcov-20261012-baseline Branches: 71.8 % 202 145 4 53 12 133
Baseline Date: 2026-10-12 10:36:06 +0200 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 100.0 % 17 17 17
(30,360] days: 93.2 % 162 151 1 10 22 129
(360..) days: 89.2 % 83 74 9 74
Function coverage date bins:
(7,30] days: 100.0 % 2 2 2
(30,360] days: 100.0 % 10 10 1 9
(360..) days: 100.0 % 3 3 3
Branch coverage date bins:
(7,30] days: 100.0 % 10 10 10
(30,360] days: 73.5 % 102 75 4 23 12 63
(360..) days: 66.7 % 90 60 30 60

 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-2026, 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_collation.h"
                                 25                 :                : #include "catalog/pg_database.h"
                                 26                 :                : #include "catalog/pg_statistic.h"
                                 27                 :                : #include "funcapi.h"
                                 28                 :                : #include "miscadmin.h"
                                 29                 :                : #include "nodes/nodeFuncs.h"
                                 30                 :                : #include "statistics/stat_utils.h"
                                 31                 :                : #include "storage/lmgr.h"
                                 32                 :                : #include "utils/acl.h"
                                 33                 :                : #include "utils/array.h"
                                 34                 :                : #include "utils/builtins.h"
                                 35                 :                : #include "utils/lsyscache.h"
                                 36                 :                : #include "utils/rangetypes.h"
                                 37                 :                : #include "utils/rel.h"
                                 38                 :                : #include "utils/syscache.h"
                                 39                 :                : #include "utils/typcache.h"
                                 40                 :                : 
                                 41                 :                : /* Default values assigned to new pg_statistic tuples. */
                                 42                 :                : #define DEFAULT_STATATT_NULL_FRAC      Float4GetDatum(0.0)  /* stanullfrac */
                                 43                 :                : #define DEFAULT_STATATT_AVG_WIDTH      Int32GetDatum(0) /* stawidth, same as
                                 44                 :                :                                                          * unknown */
                                 45                 :                : #define DEFAULT_STATATT_N_DISTINCT     Float4GetDatum(0.0)  /* stadistinct, same as
                                 46                 :                :                                                              * unknown */
                                 47                 :                : 
                                 48                 :                : static Node *statatt_get_index_expr(Relation rel, int attnum);
                                 49                 :                : 
                                 50                 :                : /*
                                 51                 :                :  * Ensure that a given argument is not null.
                                 52                 :                :  */
                                 53                 :                : void
   39 michael@paquier.xyz        54                 :CBC        7265 : stats_check_required_arg(FunctionCallInfo fcinfo,
                                 55                 :                :                          struct StatsArgInfo *arginfo,
                                 56                 :                :                          int argnum)
                                 57                 :                : {
                                 58         [ +  + ]:           7265 :     if (PG_ARGISNULL(argnum))
  731 jdavis@postgresql.or       59         [ +  - ]:             88 :         ereport(ERROR,
                                 60                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 61                 :                :                  errmsg("argument \"%s\" must not be null",
                                 62                 :                :                         arginfo[argnum].argname)));
                                 63                 :           7177 : }
                                 64                 :                : 
                                 65                 :                : /*
                                 66                 :                :  * Check that argument is either NULL or a one dimensional array with no
                                 67                 :                :  * NULLs.
                                 68                 :                :  *
                                 69                 :                :  * If a problem is found, emit a WARNING, and return false. Otherwise return
                                 70                 :                :  * true.
                                 71                 :                :  */
                                 72                 :                : bool
   39 michael@paquier.xyz        73                 :           2784 : stats_check_arg_array(FunctionCallInfo fcinfo,
                                 74                 :                :                       struct StatsArgInfo *arginfo,
                                 75                 :                :                       int argnum)
                                 76                 :                : {
                                 77                 :                :     ArrayType  *arr;
                                 78                 :                : 
                                 79         [ +  + ]:           2784 :     if (PG_ARGISNULL(argnum))
  720 jdavis@postgresql.or       80                 :           2278 :         return true;
                                 81                 :                : 
   39 michael@paquier.xyz        82                 :            506 :     arr = DatumGetArrayTypeP(PG_GETARG_DATUM(argnum));
                                 83                 :                : 
  720 jdavis@postgresql.or       84         [ -  + ]:            506 :     if (ARR_NDIM(arr) != 1)
                                 85                 :                :     {
  594 jdavis@postgresql.or       86         [ #  # ]:UBC           0 :         ereport(WARNING,
                                 87                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 88                 :                :                  errmsg("argument \"%s\" must not be a multidimensional array",
                                 89                 :                :                         arginfo[argnum].argname)));
  720                            90                 :              0 :         return false;
                                 91                 :                :     }
                                 92                 :                : 
  720 jdavis@postgresql.or       93         [ +  + ]:CBC         506 :     if (array_contains_nulls(arr))
                                 94                 :                :     {
  594                            95         [ +  - ]:              4 :         ereport(WARNING,
                                 96                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 97                 :                :                  errmsg("argument \"%s\" array must not contain null values",
                                 98                 :                :                         arginfo[argnum].argname)));
  720                            99                 :              4 :         return false;
                                100                 :                :     }
                                101                 :                : 
                                102                 :            502 :     return true;
                                103                 :                : }
                                104                 :                : 
                                105                 :                : /*
                                106                 :                :  * Enforce parameter pairs that must be specified together (or not at all) for
                                107                 :                :  * a particular stakind, such as most_common_vals and most_common_freqs for
                                108                 :                :  * STATISTIC_KIND_MCV.
                                109                 :                :  *
                                110                 :                :  * If a problem is found, emit a WARNING, and return false. Otherwise return
                                111                 :                :  * true.
                                112                 :                :  */
                                113                 :                : bool
   39 michael@paquier.xyz       114                 :           2784 : stats_check_arg_pair(FunctionCallInfo fcinfo,
                                115                 :                :                      struct StatsArgInfo *arginfo,
                                116                 :                :                      int argnum1, int argnum2)
                                117                 :                : {
                                118   [ +  +  +  + ]:           2784 :     if (PG_ARGISNULL(argnum1) && PG_ARGISNULL(argnum2))
  720 jdavis@postgresql.or      119                 :           2241 :         return true;
                                120                 :                : 
   39 michael@paquier.xyz       121   [ +  +  +  + ]:            543 :     if (PG_ARGISNULL(argnum1) || PG_ARGISNULL(argnum2))
                                122                 :                :     {
                                123         [ +  + ]:             28 :         int         nullarg = PG_ARGISNULL(argnum1) ? argnum1 : argnum2;
                                124         [ +  + ]:             28 :         int         otherarg = PG_ARGISNULL(argnum1) ? argnum2 : argnum1;
                                125                 :                : 
  594 jdavis@postgresql.or      126         [ +  - ]:             28 :         ereport(WARNING,
                                127                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                128                 :                :                  errmsg("argument \"%s\" must be specified when argument \"%s\" is specified",
                                129                 :                :                         arginfo[nullarg].argname,
                                130                 :                :                         arginfo[otherarg].argname)));
                                131                 :                : 
  720                           132                 :             28 :         return false;
                                133                 :                :     }
                                134                 :                : 
                                135                 :            515 :     return true;
                                136                 :                : }
                                137                 :                : 
                                138                 :                : /*
                                139                 :                :  * A role has privileges to set statistics on the relation if any of the
                                140                 :                :  * following are true:
                                141                 :                :  *   - the role owns the current database and the relation is not shared
                                142                 :                :  *   - the role has the MAINTAIN privilege on the relation
                                143                 :                :  */
                                144                 :                : void
  362 nathan@postgresql.or      145                 :           2493 : RangeVarCallbackForStats(const RangeVar *relation,
                                146                 :                :                          Oid relId, Oid oldRelId, void *arg)
                                147                 :                : {
                                148                 :           2493 :     Oid        *locked_oid = (Oid *) arg;
                                149                 :           2493 :     Oid         table_oid = relId;
                                150                 :                :     HeapTuple   tuple;
                                151                 :                :     Form_pg_class form;
                                152                 :                :     char        relkind;
                                153                 :                : 
                                154                 :                :     /*
                                155                 :                :      * If we previously locked some other index's heap, and the name we're
                                156                 :                :      * looking up no longer refers to that relation, release the now-useless
                                157                 :                :      * lock.
                                158                 :                :      */
                                159   [ +  +  -  + ]:           2493 :     if (relId != oldRelId && OidIsValid(*locked_oid))
                                160                 :                :     {
  362 nathan@postgresql.or      161                 :UBC           0 :         UnlockRelationOid(*locked_oid, ShareUpdateExclusiveLock);
                                162                 :              0 :         *locked_oid = InvalidOid;
                                163                 :                :     }
                                164                 :                : 
                                165                 :                :     /* If the relation does not exist, there's nothing more to do. */
  362 nathan@postgresql.or      166         [ +  + ]:CBC        2493 :     if (!OidIsValid(relId))
                                167                 :             20 :         return;
                                168                 :                : 
                                169                 :                :     /* If the relation does exist, check whether it's an index. */
                                170                 :           2473 :     relkind = get_rel_relkind(relId);
                                171   [ +  +  +  + ]:           2473 :     if (relkind == RELKIND_INDEX ||
                                172                 :                :         relkind == RELKIND_PARTITIONED_INDEX)
                                173                 :            321 :         table_oid = IndexGetRelation(relId, false);
                                174                 :                : 
                                175                 :                :     /*
                                176                 :                :      * If retrying yields the same OID, there are a couple of extremely
                                177                 :                :      * unlikely scenarios we need to handle.
                                178                 :                :      */
                                179         [ +  + ]:           2473 :     if (relId == oldRelId)
                                180                 :                :     {
                                181                 :                :         /*
                                182                 :                :          * If a previous lookup found an index, but the current lookup did
                                183                 :                :          * not, the index was dropped and the OID was reused for something
                                184                 :                :          * else between lookups.  In theory, we could simply drop our lock on
                                185                 :                :          * the index's parent table and proceed, but in the interest of
                                186                 :                :          * avoiding complexity, we just error.
                                187                 :                :          */
                                188   [ +  -  -  + ]:              2 :         if (table_oid == relId && OidIsValid(*locked_oid))
  362 nathan@postgresql.or      189         [ #  # ]:UBC           0 :             ereport(ERROR,
                                190                 :                :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
                                191                 :                :                      errmsg("index \"%s\" was concurrently dropped",
                                192                 :                :                             relation->relname)));
                                193                 :                : 
                                194                 :                :         /*
                                195                 :                :          * If the current lookup found an index but a previous lookup either
                                196                 :                :          * did not find an index or found one with a different parent
                                197                 :                :          * relation, the relation was dropped and the OID was reused for an
                                198                 :                :          * index between lookups.  RangeVarGetRelidExtended() will have
                                199                 :                :          * already locked the index at this point, so we can't just lock the
                                200                 :                :          * newly discovered parent table OID without risking deadlock.  As
                                201                 :                :          * above, we just error in this case.
                                202                 :                :          */
  362 nathan@postgresql.or      203   [ -  +  -  - ]:CBC           2 :         if (table_oid != relId && table_oid != *locked_oid)
  362 nathan@postgresql.or      204         [ #  # ]:UBC           0 :             ereport(ERROR,
                                205                 :                :                     (errcode(ERRCODE_UNDEFINED_OBJECT),
                                206                 :                :                      errmsg("index \"%s\" was concurrently created",
                                207                 :                :                             relation->relname)));
                                208                 :                :     }
                                209                 :                : 
  362 nathan@postgresql.or      210                 :CBC        2473 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
                                211         [ -  + ]:           2473 :     if (!HeapTupleIsValid(tuple))
  362 nathan@postgresql.or      212         [ #  # ]:UBC           0 :         elog(ERROR, "cache lookup failed for OID %u", table_oid);
  362 nathan@postgresql.or      213                 :CBC        2473 :     form = (Form_pg_class) GETSTRUCT(tuple);
                                214                 :                : 
                                215                 :                :     /* the relkinds that can be used with ANALYZE */
                                216         [ +  + ]:           2473 :     switch (form->relkind)
                                217                 :                :     {
  731 jdavis@postgresql.or      218                 :           2453 :         case RELKIND_RELATION:
                                219                 :                :         case RELKIND_MATVIEW:
                                220                 :                :         case RELKIND_FOREIGN_TABLE:
                                221                 :                :         case RELKIND_PARTITIONED_TABLE:
                                222                 :           2453 :             break;
                                223                 :             20 :         default:
                                224         [ +  - ]:             20 :             ereport(ERROR,
                                225                 :                :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                226                 :                :                      errmsg("cannot modify statistics for relation \"%s\"",
                                227                 :                :                             NameStr(form->relname)),
                                228                 :                :                      errdetail_relkind_not_supported(form->relkind)));
                                229                 :                :     }
                                230                 :                : 
  362 nathan@postgresql.or      231         [ -  + ]:           2453 :     if (form->relisshared)
  731 jdavis@postgresql.or      232         [ #  # ]:UBC           0 :         ereport(ERROR,
                                233                 :                :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                234                 :                :                  errmsg("cannot modify statistics for shared relation")));
                                235                 :                : 
                                236                 :                :     /* Check permissions */
  731 jdavis@postgresql.or      237         [ +  + ]:CBC        2453 :     if (!object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()))
                                238                 :                :     {
  362 nathan@postgresql.or      239                 :             16 :         AclResult   aclresult = pg_class_aclcheck(table_oid,
                                240                 :                :                                                   GetUserId(),
                                241                 :                :                                                   ACL_MAINTAIN);
                                242                 :                : 
  731 jdavis@postgresql.or      243         [ +  + ]:             16 :         if (aclresult != ACLCHECK_OK)
                                244                 :              8 :             aclcheck_error(aclresult,
  362 nathan@postgresql.or      245                 :              8 :                            get_relkind_objtype(form->relkind),
                                246                 :              8 :                            NameStr(form->relname));
                                247                 :                :     }
                                248                 :                : 
                                249                 :           2445 :     ReleaseSysCache(tuple);
                                250                 :                : 
                                251                 :                :     /* Lock heap before index to avoid deadlock. */
                                252   [ +  +  +  + ]:           2445 :     if (relId != oldRelId && table_oid != relId)
                                253                 :                :     {
                                254                 :            321 :         LockRelationOid(table_oid, ShareUpdateExclusiveLock);
                                255                 :            321 :         *locked_oid = table_oid;
                                256                 :                :     }
                                257                 :                : }
                                258                 :                : 
                                259                 :                : 
                                260                 :                : /*
                                261                 :                :  * Find the argument number for the given argument name, returning -1 if not
                                262                 :                :  * found.
                                263                 :                :  */
                                264                 :                : static int
  594 jdavis@postgresql.or      265                 :          17390 : get_arg_by_name(const char *argname, struct StatsArgInfo *arginfo)
                                266                 :                : {
                                267                 :                :     int         argnum;
                                268                 :                : 
  718                           269         [ +  + ]:          85296 :     for (argnum = 0; arginfo[argnum].argname != NULL; argnum++)
                                270         [ +  + ]:          85288 :         if (pg_strcasecmp(argname, arginfo[argnum].argname) == 0)
                                271                 :          17382 :             return argnum;
                                272                 :                : 
  594                           273         [ +  - ]:              8 :     ereport(WARNING,
                                274                 :                :             (errmsg("unrecognized argument name: \"%s\"", argname)));
                                275                 :                : 
  718                           276                 :              8 :     return -1;
                                277                 :                : }
                                278                 :                : 
                                279                 :                : /*
                                280                 :                :  * Ensure that a given argument matched the expected type.
                                281                 :                :  */
                                282                 :                : static bool
  594                           283                 :          17382 : stats_check_arg_type(const char *argname, Oid argtype, Oid expectedtype)
                                284                 :                : {
  718                           285         [ +  + ]:          17382 :     if (argtype != expectedtype)
                                286                 :                :     {
  594                           287         [ +  - ]:             16 :         ereport(WARNING,
                                288                 :                :                 (errmsg("argument \"%s\" has type %s, expected type %s",
                                289                 :                :                         argname, format_type_be(argtype),
                                290                 :                :                         format_type_be(expectedtype))));
  718                           291                 :             16 :         return false;
                                292                 :                :     }
                                293                 :                : 
                                294                 :          17366 :     return true;
                                295                 :                : }
                                296                 :                : 
                                297                 :                : /*
                                298                 :                :  * Check if attribute of an index is an expression, then retrieve the
                                299                 :                :  * expression if is it the case.
                                300                 :                :  *
                                301                 :                :  * If the attnum specified is known to be an expression, then we must
                                302                 :                :  * walk the list attributes up to the specified attnum to get the right
                                303                 :                :  * expression.
                                304                 :                :  */
                                305                 :                : static Node *
  291 michael@paquier.xyz       306                 :            928 : statatt_get_index_expr(Relation rel, int attnum)
                                307                 :                : {
                                308                 :                :     List       *index_exprs;
                                309                 :                :     ListCell   *indexpr_item;
                                310                 :                : 
                                311                 :                :     /* relation is not an index */
                                312         [ +  + ]:            928 :     if (rel->rd_rel->relkind != RELKIND_INDEX &&
                                313         [ +  - ]:            918 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
                                314                 :            918 :         return NULL;
                                315                 :                : 
                                316                 :             10 :     index_exprs = RelationGetIndexExpressions(rel);
                                317                 :                : 
                                318                 :                :     /* index has no expressions to give */
                                319         [ -  + ]:             10 :     if (index_exprs == NIL)
  291 michael@paquier.xyz       320                 :UBC           0 :         return NULL;
                                321                 :                : 
                                322                 :                :     /*
                                323                 :                :      * The index's attnum points directly to a relation attnum, hence it is
                                324                 :                :      * not an expression attribute.
                                325                 :                :      */
  291 michael@paquier.xyz       326         [ -  + ]:CBC          10 :     if (rel->rd_index->indkey.values[attnum - 1] != 0)
  291 michael@paquier.xyz       327                 :UBC           0 :         return NULL;
                                328                 :                : 
  291 michael@paquier.xyz       329                 :CBC          10 :     indexpr_item = list_head(rel->rd_indexprs);
                                330                 :                : 
                                331         [ -  + ]:             10 :     for (int i = 0; i < attnum - 1; i++)
  291 michael@paquier.xyz       332         [ #  # ]:UBC           0 :         if (rel->rd_index->indkey.values[i] == 0)
                                333                 :              0 :             indexpr_item = lnext(rel->rd_indexprs, indexpr_item);
                                334                 :                : 
  291 michael@paquier.xyz       335         [ -  + ]:CBC          10 :     if (indexpr_item == NULL)   /* shouldn't happen */
  291 michael@paquier.xyz       336         [ #  # ]:UBC           0 :         elog(ERROR, "too few entries in indexprs list");
                                337                 :                : 
  291 michael@paquier.xyz       338                 :CBC          10 :     return (Node *) lfirst(indexpr_item);
                                339                 :                : }
                                340                 :                : 
                                341                 :                : /*
                                342                 :                :  * Translate variadic argument pairs from 'pairs_fcinfo' into a
                                343                 :                :  * 'positional_fcinfo' appropriate for calling relation_statistics_update() or
                                344                 :                :  * attribute_statistics_update() with positional arguments.
                                345                 :                :  *
                                346                 :                :  * Caller should have already initialized positional_fcinfo with a size
                                347                 :                :  * appropriate for calling the intended positional function, and arginfo
                                348                 :                :  * should also match the intended positional function.
                                349                 :                :  */
                                350                 :                : bool
   39                           351                 :           2475 : stats_fill_fcinfo_from_arg_pairs(FunctionCallInfo pairs_fcinfo,
                                352                 :                :                                  FunctionCallInfo positional_fcinfo,
                                353                 :                :                                  struct StatsArgInfo *arginfo)
                                354                 :                : {
                                355                 :                :     Datum      *args;
                                356                 :                :     bool       *argnulls;
                                357                 :                :     Oid        *types;
                                358                 :                :     int         nargs;
  718 jdavis@postgresql.or      359                 :           2475 :     bool        result = true;
                                360                 :                : 
                                361                 :                :     /* clear positional args */
                                362         [ +  + ]:          30513 :     for (int i = 0; arginfo[i].argname != NULL; i++)
                                363                 :                :     {
   39 michael@paquier.xyz       364                 :          28038 :         positional_fcinfo->args[i].value = (Datum) 0;
                                365                 :          28038 :         positional_fcinfo->args[i].isnull = true;
                                366                 :                :     }
                                367                 :                : 
  718 jdavis@postgresql.or      368                 :           2475 :     nargs = extract_variadic_args(pairs_fcinfo, 0, true,
                                369                 :                :                                   &args, &types, &argnulls);
                                370                 :                : 
                                371         [ +  + ]:           2475 :     if (nargs % 2 != 0)
                                372         [ +  - ]:              4 :         ereport(ERROR,
                                373                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                374                 :                :                  errmsg("variadic arguments must be name/value pairs"),
                                375                 :                :                  errhint("Provide an even number of variadic arguments that can be divided into pairs.")));
                                376                 :                : 
                                377                 :                :     /*
                                378                 :                :      * For each argument name/value pair, find corresponding positional
                                379                 :                :      * argument for the argument name, and assign the argument value to
                                380                 :                :      * positional_fcinfo.
                                381                 :                :      */
                                382         [ +  + ]:          21992 :     for (int i = 0; i < nargs; i += 2)
                                383                 :                :     {
                                384                 :                :         int         argnum;
                                385                 :                :         char       *argname;
                                386                 :                : 
                                387         [ +  + ]:          19525 :         if (argnulls[i])
                                388         [ +  - ]:              4 :             ereport(ERROR,
                                389                 :                :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
                                390                 :                :                      errmsg("name at variadic position %d is null", i + 1)));
                                391                 :                : 
                                392         [ -  + ]:          19521 :         if (types[i] != TEXTOID)
  718 jdavis@postgresql.or      393         [ #  # ]:UBC           0 :             ereport(ERROR,
                                394                 :                :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
                                395                 :                :                      errmsg("name at variadic position %d has type %s, expected type %s",
                                396                 :                :                             i + 1, format_type_be(types[i]),
                                397                 :                :                             format_type_be(TEXTOID))));
                                398                 :                : 
  718 jdavis@postgresql.or      399         [ +  + ]:CBC       19521 :         if (argnulls[i + 1])
                                400                 :            296 :             continue;
                                401                 :                : 
                                402                 :          19225 :         argname = TextDatumGetCString(args[i]);
                                403                 :                : 
                                404                 :                :         /*
                                405                 :                :          * The 'version' argument is a special case, not handled by arginfo
                                406                 :                :          * because it's not a valid positional argument.
                                407                 :                :          *
                                408                 :                :          * For now, 'version' is accepted but ignored. In the future it can be
                                409                 :                :          * used to interpret older statistics properly.
                                410                 :                :          */
                                411         [ +  + ]:          19225 :         if (pg_strcasecmp(argname, "version") == 0)
                                412                 :           1835 :             continue;
                                413                 :                : 
  594                           414                 :          17390 :         argnum = get_arg_by_name(argname, arginfo);
                                415                 :                : 
  718                           416   [ +  +  +  + ]:          34772 :         if (argnum < 0 || !stats_check_arg_type(argname, types[i + 1],
  594                           417                 :          17382 :                                                 arginfo[argnum].argtype))
                                418                 :                :         {
  718                           419                 :             24 :             result = false;
                                420                 :             24 :             continue;
                                421                 :                :         }
                                422                 :                : 
   39 michael@paquier.xyz       423                 :          17366 :         positional_fcinfo->args[argnum].value = args[i + 1];
                                424                 :          17366 :         positional_fcinfo->args[argnum].isnull = false;
                                425                 :                :     }
                                426                 :                : 
  718 jdavis@postgresql.or      427                 :           2467 :     return result;
                                428                 :                : }
                                429                 :                : 
                                430                 :                : /*
                                431                 :                :  * Derive type information from a relation attribute.
                                432                 :                :  *
                                433                 :                :  * This is needed for setting most slot statistics for all data types.
                                434                 :                :  *
                                435                 :                :  * This duplicates the logic in examine_attribute() but it will not skip the
                                436                 :                :  * attribute if the attstattarget is 0.
                                437                 :                :  *
                                438                 :                :  * *atttypid and *atttypmod describe the type as declared.  *basetypcache is
                                439                 :                :  * the cache entry of the base type behind any domain.
                                440                 :                :  *
                                441                 :                :  * This information, retrieved from pg_attribute and pg_type with some
                                442                 :                :  * specific handling for index expressions, is a prerequisite to calling
                                443                 :                :  * any of the other statatt_*() functions.
                                444                 :                :  */
                                445                 :                : void
  291 michael@paquier.xyz       446                 :            928 : statatt_get_type(Oid reloid, AttrNumber attnum,
                                447                 :                :                  Oid *atttypid, int32 *atttypmod,
                                448                 :                :                  TypeCacheEntry **basetypcache, Oid *atttypcoll,
                                449                 :                :                  Oid *eq_opr, Oid *lt_opr)
                                450                 :                : {
                                451                 :            928 :     Relation    rel = relation_open(reloid, AccessShareLock);
                                452                 :                :     Form_pg_attribute attr;
                                453                 :                :     HeapTuple   atup;
                                454                 :                :     Node       *expr;
                                455                 :                : 
                                456                 :            928 :     atup = SearchSysCache2(ATTNUM, ObjectIdGetDatum(reloid),
                                457                 :                :                            Int16GetDatum(attnum));
                                458                 :                : 
                                459                 :                :     /* Attribute not found */
                                460         [ -  + ]:            928 :     if (!HeapTupleIsValid(atup))
  291 michael@paquier.xyz       461         [ #  # ]:UBC           0 :         ereport(ERROR,
                                462                 :                :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
                                463                 :                :                  errmsg("column %d of relation \"%s\" does not exist",
                                464                 :                :                         attnum, RelationGetRelationName(rel))));
                                465                 :                : 
  291 michael@paquier.xyz       466                 :CBC         928 :     attr = (Form_pg_attribute) GETSTRUCT(atup);
                                467                 :                : 
                                468         [ -  + ]:            928 :     if (attr->attisdropped)
  291 michael@paquier.xyz       469         [ #  # ]:UBC           0 :         ereport(ERROR,
                                470                 :                :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
                                471                 :                :                  errmsg("column %d of relation \"%s\" does not exist",
                                472                 :                :                         attnum, RelationGetRelationName(rel))));
                                473                 :                : 
  291 michael@paquier.xyz       474                 :CBC         928 :     expr = statatt_get_index_expr(rel, attr->attnum);
                                475                 :                : 
                                476                 :                :     /*
                                477                 :                :      * When analyzing an expression index, believe the expression tree's type
                                478                 :                :      * not the column datatype --- the latter might be the opckeytype storage
                                479                 :                :      * type of the opclass, which is not interesting for our purposes.  This
                                480                 :                :      * mimics the behavior of examine_attribute().
                                481                 :                :      */
                                482         [ +  + ]:            928 :     if (expr == NULL)
                                483                 :                :     {
                                484                 :            918 :         *atttypid = attr->atttypid;
                                485                 :            918 :         *atttypmod = attr->atttypmod;
                                486                 :            918 :         *atttypcoll = attr->attcollation;
                                487                 :                :     }
                                488                 :                :     else
                                489                 :                :     {
                                490                 :             10 :         *atttypid = exprType(expr);
                                491                 :             10 :         *atttypmod = exprTypmod(expr);
                                492                 :                : 
                                493         [ -  + ]:             10 :         if (OidIsValid(attr->attcollation))
  291 michael@paquier.xyz       494                 :UBC           0 :             *atttypcoll = attr->attcollation;
                                495                 :                :         else
  291 michael@paquier.xyz       496                 :CBC          10 :             *atttypcoll = exprCollation(expr);
                                497                 :                :     }
                                498                 :            928 :     ReleaseSysCache(atup);
                                499                 :                : 
                                500                 :                :     /* finds the right operators even if atttypid is a domain */
   16                           501                 :            928 :     *basetypcache = lookup_type_cache(*atttypid, TYPECACHE_LT_OPR |
                                502                 :                :                                       TYPECACHE_EQ_OPR |
                                503                 :                :                                       TYPECACHE_DOMAIN_BASE_INFO);
                                504         [ +  + ]:            928 :     if (OidIsValid((*basetypcache)->domainBaseType))
                                505                 :             36 :         *basetypcache = lookup_type_cache((*basetypcache)->domainBaseType,
                                506                 :                :                                           TYPECACHE_LT_OPR |
                                507                 :                :                                           TYPECACHE_EQ_OPR);
                                508                 :                : 
                                509                 :            928 :     *eq_opr = (*basetypcache)->eq_opr;
                                510                 :            928 :     *lt_opr = (*basetypcache)->lt_opr;
                                511                 :                : 
                                512                 :                :     /*
                                513                 :                :      * Special case: collation for tsvector is DEFAULT_COLLATION_OID. See
                                514                 :                :      * compute_tsvector_stats().
                                515                 :                :      */
                                516         [ +  + ]:            928 :     if ((*basetypcache)->type_id == TSVECTOROID)
  291                           517                 :              9 :         *atttypcoll = DEFAULT_COLLATION_OID;
                                518                 :                : 
                                519                 :            928 :     relation_close(rel, NoLock);
                                520                 :            928 : }
                                521                 :                : 
                                522                 :                : /*
                                523                 :                :  * Derive element type information from the base type of an attribute.  This
                                524                 :                :  * information is needed when the given type is one that contains elements of
                                525                 :                :  * other types.
                                526                 :                :  *
                                527                 :                :  * The type cache entry should be derived from a previous call to
                                528                 :                :  * statatt_get_type().
                                529                 :                :  */
                                530                 :                : bool
   16                           531                 :             72 : statatt_get_elem_type(TypeCacheEntry *basetypcache,
                                532                 :                :                       Oid *elemtypid, Oid *elem_eq_opr)
                                533                 :                : {
                                534                 :                :     TypeCacheEntry *elemtypcache;
                                535                 :                : 
                                536         [ +  + ]:             72 :     if (basetypcache->type_id == TSVECTOROID)
                                537                 :                :     {
                                538                 :                :         /*
                                539                 :                :          * Special case: element type for tsvector is text. See
                                540                 :                :          * compute_tsvector_stats().
                                541                 :                :          */
  291                           542                 :             17 :         *elemtypid = TEXTOID;
                                543                 :                :     }
                                544                 :                :     else
                                545                 :                :     {
                                546                 :                :         /* find the underlying element type */
   16                           547                 :             55 :         *elemtypid = get_element_type(basetypcache->type_id);
                                548                 :                :     }
                                549                 :                : 
  291                           550         [ +  + ]:             72 :     if (!OidIsValid(*elemtypid))
                                551                 :             20 :         return false;
                                552                 :                : 
                                553                 :                :     /* finds the right operator even if elemtypid is a domain */
                                554                 :             52 :     elemtypcache = lookup_type_cache(*elemtypid, TYPECACHE_EQ_OPR);
                                555         [ -  + ]:             52 :     if (!OidIsValid(elemtypcache->eq_opr))
  291 michael@paquier.xyz       556                 :UBC           0 :         return false;
                                557                 :                : 
  291 michael@paquier.xyz       558                 :CBC          52 :     *elem_eq_opr = elemtypcache->eq_opr;
                                559                 :                : 
                                560                 :             52 :     return true;
                                561                 :                : }
                                562                 :                : 
                                563                 :                : /*
                                564                 :                :  * Derive the range type to use from the attribute type, returning false if
                                565                 :                :  * the attribute cannot have range statistics at all.
                                566                 :                :  *
                                567                 :                :  * For a multirange type, we step down to its range type, because
                                568                 :                :  * compute_range_stats() stores range bounds even when analyzing a multirange
                                569                 :                :  * column (see also range_typanalyze() and multirange_typanalyze()).
                                570                 :                :  *
                                571                 :                :  * The type cache entry should be derived from a previous call to
                                572                 :                :  * statatt_get_type(), so that any domain has already been looked through.
                                573                 :                :  */
                                574                 :                : bool
   16                           575                 :             96 : statatt_get_range_type(TypeCacheEntry *basetypcache, Oid *rangetypid)
                                576                 :                : {
                                577         [ +  + ]:             96 :     if (basetypcache->typtype == TYPTYPE_MULTIRANGE)
                                578                 :             41 :         *rangetypid = get_multirange_range(basetypcache->type_id);
                                579         [ +  + ]:             55 :     else if (basetypcache->typtype == TYPTYPE_RANGE)
                                580                 :             39 :         *rangetypid = basetypcache->type_id;
                                581                 :                :     else
                                582                 :                :     {
                                583                 :             16 :         *rangetypid = InvalidOid;
                                584                 :             16 :         return false;
                                585                 :                :     }
                                586                 :                : 
                                587                 :             80 :     return true;
                                588                 :                : }
                                589                 :                : 
                                590                 :                : /*
                                591                 :                :  * Build an array with element type typid from a text datum, used as
                                592                 :                :  * value of an attribute in a tuple to-be-inserted into pg_statistic.
                                593                 :                :  *
                                594                 :                :  * The typid and typmod should be derived from a previous call to
                                595                 :                :  * statatt_get_type().
                                596                 :                :  *
                                597                 :                :  * If an error is encountered, capture it and throw a WARNING, with "ok" set
                                598                 :                :  * to false.  If the resulting array contains NULLs, raise a WARNING and
                                599                 :                :  * set "ok" to false.  When the operation succeeds, set "ok" to true.
                                600                 :                :  */
                                601                 :                : Datum
  291                           602                 :            955 : statatt_build_stavalues(const char *staname, FmgrInfo *array_in, Datum d, Oid typid,
                                603                 :                :                         int32 typmod, bool *ok)
                                604                 :                : {
                                605                 :                :     char       *s;
                                606                 :                :     Datum       result;
                                607                 :            955 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
                                608                 :                : 
                                609                 :            955 :     escontext.details_wanted = true;
                                610                 :                : 
                                611                 :            955 :     s = TextDatumGetCString(d);
                                612                 :                : 
  104                           613         [ +  + ]:            955 :     if (!InputFunctionCallSafe(array_in, s, typid, typmod,
                                614                 :                :                                (Node *) &escontext, &result))
                                615                 :                :     {
                                616                 :             12 :         pfree(s);
  291                           617                 :             12 :         escontext.error_data->elevel = WARNING;
                                618                 :             12 :         ThrowErrorData(escontext.error_data);
                                619                 :             12 :         *ok = false;
                                620                 :             12 :         return (Datum) 0;
                                621                 :                :     }
                                622                 :                : 
  104                           623                 :            943 :     pfree(s);
                                624                 :                : 
  154                           625         [ +  + ]:            943 :     if (ARR_NDIM(DatumGetArrayTypeP(result)) != 1)
                                626                 :                :     {
                                627         [ +  - ]:              4 :         ereport(WARNING,
                                628                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                629                 :                :                  errmsg("\"%s\" must be a one-dimensional array", staname)));
                                630                 :              4 :         *ok = false;
                                631                 :              4 :         return (Datum) 0;
                                632                 :                :     }
                                633                 :                : 
  291                           634         [ +  + ]:            939 :     if (array_contains_nulls(DatumGetArrayTypeP(result)))
                                635                 :                :     {
                                636         [ +  - ]:              4 :         ereport(WARNING,
                                637                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                638                 :                :                  errmsg("\"%s\" array must not contain null values", staname)));
                                639                 :              4 :         *ok = false;
                                640                 :              4 :         return (Datum) 0;
                                641                 :                :     }
                                642                 :                : 
                                643                 :            935 :     *ok = true;
                                644                 :                : 
                                645                 :            935 :     return result;
                                646                 :                : }
                                647                 :                : 
                                648                 :                : /*
                                649                 :                :  * Find and update the slot of a stakind, or use the first empty slot.
                                650                 :                :  *
                                651                 :                :  * Core statistics types expect the stakind value to be one of the
                                652                 :                :  * STATISTIC_KIND_* constants defined in pg_statistic.h, but types defined
                                653                 :                :  * by extensions are not restricted to those values.
                                654                 :                :  *
                                655                 :                :  * In the case of core statistics, the required staop is determined by the
                                656                 :                :  * stakind given and will either be a hardcoded oid, or the eq/lt operator
                                657                 :                :  * derived from statatt_get_type().  Likewise, types defined by extensions
                                658                 :                :  * have no such restriction.
                                659                 :                :  *
                                660                 :                :  * The stacoll value should be either the atttypcoll derived from
                                661                 :                :  * statatt_get_type(), or a hardcoded value required by that particular
                                662                 :                :  * stakind.
                                663                 :                :  *
                                664                 :                :  * The value/null pairs for stanumbers and stavalues should be calculated
                                665                 :                :  * based on the stakind, using statatt_build_stavalues() or constructed arrays.
                                666                 :                :  */
                                667                 :                : void
                                668                 :           1826 : statatt_set_slot(Datum *values, bool *nulls, bool *replaces,
                                669                 :                :                  int16 stakind, Oid staop, Oid stacoll,
                                670                 :                :                  Datum stanumbers, bool stanumbers_isnull,
                                671                 :                :                  Datum stavalues, bool stavalues_isnull)
                                672                 :                : {
                                673                 :                :     int         slotidx;
                                674                 :           1826 :     int         first_empty = -1;
                                675                 :                :     AttrNumber  stakind_attnum;
                                676                 :                :     AttrNumber  staop_attnum;
                                677                 :                :     AttrNumber  stacoll_attnum;
                                678                 :                : 
                                679                 :                :     /* find existing slot with given stakind */
                                680         [ +  + ]:          10940 :     for (slotidx = 0; slotidx < STATISTIC_NUM_SLOTS; slotidx++)
                                681                 :                :     {
                                682                 :           9118 :         stakind_attnum = Anum_pg_statistic_stakind1 - 1 + slotidx;
                                683                 :                : 
                                684   [ +  +  +  + ]:          12078 :         if (first_empty < 0 &&
                                685                 :           2960 :             DatumGetInt16(values[stakind_attnum]) == 0)
                                686                 :           1822 :             first_empty = slotidx;
                                687         [ +  + ]:           9118 :         if (DatumGetInt16(values[stakind_attnum]) == stakind)
                                688                 :              4 :             break;
                                689                 :                :     }
                                690                 :                : 
                                691   [ +  +  +  - ]:           1826 :     if (slotidx >= STATISTIC_NUM_SLOTS && first_empty >= 0)
                                692                 :           1822 :         slotidx = first_empty;
                                693                 :                : 
                                694         [ -  + ]:           1826 :     if (slotidx >= STATISTIC_NUM_SLOTS)
  291 michael@paquier.xyz       695         [ #  # ]:UBC           0 :         ereport(ERROR,
                                696                 :                :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                                697                 :                :                  errmsg("maximum number of statistics slots exceeded: %d",
                                698                 :                :                         slotidx + 1)));
                                699                 :                : 
  291 michael@paquier.xyz       700                 :CBC        1826 :     stakind_attnum = Anum_pg_statistic_stakind1 - 1 + slotidx;
                                701                 :           1826 :     staop_attnum = Anum_pg_statistic_staop1 - 1 + slotidx;
                                702                 :           1826 :     stacoll_attnum = Anum_pg_statistic_stacoll1 - 1 + slotidx;
                                703                 :                : 
                                704         [ +  + ]:           1826 :     if (DatumGetInt16(values[stakind_attnum]) != stakind)
                                705                 :                :     {
                                706                 :           1822 :         values[stakind_attnum] = Int16GetDatum(stakind);
                                707                 :           1822 :         replaces[stakind_attnum] = true;
                                708                 :                :     }
                                709         [ +  + ]:           1826 :     if (DatumGetObjectId(values[staop_attnum]) != staop)
                                710                 :                :     {
                                711                 :           1770 :         values[staop_attnum] = ObjectIdGetDatum(staop);
                                712                 :           1770 :         replaces[staop_attnum] = true;
                                713                 :                :     }
                                714         [ +  + ]:           1826 :     if (DatumGetObjectId(values[stacoll_attnum]) != stacoll)
                                715                 :                :     {
                                716                 :            424 :         values[stacoll_attnum] = ObjectIdGetDatum(stacoll);
                                717                 :            424 :         replaces[stacoll_attnum] = true;
                                718                 :                :     }
                                719         [ +  + ]:           1826 :     if (!stanumbers_isnull)
                                720                 :                :     {
                                721                 :           1350 :         values[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = stanumbers;
                                722                 :           1350 :         nulls[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = false;
                                723                 :           1350 :         replaces[Anum_pg_statistic_stanumbers1 - 1 + slotidx] = true;
                                724                 :                :     }
                                725         [ +  + ]:           1826 :     if (!stavalues_isnull)
                                726                 :                :     {
                                727                 :           1046 :         values[Anum_pg_statistic_stavalues1 - 1 + slotidx] = stavalues;
                                728                 :           1046 :         nulls[Anum_pg_statistic_stavalues1 - 1 + slotidx] = false;
                                729                 :           1046 :         replaces[Anum_pg_statistic_stavalues1 - 1 + slotidx] = true;
                                730                 :                :     }
                                731                 :           1826 : }
                                732                 :                : 
                                733                 :                : /*
                                734                 :                :  * Initialize values and nulls for a new pg_statistic tuple.
                                735                 :                :  *
                                736                 :                :  * The caller is responsible for allocating the arrays where the results are
                                737                 :                :  * stored, which should be of size Natts_pg_statistic.
                                738                 :                :  *
                                739                 :                :  * When using this routine for a tuple inserted into pg_statistic, reloid,
                                740                 :                :  * attnum and inherited flags should all be set.
                                741                 :                :  *
                                742                 :                :  * When using this routine for a tuple that is an element of a stxdexpr
                                743                 :                :  * array inserted into pg_statistic_ext_data, reloid, attnum and inherited
                                744                 :                :  * should be respectively set to InvalidOid, InvalidAttrNumber and false.
                                745                 :                :  */
                                746                 :                : void
                                747                 :           1007 : statatt_init_empty_tuple(Oid reloid, int16 attnum, bool inherited,
                                748                 :                :                          Datum *values, bool *nulls, bool *replaces)
                                749                 :                : {
                                750                 :           1007 :     memset(nulls, true, sizeof(bool) * Natts_pg_statistic);
                                751                 :           1007 :     memset(replaces, true, sizeof(bool) * Natts_pg_statistic);
                                752                 :                : 
                                753                 :                :     /* This must initialize non-NULL attributes */
                                754                 :           1007 :     values[Anum_pg_statistic_starelid - 1] = ObjectIdGetDatum(reloid);
                                755                 :           1007 :     nulls[Anum_pg_statistic_starelid - 1] = false;
                                756                 :           1007 :     values[Anum_pg_statistic_staattnum - 1] = Int16GetDatum(attnum);
                                757                 :           1007 :     nulls[Anum_pg_statistic_staattnum - 1] = false;
                                758                 :           1007 :     values[Anum_pg_statistic_stainherit - 1] = BoolGetDatum(inherited);
                                759                 :           1007 :     nulls[Anum_pg_statistic_stainherit - 1] = false;
                                760                 :                : 
                                761                 :           1007 :     values[Anum_pg_statistic_stanullfrac - 1] = DEFAULT_STATATT_NULL_FRAC;
                                762                 :           1007 :     nulls[Anum_pg_statistic_stanullfrac - 1] = false;
                                763                 :           1007 :     values[Anum_pg_statistic_stawidth - 1] = DEFAULT_STATATT_AVG_WIDTH;
                                764                 :           1007 :     nulls[Anum_pg_statistic_stawidth - 1] = false;
                                765                 :           1007 :     values[Anum_pg_statistic_stadistinct - 1] = DEFAULT_STATATT_N_DISTINCT;
                                766                 :           1007 :     nulls[Anum_pg_statistic_stadistinct - 1] = false;
                                767                 :                : 
                                768                 :                :     /* initialize stakind, staop, and stacoll slots */
                                769         [ +  + ]:           6042 :     for (int slotnum = 0; slotnum < STATISTIC_NUM_SLOTS; slotnum++)
                                770                 :                :     {
                                771                 :           5035 :         values[Anum_pg_statistic_stakind1 + slotnum - 1] = (Datum) 0;
                                772                 :           5035 :         nulls[Anum_pg_statistic_stakind1 + slotnum - 1] = false;
                                773                 :           5035 :         values[Anum_pg_statistic_staop1 + slotnum - 1] = ObjectIdGetDatum(InvalidOid);
                                774                 :           5035 :         nulls[Anum_pg_statistic_staop1 + slotnum - 1] = false;
                                775                 :           5035 :         values[Anum_pg_statistic_stacoll1 + slotnum - 1] = ObjectIdGetDatum(InvalidOid);
                                776                 :           5035 :         nulls[Anum_pg_statistic_stacoll1 + slotnum - 1] = false;
                                777                 :                :     }
                                778                 :           1007 : }
                                779                 :                : 
                                780                 :                : /*
                                781                 :                :  * Check that an imported bounds histogram (STATISTIC_KIND_BOUNDS_HISTOGRAM)
                                782                 :                :  * is shaped the same way ANALYZE builds it in compute_range_stats().
                                783                 :                :  *
                                784                 :                :  * For both range-typed and multirange-typed columns the histogram is an array
                                785                 :                :  * of ranges, so we take the range type from the array's element type.
                                786                 :                :  */
                                787                 :                : bool
   95 michael@paquier.xyz       788                 :GNC          68 : statatt_check_bounds_histogram(Datum arrayval)
                                789                 :                : {
                                790                 :             68 :     ArrayType  *arr = DatumGetArrayTypeP(arrayval);
                                791                 :             68 :     Oid         rngtypid = ARR_ELEMTYPE(arr);
                                792                 :                :     TypeCacheEntry *typcache;
                                793                 :                :     int16       elmlen;
                                794                 :                :     bool        elmbyval;
                                795                 :                :     char        elmalign;
                                796                 :                :     Datum      *elems;
                                797                 :                :     bool       *nulls;
                                798                 :                :     int         nelems;
                                799                 :             68 :     RangeBound  prev_lower = {0};
                                800                 :             68 :     RangeBound  prev_upper = {0};
                                801                 :                : 
                                802                 :             68 :     typcache = lookup_type_cache(rngtypid, TYPECACHE_RANGE_INFO);
                                803                 :                : 
                                804                 :                :     /*
                                805                 :                :      * The element type should always be a range type here.  This is
                                806                 :                :      * defensive. If it isn't, the bounds histogram is never consulted by the
                                807                 :                :      * range estimator, and there is nothing to verify.
                                808                 :                :      */
                                809         [ -  + ]:             68 :     if (typcache->rngelemtype == NULL)
   95 michael@paquier.xyz       810                 :UNC           0 :         return true;
                                811                 :                : 
   95 michael@paquier.xyz       812                 :GNC          68 :     get_typlenbyvalalign(rngtypid, &elmlen, &elmbyval, &elmalign);
                                813                 :             68 :     deconstruct_array(arr, rngtypid, elmlen, elmbyval, elmalign,
                                814                 :                :                       &elems, &nulls, &nelems);
                                815                 :                : 
                                816         [ +  + ]:            442 :     for (int i = 0; i < nelems; i++)
                                817                 :                :     {
                                818                 :                :         RangeBound  lower,
                                819                 :                :                     upper;
                                820                 :                :         bool        empty;
                                821                 :                : 
                                822                 :                :         /*
                                823                 :                :          * NULL elements are already rejected by statatt_build_stavalues() and
                                824                 :                :          * array_in_safe().
                                825                 :                :          */
                                826                 :            390 :         range_deserialize(typcache, DatumGetRangeTypeP(elems[i]),
                                827                 :                :                           &lower, &upper, &empty);
                                828                 :                : 
                                829         [ +  + ]:            390 :         if (empty)
                                830                 :                :         {
                                831         [ +  - ]:              8 :             ereport(WARNING,
                                832                 :                :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                833                 :                :                      errmsg("\"%s\" must not contain empty ranges",
                                834                 :                :                             "range_bounds_histogram")));
                                835                 :             16 :             return false;
                                836                 :                :         }
                                837                 :                : 
                                838   [ +  +  +  + ]:            704 :         if (i > 0 &&
                                839         [ -  + ]:            636 :             (range_cmp_bounds(typcache, &lower, &prev_lower) < 0 ||
                                840                 :            314 :              range_cmp_bounds(typcache, &upper, &prev_upper) < 0))
                                841                 :                :         {
                                842         [ +  - ]:              8 :             ereport(WARNING,
                                843                 :                :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                844                 :                :                      errmsg("\"%s\" must have its lower and upper bounds sorted in ascending order",
                                845                 :                :                             "range_bounds_histogram")));
                                846                 :              8 :             return false;
                                847                 :                :         }
                                848                 :                : 
                                849                 :            374 :         prev_lower = lower;
                                850                 :            374 :         prev_upper = upper;
                                851                 :                :     }
                                852                 :                : 
                                853                 :             52 :     return true;
                                854                 :                : }
        

Generated by: LCOV version 2.0-1