diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index cd66abc..8231d28 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -5552,6 +5552,21 @@ COPY postgres_log FROM '/full/path/to/logfile.csv' WITH csv; + + track_sql (boolean) + + track_sql configuration parameter + + + + + Enables collection of different SQL statement statistics that are + executed on the instance. This parameter is off by default. Only + superusers can change this setting. + + + + stats_temp_directory (string) diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index 0776428..ff0cb6d 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -513,6 +513,13 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser VACUUM, showing current progress. See . + + + pg_stat_sqlpg_stat_sql + One row only, showing statistics about the SQL statements that are + executed on the instance. + See for details. + @@ -2222,6 +2229,150 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i controls exactly which functions are tracked. + + <structname>pg_stat_sql</structname> View + + + + Column + Type + Description + + + + + + insert + bigint + Number of insert SQL statements executed + + + delete + bigint + Number of delete SQL statements executed + + + update + bigint + Number of update SQL statements executed + + + select + bigint + Number of select SQL statements executed + + + declar_cursor + bigint + Number of declare_cursor SQL statements executed + + + close + bigint + Number of close SQL statements executed + + + create + bigint + Number of create SQL statements executed + + + drop + bigint + Number of drop SQL statements executed + + + alter + bigint + Number of alter SQL statements executed + + + import + bigint + Number of import SQL statements executed + + + truncate + bigint + Number of truncate SQL statements executed + + + copy + bigint + Number of copy SQL statements executed + + + grant + bigint + Number of grant SQL statements executed + + + revoke + bigint + Number of revoke SQL statements executed + + + cluster + bigint + Number of cluster SQL statements executed + + + vacuum + bigint + Number of vacuum SQL statements executed + + + analyze + bigint + Number of analyze SQL statements executed + + + refresh + bigint + Number of refresh SQL statements executed + + + lock + bigint + Number of lock SQL statements executed + + + checkpoint + bigint + Number of checkpoint SQL statements executed + + + reindex + bigint + Number of reindex SQL statements executed + + + deallocate + bigint + Number of deallocate SQL statements executed + + + others + bigint + Number of others SQL statements executed + + + stats_reset + timestamp with time zone + Time at which these statistics were last reset + + + +
+ + + The pg_stat_sql view will contain only + one row, showing statistics about number of SQL statements that are + executed on the instance. The parameter + controls the SQL statement execution statistics. + + + diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index ada2142..0e39920 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -842,6 +842,35 @@ CREATE VIEW pg_replication_origin_status AS REVOKE ALL ON pg_replication_origin_status FROM public; +CREATE VIEW pg_stat_sql AS + SELECT + pg_stat_get_insert_sqlstmt() AS insert, + pg_stat_get_delete_sqlstmt() AS delete, + pg_stat_get_update_sqlstmt() AS update, + pg_stat_get_select_sqlstmt() AS select, + pg_stat_get_declare_cursor_sqlstmt() AS declare_cursor, + pg_stat_get_close_sqlstmt() AS close, + pg_stat_get_create_sqlstmt() AS create, + pg_stat_get_drop_sqlstmt() AS drop, + pg_stat_get_alter_sqlstmt() AS alter, + pg_stat_get_import_sqlstmt() AS import, + pg_stat_get_truncate_sqlstmt() AS truncate, + pg_stat_get_copy_sqlstmt() AS copy, + pg_stat_get_grant_sqlstmt() AS grant, + pg_stat_get_revoke_sqlstmt() AS revoke, + pg_stat_get_cluster_sqlstmt() AS cluster, + pg_stat_get_vacuum_sqlstmt() AS vacuum, + pg_stat_get_analyze_sqlstmt() AS analyze, + pg_stat_get_refresh_sqlstmt() AS refresh, + pg_stat_get_lock_sqlstmt() AS lock, + pg_stat_get_checkpoint_sqlstmt() AS checkpoint, + pg_stat_get_reindex_sqlstmt() AS reindex, + pg_stat_get_deallocate_sqlstmt() AS deallocate, + pg_stat_get_others_sqlstmt() AS others, + pgx_stat_get_reset_time_sqlstmt() AS stats_reset; + +GRANT SELECT ON pg_stat_sql TO PUBLIC; + -- -- We have a few function definitions in here, too. -- At some point there might be enough to justify breaking them out into diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 8a2ce91..4cc5533 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -105,6 +105,7 @@ */ bool pgstat_track_activities = false; bool pgstat_track_counts = false; +bool pgstat_track_sql = false; int pgstat_track_functions = TRACK_FUNC_OFF; int pgstat_track_activity_query_size = 1024; @@ -123,6 +124,13 @@ char *pgstat_stat_tmpname = NULL; */ PgStat_MsgBgWriter BgWriterStats; +/* + * SQL statistics counter. + * This counter is incremented by each SQL Statement, + * And then, sent to the stat collector process. + */ +PgStat_MsgSqlstmt SqlstmtStatsLocal; + /* ---------- * Local data * ---------- @@ -219,6 +227,7 @@ static int localNumBackends = 0; */ static PgStat_ArchiverStats archiverStats; static PgStat_GlobalStats globalStats; +static PgStat_SqlStmtStats sqlstmtStats; /* * List of OIDs of databases we need to write out. If an entry is InvalidOid, @@ -267,6 +276,7 @@ static bool pgstat_db_requested(Oid databaseid); static void pgstat_send_tabstat(PgStat_MsgTabstat *tsmsg); static void pgstat_send_funcstats(void); +static void pgstat_send_sqlstmt(void); static HTAB *pgstat_collect_oids(Oid catalogid); static PgStat_TableStatus *get_tabstat_entry(Oid rel_id, bool isshared); @@ -293,6 +303,7 @@ static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len); static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len); static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len); static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len); +static void pgstat_recv_sqlstmt(PgStat_MsgSqlstmt * msg, int len); /* ------------------------------------------------------------ * Public functions called from postmaster follow @@ -826,6 +837,10 @@ pgstat_report_stat(bool force) /* Now, send function statistics */ pgstat_send_funcstats(); + + /* Now, send sql statistics */ + if (pgstat_track_sql) + pgstat_send_sqlstmt(); } /* @@ -1252,11 +1267,13 @@ pgstat_reset_shared_counters(const char *target) msg.m_resettarget = RESET_ARCHIVER; else if (strcmp(target, "bgwriter") == 0) msg.m_resettarget = RESET_BGWRITER; + else if (strcmp(target, "sqlstmt") == 0) + msg.m_resettarget = RESET_SQLSTMT; else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized reset target: \"%s\"", target), - errhint("Target must be \"archiver\" or \"bgwriter\"."))); + errhint("Target must be \"archiver\" or \"bgwriter\" or \"sqlstmt\"."))); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER); pgstat_send(&msg, sizeof(msg)); @@ -2465,6 +2482,21 @@ pgstat_fetch_global(void) return &globalStats; } +/* + * --------- + * pgstat_fetch_sqlstmt() - + * + * Support function for the SQL-callable pgstat* functions. Returns + * a pointer to the sql statement statistics struct. + * --------- + */ +PgStat_SqlStmtStats * +pgstat_fetch_sqlstmt(void) +{ + backend_read_statsfile(); + + return &sqlstmtStats; +} /* ------------------------------------------------------------ * Functions for management of the shared-memory PgBackendStatus array @@ -3456,6 +3488,37 @@ pgstat_send_bgwriter(void) MemSet(&BgWriterStats, 0, sizeof(BgWriterStats)); } +/* ---------- + * pgstat_send_sqlstmt(void) + * + * Send SQL statement statistics to the collector + * ---------- + */ +static void +pgstat_send_sqlstmt(void) +{ + /* We assume this initializes to zeroes */ + static const PgStat_MsgSqlstmt all_zeroes; + + /* + * This function can be called even if nothing at all has happened. In + * this case, avoid sending a completely empty message to the stats + * collector. + */ + if (memcmp(&SqlstmtStatsLocal, &all_zeroes, sizeof(PgStat_MsgSqlstmt)) == 0) + return; + + /* + * Prepare and send the message + */ + pgstat_setheader(&SqlstmtStatsLocal.m_hdr, PGSTAT_MTYPE_SQLSTMT); + pgstat_send(&SqlstmtStatsLocal, sizeof(SqlstmtStatsLocal)); + + /* + * Clear out the statistics buffer, so it can be re-used. + */ + MemSet(&SqlstmtStatsLocal, 0, sizeof(SqlstmtStatsLocal)); +} /* ---------- * PgstatCollectorMain() - @@ -3672,6 +3735,9 @@ PgstatCollectorMain(int argc, char *argv[]) pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len); break; + case PGSTAT_MTYPE_SQLSTMT: + pgstat_recv_sqlstmt((PgStat_MsgSqlstmt *) & msg, len); + break; default: break; } @@ -3937,6 +4003,12 @@ pgstat_write_statsfiles(bool permanent, bool allDbs) (void) rc; /* we'll check for error with ferror */ /* + * Write Sql statement stats struct + */ + rc = fwrite(&sqlstmtStats, sizeof(PgStat_SqlStmtStats), 1, fpout); + (void) rc; /* we'll check for error with ferror */ + + /* * Walk through the database table. */ hash_seq_init(&hstat, pgStatDBHash); @@ -4193,6 +4265,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep) */ memset(&globalStats, 0, sizeof(globalStats)); memset(&archiverStats, 0, sizeof(archiverStats)); + memset(&sqlstmtStats, 0, sizeof(sqlstmtStats)); /* * Set the current timestamp (will be kept only in case we can't load an @@ -4200,6 +4273,7 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep) */ globalStats.stat_reset_timestamp = GetCurrentTimestamp(); archiverStats.stat_reset_timestamp = globalStats.stat_reset_timestamp; + sqlstmtStats.stat_reset_timestamp = globalStats.stat_reset_timestamp; /* * Try to open the stats file. If it doesn't exist, the backends simply @@ -4252,6 +4326,16 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep) } /* + * Read Sql statement stats struct + */ + if (fread(&sqlstmtStats, 1, sizeof(sqlstmtStats), fpin) != sizeof(sqlstmtStats)) + { + ereport(pgStatRunningInCollector ? LOG : WARNING, + (errmsg("corrupted statistics file \"%s\"", statfile))); + goto done; + } + + /* * We found an existing collector stats file. Read it and put all the * hashtable entries into place. */ @@ -4540,6 +4624,7 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent, PgStat_StatDBEntry dbentry; PgStat_GlobalStats myGlobalStats; PgStat_ArchiverStats myArchiverStats; + PgStat_SqlStmtStats mySqlstmtStats; FILE *fpin; int32 format_id; const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename; @@ -4594,6 +4679,18 @@ pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent, return false; } + /* + * Read sql statement stats struct + */ + if (fread(&mySqlstmtStats, 1, sizeof(mySqlstmtStats), + fpin) != sizeof(mySqlstmtStats)) + { + ereport(pgStatRunningInCollector ? LOG : WARNING, + (errmsg("corrupted statistics file \"%s\"", statfile))); + FreeFile(fpin); + return false; + } + /* By default, we're going to return the timestamp of the global file. */ *ts = myGlobalStats.stats_timestamp; @@ -5156,6 +5253,12 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len) memset(&archiverStats, 0, sizeof(archiverStats)); archiverStats.stat_reset_timestamp = GetCurrentTimestamp(); } + else if (msg->m_resettarget == RESET_SQLSTMT) + { + /* Reset the archiver statistics for the cluster. */ + memset(&sqlstmtStats, 0, sizeof(sqlstmtStats)); + sqlstmtStats.stat_reset_timestamp = GetCurrentTimestamp(); + } /* * Presumably the sender of this message validated the target, don't @@ -5336,6 +5439,40 @@ pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len) } /* ---------- + * pgstat_recv_sqlstmt() - + * + * Process a SQL statement statistics message. + * ---------- + */ +static void +pgstat_recv_sqlstmt(PgStat_MsgSqlstmt * msg, int len) +{ + sqlstmtStats.n_insert_sqlstmt += msg->n_insert_sqlstmt; + sqlstmtStats.n_delete_sqlstmt += msg->n_delete_sqlstmt; + sqlstmtStats.n_update_sqlstmt += msg->n_update_sqlstmt; + sqlstmtStats.n_select_sqlstmt += msg->n_select_sqlstmt; + sqlstmtStats.n_declare_cursor_sqlstmt += msg->n_declare_cursor_sqlstmt; + sqlstmtStats.n_close_sqlstmt += msg->n_close_sqlstmt; + sqlstmtStats.n_create_sqlstmt += msg->n_create_sqlstmt; + sqlstmtStats.n_drop_sqlstmt += msg->n_drop_sqlstmt; + sqlstmtStats.n_alter_sqlstmt += msg->n_alter_sqlstmt; + sqlstmtStats.n_import_sqlstmt += msg->n_import_sqlstmt; + sqlstmtStats.n_truncate_sqlstmt += msg->n_truncate_sqlstmt; + sqlstmtStats.n_copy_sqlstmt += msg->n_copy_sqlstmt; + sqlstmtStats.n_grant_sqlstmt += msg->n_grant_sqlstmt; + sqlstmtStats.n_revoke_sqlstmt += msg->n_revoke_sqlstmt; + sqlstmtStats.n_cluster_sqlstmt += msg->n_cluster_sqlstmt; + sqlstmtStats.n_vacuum_sqlstmt += msg->n_vacuum_sqlstmt; + sqlstmtStats.n_analyze_sqlstmt += msg->n_analyze_sqlstmt; + sqlstmtStats.n_refresh_sqlstmt += msg->n_refresh_sqlstmt; + sqlstmtStats.n_lock_sqlstmt += msg->n_lock_sqlstmt; + sqlstmtStats.n_checkpoint_sqlstmt += msg->n_checkpoint_sqlstmt; + sqlstmtStats.n_reindex_sqlstmt += msg->n_reindex_sqlstmt; + sqlstmtStats.n_deallocate_sqlstmt += msg->n_deallocate_sqlstmt; + sqlstmtStats.n_others_sqlstmt += msg->n_others_sqlstmt; +} + +/* ---------- * pgstat_recv_recoveryconflict() - * * Process a RECOVERYCONFLICT message. diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 98ccbbb..620a6c9 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -41,6 +41,7 @@ #include "access/xact.h" #include "catalog/pg_type.h" #include "commands/async.h" +#include "commands/defrem.h" #include "commands/prepare.h" #include "libpq/libpq.h" #include "libpq/pqformat.h" @@ -188,7 +189,7 @@ static bool IsTransactionStmtList(List *parseTrees); static void drop_unnamed_stmt(void); static void SigHupHandler(SIGNAL_ARGS); static void log_disconnections(int code, Datum arg); - +static void CountSQLStmtExecutions(Node *parsetree); /* ---------------------------------------------------------------- * routines to obtain user input @@ -1102,6 +1103,12 @@ exec_simple_query(const char *query_string) PortalDrop(portal, false); + /* + * Count SQL statement for pg_stat_sql view + */ + if (pgstat_track_sql) + CountSQLStmtExecutions(parsetree); + if (IsA(parsetree, TransactionStmt)) { /* @@ -1983,6 +1990,29 @@ exec_execute_message(const char *portal_name, long max_rows) (*receiver->rDestroy) (receiver); + /* + * Count SQL Statement for pgx_stat_sql + */ + if (pgstat_track_sql) + { + CachedPlanSource *psrc = NULL; + + if (portal->prepStmtName) + { + PreparedStatement *pstmt; + + pstmt = FetchPreparedStatement(portal->prepStmtName, false); + if (pstmt) + psrc = pstmt->plansource; + } + else + psrc = unnamed_stmt_psrc; + + if (psrc && !execute_is_fetch) /* psrc should not be NULL here, just + * for paranoia */ + CountSQLStmtExecutions(psrc->raw_parse_tree); + } + if (completed) { if (is_xact_command) @@ -4506,3 +4536,319 @@ log_disconnections(int code, Datum arg) port->user_name, port->database_name, port->remote_host, port->remote_port[0] ? " port=" : "", port->remote_port))); } + + +/* + * Count SQL statement for pg_stat_sql view + */ +static void +CountSQLStmtExecutions(Node *parsetree) +{ + /* + * If pgstat_track_sql is true, parsetree should not be NULL. For safer, + * Check NULL here. + */ + if (!parsetree) + return; + + switch (nodeTag(parsetree)) + { + /* raw plannable queries */ + case T_InsertStmt: + SqlstmtStatsLocal.n_insert_sqlstmt++; + break; + + case T_DeleteStmt: + SqlstmtStatsLocal.n_delete_sqlstmt++; + break; + + case T_UpdateStmt: + SqlstmtStatsLocal.n_update_sqlstmt++; + break; + + case T_SelectStmt: + SqlstmtStatsLocal.n_select_sqlstmt++; + break; + + case T_DeclareCursorStmt: + SqlstmtStatsLocal.n_declare_cursor_sqlstmt++; + break; + + case T_ClosePortalStmt: + SqlstmtStatsLocal.n_close_sqlstmt++; + break; + + case T_CreateDomainStmt: + case T_CreateSchemaStmt: + case T_CreateStmt: + case T_CreateTableSpaceStmt: + case T_CreateExtensionStmt: + case T_CreateFdwStmt: + case T_CreateForeignServerStmt: + case T_CreateUserMappingStmt: + case T_CreateForeignTableStmt: + case T_DefineStmt: + case T_CompositeTypeStmt: + case T_CreateEnumStmt: + case T_CreateRangeStmt: + case T_ViewStmt: + case T_CreateFunctionStmt: + case T_IndexStmt: + case T_RuleStmt: + case T_CreateSeqStmt: + case T_CreatedbStmt: + case T_CreateTransformStmt: + case T_CreateTrigStmt: + case T_CreateEventTrigStmt: + case T_CreatePLangStmt: + case T_CreateRoleStmt: + case T_CreateConversionStmt: + case T_CreateCastStmt: + case T_CreateOpClassStmt: + case T_CreateOpFamilyStmt: + case T_CreatePolicyStmt: + case T_CreateAmStmt: + SqlstmtStatsLocal.n_create_sqlstmt++; + break; + + case T_DropTableSpaceStmt: + case T_DropUserMappingStmt: + case T_DropStmt: + case T_DropdbStmt: + case T_DropRoleStmt: + case T_DropOwnedStmt: + SqlstmtStatsLocal.n_drop_sqlstmt++; + break; + + case T_AlterTableSpaceOptionsStmt: + case T_AlterExtensionStmt: + case T_AlterExtensionContentsStmt: + case T_AlterFdwStmt: + case T_AlterForeignServerStmt: + case T_AlterUserMappingStmt: + case T_RenameStmt: + case T_AlterObjectDependsStmt: + case T_AlterObjectSchemaStmt: + case T_AlterOwnerStmt: + case T_AlterTableMoveAllStmt: + case T_AlterTableStmt: + case T_AlterDomainStmt: + case T_AlterFunctionStmt: + case T_AlterDefaultPrivilegesStmt: + case T_AlterEnumStmt: + case T_AlterSeqStmt: + case T_AlterDatabaseStmt: + case T_AlterDatabaseSetStmt: + case T_AlterSystemStmt: + case T_AlterEventTrigStmt: + case T_AlterRoleStmt: + case T_AlterRoleSetStmt: + case T_AlterOpFamilyStmt: + case T_AlterOperatorStmt: + case T_AlterTSDictionaryStmt: + case T_AlterTSConfigurationStmt: + case T_AlterPolicyStmt: + SqlstmtStatsLocal.n_alter_sqlstmt++; + break; + + case T_ImportForeignSchemaStmt: + SqlstmtStatsLocal.n_import_sqlstmt++; + break; + + case T_TruncateStmt: + SqlstmtStatsLocal.n_truncate_sqlstmt++; + break; + + case T_CopyStmt: + SqlstmtStatsLocal.n_copy_sqlstmt++; + break; + + case T_GrantStmt: + { + GrantStmt *stmt = (GrantStmt *) parsetree; + + if (stmt->is_grant) + SqlstmtStatsLocal.n_grant_sqlstmt++; + else + SqlstmtStatsLocal.n_revoke_sqlstmt++; + } + break; + + case T_GrantRoleStmt: + { + GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree; + + if (stmt->is_grant) + SqlstmtStatsLocal.n_grant_sqlstmt++; + else + SqlstmtStatsLocal.n_revoke_sqlstmt++; + } + break; + + case T_ClusterStmt: + SqlstmtStatsLocal.n_cluster_sqlstmt++; + break; + + case T_VacuumStmt: + if (((VacuumStmt *) parsetree)->options & VACOPT_VACUUM) + SqlstmtStatsLocal.n_vacuum_sqlstmt++; + else + SqlstmtStatsLocal.n_analyze_sqlstmt++; + break; + + case T_ExplainStmt: + { + ExplainStmt *stmt = (ExplainStmt *) parsetree; + bool analyze = false; + ListCell *lc; + + /* Look through an EXPLAIN ANALYZE to the contained stmt */ + foreach(lc, stmt->options) + { + DefElem *opt = (DefElem *) lfirst(lc); + + if (strcmp(opt->defname, "analyze") == 0) + { + analyze = defGetBoolean(opt); + break; + } + } + if (analyze) + CountSQLStmtExecutions(stmt->query); + } + break; + + case T_CreateTableAsStmt: + switch (((CreateTableAsStmt *) parsetree)->relkind) + { + case OBJECT_TABLE: + if (((CreateTableAsStmt *) parsetree)->is_select_into) + SqlstmtStatsLocal.n_select_sqlstmt++; + else + SqlstmtStatsLocal.n_create_sqlstmt++; + break; + case OBJECT_MATVIEW: + SqlstmtStatsLocal.n_create_sqlstmt++; + break; + default: + SqlstmtStatsLocal.n_others_sqlstmt++; + } + break; + + case T_RefreshMatViewStmt: + SqlstmtStatsLocal.n_refresh_sqlstmt++; + break; + + case T_LockStmt: + SqlstmtStatsLocal.n_lock_sqlstmt++; + break; + + case T_CheckPointStmt: + SqlstmtStatsLocal.n_checkpoint_sqlstmt++; + break; + + case T_ReindexStmt: + SqlstmtStatsLocal.n_reindex_sqlstmt++; + break; + + case T_ExecuteStmt: + { + ExecuteStmt *stmt = (ExecuteStmt *) parsetree; + PreparedStatement *entry; + + /* not our business to raise error */ + entry = FetchPreparedStatement(stmt->name, false); + if (!entry || !entry->plansource) + break; + CountSQLStmtExecutions(entry->plansource->raw_parse_tree); + } + break; + + case T_DeallocateStmt: + SqlstmtStatsLocal.n_deallocate_sqlstmt++; + break; + + /* already-planned queries */ + case T_PlannedStmt: + { + PlannedStmt *stmt = (PlannedStmt *) parsetree; + + switch (stmt->commandType) + { + case CMD_SELECT: + + /* + * We take a little extra care here so that the result + * will be useful for complaints about read-only + * statements + */ + if (stmt->utilityStmt != NULL) + { + Assert(IsA(stmt->utilityStmt, DeclareCursorStmt)); + SqlstmtStatsLocal.n_declare_cursor_sqlstmt++; + } + else + SqlstmtStatsLocal.n_select_sqlstmt++; + break; + case CMD_UPDATE: + SqlstmtStatsLocal.n_update_sqlstmt++; + break; + case CMD_INSERT: + SqlstmtStatsLocal.n_insert_sqlstmt++; + break; + case CMD_DELETE: + SqlstmtStatsLocal.n_delete_sqlstmt++; + break; + default: + SqlstmtStatsLocal.n_others_sqlstmt++; + break; + } + } + break; + + /* parsed-and-rewritten-but-not-planned queries */ + case T_Query: + { + Query *stmt = (Query *) parsetree; + + switch (stmt->commandType) + { + case CMD_SELECT: + + /* + * We take a little extra care here so that the result + * will be useful for complaints about read-only + * statements + */ + if (stmt->utilityStmt != NULL) + { + Assert(IsA(stmt->utilityStmt, DeclareCursorStmt)); + SqlstmtStatsLocal.n_declare_cursor_sqlstmt++; + } + else + SqlstmtStatsLocal.n_select_sqlstmt++; + break; + case CMD_UPDATE: + SqlstmtStatsLocal.n_revoke_sqlstmt++; + break; + case CMD_INSERT: + SqlstmtStatsLocal.n_revoke_sqlstmt++; + break; + case CMD_DELETE: + SqlstmtStatsLocal.n_revoke_sqlstmt++; + break; + case CMD_UTILITY: + CountSQLStmtExecutions(stmt->utilityStmt); + break; + default: + SqlstmtStatsLocal.n_others_sqlstmt++; + break; + } + } + break; + + default: + SqlstmtStatsLocal.n_others_sqlstmt++; + break; + } +} diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 2d3cf9e..bf68b33 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -129,6 +129,31 @@ extern Datum pg_stat_reset_shared(PG_FUNCTION_ARGS); extern Datum pg_stat_reset_single_table_counters(PG_FUNCTION_ARGS); extern Datum pg_stat_reset_single_function_counters(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_insert_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_delete_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_update_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_select_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_declare_cursor_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_close_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_create_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_drop_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_alter_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_import_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_truncate_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_copy_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_grant_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_revoke_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_cluster_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_vacuum_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_analyze_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_refresh_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_lock_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_checkpoint_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_reindex_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_deallocate_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_others_sqlstmt(PG_FUNCTION_ARGS); +extern Datum pgx_stat_get_reset_time_sqlstmt(PG_FUNCTION_ARGS); + /* Global bgwriter statistics, from bgwriter.c */ extern PgStat_MsgBgWriter bgwriterStats; @@ -1931,3 +1956,147 @@ pg_stat_get_archiver(PG_FUNCTION_ARGS) PG_RETURN_DATUM(HeapTupleGetDatum( heap_form_tuple(tupdesc, values, nulls))); } + +Datum +pg_stat_get_insert_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_insert_sqlstmt); +} + +Datum +pg_stat_get_delete_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_delete_sqlstmt); +} + +Datum +pg_stat_get_update_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_update_sqlstmt); +} + +Datum +pg_stat_get_select_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_select_sqlstmt); +} + +Datum +pg_stat_get_declare_cursor_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_declare_cursor_sqlstmt); +} + +Datum +pg_stat_get_close_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_close_sqlstmt); +} + +Datum +pg_stat_get_create_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_create_sqlstmt); +} + +Datum +pg_stat_get_drop_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_drop_sqlstmt); +} + +Datum +pg_stat_get_alter_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_alter_sqlstmt); +} + +Datum +pg_stat_get_import_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_import_sqlstmt); +} + +Datum +pg_stat_get_truncate_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_truncate_sqlstmt); +} + +Datum +pg_stat_get_copy_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_copy_sqlstmt); +} + +Datum +pg_stat_get_grant_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_grant_sqlstmt); +} + +Datum +pg_stat_get_revoke_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_revoke_sqlstmt); +} + +Datum +pg_stat_get_cluster_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_cluster_sqlstmt); +} + +Datum +pg_stat_get_vacuum_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_vacuum_sqlstmt); +} + +Datum +pg_stat_get_analyze_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_analyze_sqlstmt); +} + +Datum +pg_stat_get_refresh_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_refresh_sqlstmt); +} + +Datum +pg_stat_get_lock_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_lock_sqlstmt); +} + +Datum +pg_stat_get_checkpoint_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_checkpoint_sqlstmt); +} + +Datum +pg_stat_get_reindex_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_reindex_sqlstmt); +} + +Datum +pg_stat_get_deallocate_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_deallocate_sqlstmt); +} + +Datum +pg_stat_get_others_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT64(pgstat_fetch_sqlstmt()->n_others_sqlstmt); +} + +Datum +pgx_stat_get_reset_time_sqlstmt(PG_FUNCTION_ARGS) +{ + PG_RETURN_TIMESTAMPTZ(pgstat_fetch_sqlstmt()->stat_reset_timestamp); +} diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index ce4eef9..154276b 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1205,6 +1205,16 @@ static struct config_bool ConfigureNamesBool[] = }, { + {"track_sql", PGC_SUSET, STATS_COLLECTOR, + gettext_noop("Collects timing statistics for database I/O activity."), + NULL + }, + &pgstat_track_sql, + false, + NULL, NULL, NULL + }, + + { {"update_process_title", PGC_SUSET, PROCESS_TITLE, gettext_noop("Updates the process title to show the active SQL command."), gettext_noop("Enables updating of the process title every time a new SQL command is received by the server.") diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..ea77476 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -2896,6 +2896,57 @@ DESCR("statistics: total execution time of function in current transaction, in m DATA(insert OID = 3048 ( pg_stat_get_xact_function_self_time PGNSP PGUID 12 1 0 0 0 f f f f t f v r 1 0 701 "26" _null_ _null_ _null_ _null_ _null_ pg_stat_get_xact_function_self_time _null_ _null_ _null_ )); DESCR("statistics: self execution time of function in current transaction, in msec"); + +DATA(insert OID = 3401 ( pg_stat_get_insert_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_insert_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of insert sql statement executions"); +DATA(insert OID = 3402 ( pg_stat_get_delete_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_delete_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of delete sql statement executions"); +DATA(insert OID = 3403 ( pg_stat_get_update_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_update_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of update sql statement executions"); +DATA(insert OID = 3404 ( pg_stat_get_select_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_select_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of select sql statement executions"); +DATA(insert OID = 3405 ( pg_stat_get_declare_cursor_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_declare_cursor_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of declare cursor sql statement executions"); +DATA(insert OID = 3406 ( pg_stat_get_close_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_close_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of close sql statement executions"); +DATA(insert OID = 3407 ( pg_stat_get_create_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_create_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of create sql statement executions"); +DATA(insert OID = 3408 ( pg_stat_get_drop_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_drop_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of drop sql statement executions"); +DATA(insert OID = 3409 ( pg_stat_get_alter_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_alter_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of alter sql statement executions"); +DATA(insert OID = 3410 ( pg_stat_get_import_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_import_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of import sql statement executions"); +DATA(insert OID = 3411 ( pg_stat_get_truncate_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_truncate_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of truncate sql statement executions"); +DATA(insert OID = 3412 ( pg_stat_get_copy_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_copy_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of copy sql statement executions"); +DATA(insert OID = 3413 ( pg_stat_get_grant_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_grant_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of grant sql statement executions"); +DATA(insert OID = 3414 ( pg_stat_get_revoke_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_revoke_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of revoke sql statement executions"); +DATA(insert OID = 3415 ( pg_stat_get_cluster_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_cluster_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of cluster sql statement executions"); +DATA(insert OID = 3416 ( pg_stat_get_vacuum_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_vacuum_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of vacuum sql statement executions"); +DATA(insert OID = 3417 ( pg_stat_get_analyze_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_analyze_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of analyze sql statement executions"); +DATA(insert OID = 3418 ( pg_stat_get_refresh_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_refresh_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of refresh sql statement executions"); +DATA(insert OID = 3419 ( pg_stat_get_lock_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_lock_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of lock sql statement executions"); +DATA(insert OID = 3420 ( pg_stat_get_checkpoint_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_checkpoint_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of checkpoint sql statement executions"); +DATA(insert OID = 3421 ( pg_stat_get_reindex_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_reindex_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of reindex sql statement executions"); +DATA(insert OID = 3422 ( pg_stat_get_deallocate_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_deallocate_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of deallocate sql statement executions"); +DATA(insert OID = 3423 ( pg_stat_get_others_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 20 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_others_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of other sql statement executions"); +DATA(insert OID = 3424 ( pgx_stat_get_reset_time_sqlstmt PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 1184 "" _null_ _null_ _null_ _null_ _null_ pgx_stat_get_reset_time_sqlstmt _null_ _null_ _null_ )); +DESCR("statistics: number of other sql statement executions"); + + DATA(insert OID = 3788 ( pg_stat_get_snapshot_timestamp PGNSP PGUID 12 1 0 0 0 f f f f t f s r 0 0 1184 "" _null_ _null_ _null_ _null_ _null_ pg_stat_get_snapshot_timestamp _null_ _null_ _null_ )); DESCR("statistics: timestamp of the current statistics snapshot"); DATA(insert OID = 2230 ( pg_stat_clear_snapshot PGNSP PGUID 12 1 0 0 0 f f f f f f v r 0 0 2278 "" _null_ _null_ _null_ _null_ _null_ pg_stat_clear_snapshot _null_ _null_ _null_ )); diff --git a/src/include/pgstat.h b/src/include/pgstat.h index dc3320d..44c6127 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -64,7 +64,8 @@ typedef enum StatMsgType PGSTAT_MTYPE_FUNCPURGE, PGSTAT_MTYPE_RECOVERYCONFLICT, PGSTAT_MTYPE_TEMPFILE, - PGSTAT_MTYPE_DEADLOCK + PGSTAT_MTYPE_DEADLOCK, + PGSTAT_MTYPE_SQLSTMT } StatMsgType; /* ---------- @@ -119,7 +120,8 @@ typedef struct PgStat_TableCounts typedef enum PgStat_Shared_Reset_Target { RESET_ARCHIVER, - RESET_BGWRITER + RESET_BGWRITER, + RESET_SQLSTMT } PgStat_Shared_Reset_Target; /* Possible object types for resetting single counters */ @@ -530,6 +532,38 @@ typedef struct PgStat_MsgDeadlock Oid m_databaseid; } PgStat_MsgDeadlock; +/* ---------- + * PgStat_MsgSqlstmt Sent by the sql to update statistics. + * ---------- + */ +typedef struct PgStat_MsgSqlstmt +{ + PgStat_MsgHdr m_hdr; + + PgStat_Counter n_insert_sqlstmt; + PgStat_Counter n_delete_sqlstmt; + PgStat_Counter n_update_sqlstmt; + PgStat_Counter n_select_sqlstmt; + PgStat_Counter n_declare_cursor_sqlstmt; + PgStat_Counter n_close_sqlstmt; + PgStat_Counter n_create_sqlstmt; + PgStat_Counter n_drop_sqlstmt; + PgStat_Counter n_alter_sqlstmt; + PgStat_Counter n_import_sqlstmt; + PgStat_Counter n_truncate_sqlstmt; + PgStat_Counter n_copy_sqlstmt; + PgStat_Counter n_grant_sqlstmt; + PgStat_Counter n_revoke_sqlstmt; + PgStat_Counter n_cluster_sqlstmt; + PgStat_Counter n_vacuum_sqlstmt; + PgStat_Counter n_analyze_sqlstmt; + PgStat_Counter n_refresh_sqlstmt; + PgStat_Counter n_lock_sqlstmt; + PgStat_Counter n_checkpoint_sqlstmt; + PgStat_Counter n_reindex_sqlstmt; + PgStat_Counter n_deallocate_sqlstmt; + PgStat_Counter n_others_sqlstmt; +} PgStat_MsgSqlstmt; /* ---------- * PgStat_Msg Union over all possible messages. @@ -555,6 +589,7 @@ typedef union PgStat_Msg PgStat_MsgFuncpurge msg_funcpurge; PgStat_MsgRecoveryConflict msg_recoveryconflict; PgStat_MsgDeadlock msg_deadlock; + PgStat_MsgSqlstmt msg_sqlstmt; } PgStat_Msg; @@ -566,7 +601,7 @@ typedef union PgStat_Msg * ------------------------------------------------------------ */ -#define PGSTAT_FILE_FORMAT_ID 0x01A5BC9D +#define PGSTAT_FILE_FORMAT_ID 0x01A5BC9E /* ---------- * PgStat_StatDBEntry The collector's data per database @@ -675,6 +710,34 @@ typedef struct PgStat_ArchiverStats TimestampTz stat_reset_timestamp; } PgStat_ArchiverStats; +typedef struct PgStat_SqlStmtStats +{ + PgStat_Counter n_insert_sqlstmt; + PgStat_Counter n_delete_sqlstmt; + PgStat_Counter n_update_sqlstmt; + PgStat_Counter n_select_sqlstmt; + PgStat_Counter n_declare_cursor_sqlstmt; + PgStat_Counter n_close_sqlstmt; + PgStat_Counter n_create_sqlstmt; + PgStat_Counter n_drop_sqlstmt; + PgStat_Counter n_alter_sqlstmt; + PgStat_Counter n_import_sqlstmt; + PgStat_Counter n_truncate_sqlstmt; + PgStat_Counter n_copy_sqlstmt; + PgStat_Counter n_grant_sqlstmt; + PgStat_Counter n_revoke_sqlstmt; + PgStat_Counter n_cluster_sqlstmt; + PgStat_Counter n_vacuum_sqlstmt; + PgStat_Counter n_analyze_sqlstmt; + PgStat_Counter n_refresh_sqlstmt; + PgStat_Counter n_lock_sqlstmt; + PgStat_Counter n_checkpoint_sqlstmt; + PgStat_Counter n_reindex_sqlstmt; + PgStat_Counter n_deallocate_sqlstmt; + PgStat_Counter n_others_sqlstmt; + TimestampTz stat_reset_timestamp; +} PgStat_SqlStmtStats; + /* * Global statistics kept in the stats collector */ @@ -918,6 +981,7 @@ typedef struct PgStat_FunctionCallUsage */ extern bool pgstat_track_activities; extern bool pgstat_track_counts; +extern bool pgstat_track_sql; extern int pgstat_track_functions; extern PGDLLIMPORT int pgstat_track_activity_query_size; extern char *pgstat_stat_directory; @@ -930,6 +994,11 @@ extern char *pgstat_stat_filename; extern PgStat_MsgBgWriter BgWriterStats; /* + * SQL statistics counter updated by each backend + */ +extern PgStat_MsgSqlstmt SqlstmtStatsLocal; + +/* * Updated by pgstat_count_buffer_*_time macros */ extern PgStat_Counter pgStatBlockReadTime; @@ -1141,5 +1210,6 @@ extern PgStat_StatFuncEntry *pgstat_fetch_stat_funcentry(Oid funcid); extern int pgstat_fetch_stat_numbackends(void); extern PgStat_ArchiverStats *pgstat_fetch_stat_archiver(void); extern PgStat_GlobalStats *pgstat_fetch_global(void); +extern PgStat_SqlStmtStats *pgstat_fetch_sqlstmt(void); #endif /* PGSTAT_H */ diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 00700f2..b3c728d 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1795,6 +1795,30 @@ pg_stat_replication| SELECT s.pid, FROM ((pg_stat_get_activity(NULL::integer) s(datid, pid, usesysid, application_name, state, query, wait_event_type, wait_event, xact_start, query_start, backend_start, state_change, client_addr, client_hostname, client_port, backend_xid, backend_xmin, ssl, sslversion, sslcipher, sslbits, sslcompression, sslclientdn) JOIN pg_stat_get_wal_senders() w(pid, state, sent_location, write_location, flush_location, replay_location, sync_priority, sync_state) ON ((s.pid = w.pid))) LEFT JOIN pg_authid u ON ((s.usesysid = u.oid))); +pg_stat_sql| SELECT pg_stat_get_insert_sqlstmt() AS insert, + pg_stat_get_delete_sqlstmt() AS delete, + pg_stat_get_update_sqlstmt() AS update, + pg_stat_get_select_sqlstmt() AS "select", + pg_stat_get_declare_cursor_sqlstmt() AS declare_cursor, + pg_stat_get_close_sqlstmt() AS close, + pg_stat_get_create_sqlstmt() AS "create", + pg_stat_get_drop_sqlstmt() AS drop, + pg_stat_get_alter_sqlstmt() AS alter, + pg_stat_get_import_sqlstmt() AS import, + pg_stat_get_truncate_sqlstmt() AS truncate, + pg_stat_get_copy_sqlstmt() AS copy, + pg_stat_get_grant_sqlstmt() AS "grant", + pg_stat_get_revoke_sqlstmt() AS revoke, + pg_stat_get_cluster_sqlstmt() AS cluster, + pg_stat_get_vacuum_sqlstmt() AS vacuum, + pg_stat_get_analyze_sqlstmt() AS "analyze", + pg_stat_get_refresh_sqlstmt() AS refresh, + pg_stat_get_lock_sqlstmt() AS lock, + pg_stat_get_checkpoint_sqlstmt() AS checkpoint, + pg_stat_get_reindex_sqlstmt() AS reindex, + pg_stat_get_deallocate_sqlstmt() AS deallocate, + pg_stat_get_others_sqlstmt() AS others, + pgx_stat_get_reset_time_sqlstmt() AS stats_reset; pg_stat_ssl| SELECT s.pid, s.ssl, s.sslversion AS version, diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out index a811265..3e41de4 100644 --- a/src/test/regress/expected/stats.out +++ b/src/test/regress/expected/stats.out @@ -4,6 +4,55 @@ -- Must be run after tenk2 has been created (by create_table), -- populated (by create_misc) and indexed (by create_index). -- +-- pg_stat_sql +SHOW track_sql; -- must be off + track_sql +----------- + off +(1 row) + +SELECT pg_stat_sql.select, pg_stat_sql.others FROM pg_stat_sql; + select | others +--------+-------- + 0 | 0 +(1 row) + +SET track_sql TO ON; +-- force the rate-limiting logic in pgstat_report_stat() to time out +-- and send a message +SELECT pg_sleep(1.0); + pg_sleep +---------- + +(1 row) + +SELECT pg_stat_sql.select, pg_stat_sql.others FROM pg_stat_sql; + select | others +--------+-------- + 1 | 1 +(1 row) + +SET track_sql TO OFF; +SELECT pg_stat_reset_shared('sqlstmt'); -- reset the counters + pg_stat_reset_shared +---------------------- + +(1 row) + +-- force the rate-limiting logic in pgstat_report_stat() to time out +-- and send a message +SELECT pg_sleep(1.0); + pg_sleep +---------- + +(1 row) + +SELECT pg_stat_sql.select, pg_stat_sql.others FROM pg_stat_sql; + select | others +--------+-------- + 0 | 0 +(1 row) + -- conditio sine qua non SHOW track_counts; -- must be on track_counts diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql index b3e2efa..88e1d5e 100644 --- a/src/test/regress/sql/stats.sql +++ b/src/test/regress/sql/stats.sql @@ -5,6 +5,25 @@ -- populated (by create_misc) and indexed (by create_index). -- +-- pg_stat_sql +SHOW track_sql; -- must be off +SELECT pg_stat_sql.select, pg_stat_sql.others FROM pg_stat_sql; + +SET track_sql TO ON; + +-- force the rate-limiting logic in pgstat_report_stat() to time out +-- and send a message +SELECT pg_sleep(1.0); +SELECT pg_stat_sql.select, pg_stat_sql.others FROM pg_stat_sql; + +SET track_sql TO OFF; +SELECT pg_stat_reset_shared('sqlstmt'); -- reset the counters + +-- force the rate-limiting logic in pgstat_report_stat() to time out +-- and send a message +SELECT pg_sleep(1.0); +SELECT pg_stat_sql.select, pg_stat_sql.others FROM pg_stat_sql; + -- conditio sine qua non SHOW track_counts; -- must be on