LCOV - code coverage report
Current view: top level - src/backend/statistics - relation_stats.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 97 110 88.2 %
Date: 2025-01-18 04:15:08 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  * relation_stats.c
       3             :  *
       4             :  *    PostgreSQL relation statistics manipulation
       5             :  *
       6             :  * Code supporting the direct import of relation statistics, similar to
       7             :  * what is done by the ANALYZE command.
       8             :  *
       9             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
      10             :  * Portions Copyright (c) 1994, Regents of the University of California
      11             :  *
      12             :  * IDENTIFICATION
      13             :  *       src/backend/statistics/relation_stats.c
      14             :  *
      15             :  *-------------------------------------------------------------------------
      16             :  */
      17             : 
      18             : #include "postgres.h"
      19             : 
      20             : #include "access/heapam.h"
      21             : #include "catalog/indexing.h"
      22             : #include "statistics/stat_utils.h"
      23             : #include "utils/fmgroids.h"
      24             : #include "utils/fmgrprotos.h"
      25             : #include "utils/syscache.h"
      26             : 
      27             : #define DEFAULT_RELPAGES Int32GetDatum(0)
      28             : #define DEFAULT_RELTUPLES Float4GetDatum(-1.0)
      29             : #define DEFAULT_RELALLVISIBLE Int32GetDatum(0)
      30             : 
      31             : /*
      32             :  * Positional argument numbers, names, and types for
      33             :  * relation_statistics_update().
      34             :  */
      35             : 
      36             : enum relation_stats_argnum
      37             : {
      38             :     RELATION_ARG = 0,
      39             :     RELPAGES_ARG,
      40             :     RELTUPLES_ARG,
      41             :     RELALLVISIBLE_ARG,
      42             :     NUM_RELATION_STATS_ARGS
      43             : };
      44             : 
      45             : static struct StatsArgInfo relarginfo[] =
      46             : {
      47             :     [RELATION_ARG] = {"relation", REGCLASSOID},
      48             :     [RELPAGES_ARG] = {"relpages", INT4OID},
      49             :     [RELTUPLES_ARG] = {"reltuples", FLOAT4OID},
      50             :     [RELALLVISIBLE_ARG] = {"relallvisible", INT4OID},
      51             :     [NUM_RELATION_STATS_ARGS] = {0}
      52             : };
      53             : 
      54             : static bool relation_statistics_update(FunctionCallInfo fcinfo, int elevel,
      55             :                                        bool inplace);
      56             : 
      57             : /*
      58             :  * Internal function for modifying statistics for a relation.
      59             :  */
      60             : static bool
      61         126 : relation_statistics_update(FunctionCallInfo fcinfo, int elevel, bool inplace)
      62             : {
      63             :     Oid         reloid;
      64             :     Relation    crel;
      65         126 :     int32       relpages = DEFAULT_RELPAGES;
      66         126 :     bool        update_relpages = false;
      67         126 :     float       reltuples = DEFAULT_RELTUPLES;
      68         126 :     bool        update_reltuples = false;
      69         126 :     int32       relallvisible = DEFAULT_RELALLVISIBLE;
      70         126 :     bool        update_relallvisible = false;
      71         126 :     bool        result = true;
      72             : 
      73         126 :     if (!PG_ARGISNULL(RELPAGES_ARG))
      74             :     {
      75          96 :         relpages = PG_GETARG_INT32(RELPAGES_ARG);
      76             : 
      77             :         /*
      78             :          * Partitioned tables may have relpages=-1. Note: for relations with
      79             :          * no storage, relpages=-1 is not used consistently, but must be
      80             :          * supported here.
      81             :          */
      82          96 :         if (relpages < -1)
      83             :         {
      84           0 :             ereport(elevel,
      85             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
      86             :                      errmsg("relpages cannot be < -1")));
      87           0 :             result = false;
      88             :         }
      89             :         else
      90          96 :             update_relpages = true;
      91             :     }
      92             : 
      93         126 :     if (!PG_ARGISNULL(RELTUPLES_ARG))
      94             :     {
      95          90 :         reltuples = PG_GETARG_FLOAT4(RELTUPLES_ARG);
      96             : 
      97          90 :         if (reltuples < -1.0)
      98             :         {
      99           0 :             ereport(elevel,
     100             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     101             :                      errmsg("reltuples cannot be < -1.0")));
     102           0 :             result = false;
     103             :         }
     104             :         else
     105          90 :             update_reltuples = true;
     106             :     }
     107             : 
     108         126 :     if (!PG_ARGISNULL(RELALLVISIBLE_ARG))
     109             :     {
     110          84 :         relallvisible = PG_GETARG_INT32(RELALLVISIBLE_ARG);
     111             : 
     112          84 :         if (relallvisible < 0)
     113             :         {
     114           0 :             ereport(elevel,
     115             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     116             :                      errmsg("relallvisible cannot be < 0")));
     117           0 :             result = false;
     118             :         }
     119             :         else
     120          84 :             update_relallvisible = true;
     121             :     }
     122             : 
     123         126 :     stats_check_required_arg(fcinfo, relarginfo, RELATION_ARG);
     124         126 :     reloid = PG_GETARG_OID(RELATION_ARG);
     125             : 
     126         126 :     if (RecoveryInProgress())
     127           0 :         ereport(ERROR,
     128             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     129             :                  errmsg("recovery is in progress"),
     130             :                  errhint("Statistics cannot be modified during recovery.")));
     131             : 
     132         126 :     stats_lock_check_privileges(reloid);
     133             : 
     134             :     /*
     135             :      * Take RowExclusiveLock on pg_class, consistent with
     136             :      * vac_update_relstats().
     137             :      */
     138          96 :     crel = table_open(RelationRelationId, RowExclusiveLock);
     139             : 
     140          96 :     if (inplace)
     141             :     {
     142          36 :         HeapTuple   ctup = NULL;
     143             :         ScanKeyData key[1];
     144             :         Form_pg_class pgcform;
     145          36 :         void       *inplace_state = NULL;
     146          36 :         bool        dirty = false;
     147             : 
     148          36 :         ScanKeyInit(&key[0], Anum_pg_class_oid, BTEqualStrategyNumber, F_OIDEQ,
     149             :                     ObjectIdGetDatum(reloid));
     150          36 :         systable_inplace_update_begin(crel, ClassOidIndexId, true, NULL, 1, key,
     151             :                                       &ctup, &inplace_state);
     152          36 :         if (!HeapTupleIsValid(ctup))
     153           0 :             elog(ERROR, "pg_class entry for relid %u vanished while updating statistics",
     154             :                  reloid);
     155          36 :         pgcform = (Form_pg_class) GETSTRUCT(ctup);
     156             : 
     157          36 :         if (update_relpages && pgcform->relpages != relpages)
     158             :         {
     159          18 :             pgcform->relpages = relpages;
     160          18 :             dirty = true;
     161             :         }
     162          36 :         if (update_reltuples && pgcform->reltuples != reltuples)
     163             :         {
     164          18 :             pgcform->reltuples = reltuples;
     165          18 :             dirty = true;
     166             :         }
     167          36 :         if (update_relallvisible && pgcform->relallvisible != relallvisible)
     168             :         {
     169          18 :             pgcform->relallvisible = relallvisible;
     170          18 :             dirty = true;
     171             :         }
     172             : 
     173          36 :         if (dirty)
     174          36 :             systable_inplace_update_finish(inplace_state, ctup);
     175             :         else
     176           0 :             systable_inplace_update_cancel(inplace_state);
     177             : 
     178          36 :         heap_freetuple(ctup);
     179             :     }
     180             :     else
     181             :     {
     182          60 :         TupleDesc   tupdesc = RelationGetDescr(crel);
     183             :         HeapTuple   ctup;
     184             :         Form_pg_class pgcform;
     185          60 :         int         replaces[3] = {0};
     186          60 :         Datum       values[3] = {0};
     187          60 :         bool        nulls[3] = {0};
     188          60 :         int         nreplaces = 0;
     189             : 
     190          60 :         ctup = SearchSysCache1(RELOID, ObjectIdGetDatum(reloid));
     191          60 :         if (!HeapTupleIsValid(ctup))
     192             :         {
     193           0 :             ereport(elevel,
     194             :                     (errcode(ERRCODE_OBJECT_IN_USE),
     195             :                      errmsg("pg_class entry for relid %u not found", reloid)));
     196           0 :             table_close(crel, RowExclusiveLock);
     197           0 :             return false;
     198             :         }
     199          60 :         pgcform = (Form_pg_class) GETSTRUCT(ctup);
     200             : 
     201          60 :         if (update_relpages && relpages != pgcform->relpages)
     202             :         {
     203          36 :             replaces[nreplaces] = Anum_pg_class_relpages;
     204          36 :             values[nreplaces] = Int32GetDatum(relpages);
     205          36 :             nreplaces++;
     206             :         }
     207             : 
     208          60 :         if (update_reltuples && reltuples != pgcform->reltuples)
     209             :         {
     210          30 :             replaces[nreplaces] = Anum_pg_class_reltuples;
     211          30 :             values[nreplaces] = Float4GetDatum(reltuples);
     212          30 :             nreplaces++;
     213             :         }
     214             : 
     215          60 :         if (update_relallvisible && relallvisible != pgcform->relallvisible)
     216             :         {
     217          30 :             replaces[nreplaces] = Anum_pg_class_relallvisible;
     218          30 :             values[nreplaces] = Int32GetDatum(relallvisible);
     219          30 :             nreplaces++;
     220             :         }
     221             : 
     222          60 :         if (nreplaces > 0)
     223             :         {
     224             :             HeapTuple   newtup;
     225             : 
     226          48 :             newtup = heap_modify_tuple_by_cols(ctup, tupdesc, nreplaces,
     227             :                                                replaces, values, nulls);
     228          48 :             CatalogTupleUpdate(crel, &newtup->t_self, newtup);
     229          48 :             heap_freetuple(newtup);
     230             :         }
     231             : 
     232          60 :         ReleaseSysCache(ctup);
     233             :     }
     234             : 
     235             :     /* release the lock, consistent with vac_update_relstats() */
     236          96 :     table_close(crel, RowExclusiveLock);
     237             : 
     238          96 :     CommandCounterIncrement();
     239             : 
     240          96 :     return result;
     241             : }
     242             : 
     243             : /*
     244             :  * Set statistics for a given pg_class entry.
     245             :  */
     246             : Datum
     247          54 : pg_set_relation_stats(PG_FUNCTION_ARGS)
     248             : {
     249          54 :     relation_statistics_update(fcinfo, ERROR, false);
     250          48 :     PG_RETURN_VOID();
     251             : }
     252             : 
     253             : /*
     254             :  * Clear statistics for a given pg_class entry; that is, set back to initial
     255             :  * stats for a newly-created table.
     256             :  */
     257             : Datum
     258          24 : pg_clear_relation_stats(PG_FUNCTION_ARGS)
     259             : {
     260          24 :     LOCAL_FCINFO(newfcinfo, 4);
     261             : 
     262          24 :     InitFunctionCallInfoData(*newfcinfo, NULL, 4, InvalidOid, NULL, NULL);
     263             : 
     264          24 :     newfcinfo->args[0].value = PG_GETARG_OID(0);
     265          24 :     newfcinfo->args[0].isnull = PG_ARGISNULL(0);
     266          24 :     newfcinfo->args[1].value = DEFAULT_RELPAGES;
     267          24 :     newfcinfo->args[1].isnull = false;
     268          24 :     newfcinfo->args[2].value = DEFAULT_RELTUPLES;
     269          24 :     newfcinfo->args[2].isnull = false;
     270          24 :     newfcinfo->args[3].value = DEFAULT_RELALLVISIBLE;
     271          24 :     newfcinfo->args[3].isnull = false;
     272             : 
     273          24 :     relation_statistics_update(newfcinfo, ERROR, false);
     274          12 :     PG_RETURN_VOID();
     275             : }
     276             : 
     277             : Datum
     278          66 : pg_restore_relation_stats(PG_FUNCTION_ARGS)
     279             : {
     280          66 :     LOCAL_FCINFO(positional_fcinfo, NUM_RELATION_STATS_ARGS);
     281          66 :     bool        result = true;
     282             : 
     283          66 :     InitFunctionCallInfoData(*positional_fcinfo, NULL,
     284             :                              NUM_RELATION_STATS_ARGS,
     285             :                              InvalidOid, NULL, NULL);
     286             : 
     287          66 :     if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo,
     288             :                                           relarginfo, WARNING))
     289          12 :         result = false;
     290             : 
     291          48 :     if (!relation_statistics_update(positional_fcinfo, WARNING, true))
     292           0 :         result = false;
     293             : 
     294          36 :     PG_RETURN_BOOL(result);
     295             : }

Generated by: LCOV version 1.14