{"record":{"id":"c3b79743f0cda15a","repo":"ankane/pghero","slug":"historical-query-stats-not-enabled","errorCode":null,"errorMessage":"Historical query stats not enabled","messagePattern":"Historical query stats not enabled","errorType":"exception","errorClass":"PgHero::NotEnabled","httpStatus":null,"severity":"warning","filePath":"lib/pghero/methods/query_stats.rb","lineNumber":265,"sourceCode":"            SELECT NULL, #{\"NULL, \" if origin}NULL, NULL, SUM(total_time), NULL FROM query_stats\n          )\n        SQL\n\n        binds = {limit: limit.to_i}\n        binds[:user] = user if user\n        binds[:query_hash] = query_hash if query_hash\n\n        # we may be able to skip query_columns\n        # in more recent versions of Postgres\n        # as pg_stat_statements should be already normalized\n        result = select_all(query, binds, query_columns: [:query])\n        total = result.pop\n        [result, total[:total_time] || 0]\n      end\n\n      def historical_query_stats(limit: nil, sort: nil, user: nil, query_hash: nil, start_at: nil, end_at: nil)\n        if !historical_query_stats_enabled?\n          raise NotEnabled, \"Historical query stats not enabled\"\n        end\n\n        limit ||= 100\n        sort ||= \"total_time\"\n        query = <<~SQL\n          WITH query_stats AS (\n            SELECT\n              query_hash,\n              \"user\",\n              query_id,\n              SUM(total_time) AS total_time,\n              SUM(calls) AS calls\n            FROM\n              pghero_query_stats\n            WHERE\n              database = :id\n              #{\"AND captured_at >= :start_at\" if start_at}\n              #{\"AND captured_at <= :end_at\" if end_at}","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/ankane/pghero/blob/7edb57986ffd36f9d64f0830c4ccc90a5eac46d6/lib/pghero/methods/query_stats.rb#L247-L283","documentation":"PgHero raises this from PgHero::Methods::QueryStats#historical_query_stats when historical_query_stats_enabled? is false. Historical (time-ranged) query stats are an opt-in layer on top of live pg_stat_statements: they require the pghero_queries and pghero_query_stats capture tables to exist AND capture_query_stats not set to false for the database in config/pghero.yml. Without both, PgHero has no stored snapshots to aggregate with SUM(total_time)/SUM(calls) and refuses rather than returning empty data.","triggerScenarios":"Calling PgHero.historical_query_stats(start_at: ..., end_at: ...) (or the web UI queries page with a historical time range) before `rails generate pghero:query_stats && rails db:migrate` has created the capture tables; or when config/pghero.yml sets capture_query_stats: false for that database; or on an environment where the migration ran but the config disables capture.","commonSituations":"Fresh PgHero install where the optional migration was skipped; migration applied in development but not in staging/production; the hourly `rake pghero:capture_query_stats` cron was never scheduled so nobody noticed the feature was inactive; pghero.yml copied from another environment with capture_query_stats: false.","solutions":["Run `rails generate pghero:query_stats` followed by `rails db:migrate` to create pghero_queries and pghero_query_stats.","Ensure config/pghero.yml does not set capture_query_stats: false for the database in question (removing the key defaults to enabled).","Schedule `rake pghero:capture_query_stats` at least hourly (cron, whenever, sidekiq-cron) so snapshots accumulate before you query history.","If the feature is intentionally off, guard callers with `PgHero.historical_query_stats_enabled?` or rescue PgHero::NotEnabled and fall back to `PgHero.query_stats` (live pg_stat_statements data)."],"exampleFix":"# before - raises PgHero::NotEnabled: Historical query stats not enabled\nPgHero.historical_query_stats(start_at: 7.days.ago, end_at: 1.hour.ago)\n\n# after\n# terminal:\n#   rails generate pghero:query_stats\n#   rails db:migrate\n# config/schedule.rb:\n#   every :hour do\n#     rake \"pghero:capture_query_stats\"\n#   end\nstats = PgHero.historical_query_stats(start_at: 7.days.ago, end_at: 1.hour.ago) if PgHero.historical_query_stats_enabled?","handlingStrategy":"validation","validationCode":"return unless PgHero.historical_query_stats_enabled?\nstats = PgHero.historical_query_stats(start_at: 7.days.ago, end_at: 1.hour.ago)","typeGuard":"# Ruby predicate - the closest thing to a type guard\ndef historical_stats_available?\n  PgHero.historical_query_stats_enabled?\nend","tryCatchPattern":"begin\n  PgHero.historical_query_stats(start_at: 24.hours.ago)\nrescue PgHero::NotEnabled\n  Rails.logger.info(\"PgHero historical query stats not configured; using live stats\")\n  PgHero.query_stats\nend","preventionTips":["Run the pghero:query_stats generator and migration as part of setup, not later.","Schedule `rake pghero:capture_query_stats` hourly via cron/whenever from day one.","Check historical_query_stats_enabled? in a boot health check for environments where history matters.","Keep capture_query_stats unset (defaults on) unless you intentionally disable it per database."],"tags":["pghero","postgresql","rails","query-stats","configuration","not-enabled"],"backgroundTag":"optional-feature-not-enabled","analyzedSha":"7edb57986ffd36f9d64f0830c4ccc90a5eac46d6","analyzedAt":"2026-08-21T17:33:54.942Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}