github.com/supabase/cli@v1.168.1/internal/inspect/queries.go (about) 1 package inspect 2 3 const BLOAT_QUERY = `WITH constants AS ( 4 SELECT current_setting('block_size')::numeric AS bs, 23 AS hdr, 4 AS ma 5 ), bloat_info AS ( 6 SELECT 7 ma,bs,schemaname,tablename, 8 (datawidth+(hdr+ma-(case when hdr%ma=0 THEN ma ELSE hdr%ma END)))::numeric AS datahdr, 9 (maxfracsum*(nullhdr+ma-(case when nullhdr%ma=0 THEN ma ELSE nullhdr%ma END))) AS nullhdr2 10 FROM ( 11 SELECT 12 schemaname, tablename, hdr, ma, bs, 13 SUM((1-null_frac)*avg_width) AS datawidth, 14 MAX(null_frac) AS maxfracsum, 15 hdr+( 16 SELECT 1+count(*)/8 17 FROM pg_stats s2 18 WHERE null_frac<>0 AND s2.schemaname = s.schemaname AND s2.tablename = s.tablename 19 ) AS nullhdr 20 FROM pg_stats s, constants 21 GROUP BY 1,2,3,4,5 22 ) AS foo 23 ), table_bloat AS ( 24 SELECT 25 schemaname, tablename, cc.relpages, bs, 26 CEIL((cc.reltuples*((datahdr+ma- 27 (CASE WHEN datahdr%ma=0 THEN ma ELSE datahdr%ma END))+nullhdr2+4))/(bs-20::float)) AS otta 28 FROM bloat_info 29 JOIN pg_class cc ON cc.relname = bloat_info.tablename 30 JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname = bloat_info.schemaname AND nn.nspname <> 'information_schema' 31 ), index_bloat AS ( 32 SELECT 33 schemaname, tablename, bs, 34 COALESCE(c2.relname,'?') AS iname, COALESCE(c2.reltuples,0) AS ituples, COALESCE(c2.relpages,0) AS ipages, 35 COALESCE(CEIL((c2.reltuples*(datahdr-12))/(bs-20::float)),0) AS iotta -- very rough approximation, assumes all cols 36 FROM bloat_info 37 JOIN pg_class cc ON cc.relname = bloat_info.tablename 38 JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname = bloat_info.schemaname AND nn.nspname <> 'information_schema' 39 JOIN pg_index i ON indrelid = cc.oid 40 JOIN pg_class c2 ON c2.oid = i.indexrelid 41 ) 42 SELECT 43 type, schemaname, object_name, bloat, pg_size_pretty(raw_waste) as waste 44 FROM 45 (SELECT 46 'table' as type, 47 schemaname, 48 tablename as object_name, 49 ROUND(CASE WHEN otta=0 THEN 0.0 ELSE table_bloat.relpages/otta::numeric END,1) AS bloat, 50 CASE WHEN relpages < otta THEN '0' ELSE (bs*(table_bloat.relpages-otta)::bigint)::bigint END AS raw_waste 51 FROM 52 table_bloat 53 UNION 54 SELECT 55 'index' as type, 56 schemaname, 57 tablename || '::' || iname as object_name, 58 ROUND(CASE WHEN iotta=0 OR ipages=0 THEN 0.0 ELSE ipages/iotta::numeric END,1) AS bloat, 59 CASE WHEN ipages < iotta THEN '0' ELSE (bs*(ipages-iotta))::bigint END AS raw_waste 60 FROM 61 index_bloat) bloat_summary 62 WHERE NOT schemaname LIKE ANY($1) 63 ORDER BY raw_waste DESC, bloat DESC` 64 65 // Ref: https://github.com/heroku/heroku-pg-extras/blob/main/commands/blocking.js#L7 66 const BLOCKING_QUERY = `SELECT 67 bl.pid AS blocked_pid, 68 ka.query AS blocking_statement, 69 age(now(), ka.query_start)::text AS blocking_duration, 70 kl.pid AS blocking_pid, 71 a.query AS blocked_statement, 72 age(now(), a.query_start)::text AS blocked_duration 73 FROM pg_catalog.pg_locks bl 74 JOIN pg_catalog.pg_stat_activity a 75 ON bl.pid = a.pid 76 JOIN pg_catalog.pg_locks kl 77 JOIN pg_catalog.pg_stat_activity ka 78 ON kl.pid = ka.pid 79 ON bl.transactionid = kl.transactionid AND bl.pid != kl.pid 80 WHERE NOT bl.granted` 81 82 // Ref: https://github.com/heroku/heroku-pg-extras/blob/main/commands/cache_hit.js#L7 83 const CACHE_QUERY = `SELECT 84 'index hit rate' AS name, 85 (sum(idx_blks_hit)) / nullif(sum(idx_blks_hit + idx_blks_read),0) AS ratio 86 FROM pg_statio_user_indexes 87 UNION ALL 88 SELECT 89 'table hit rate' AS name, 90 sum(heap_blks_hit) / nullif(sum(heap_blks_hit) + sum(heap_blks_read),0) AS ratio 91 FROM pg_statio_user_tables;` 92 93 const CALLS_QUERY = `SELECT 94 query, 95 (interval '1 millisecond' * total_exec_time)::text AS total_exec_time, 96 to_char((total_exec_time/sum(total_exec_time) OVER()) * 100, 'FM90D0') || '%' AS prop_exec_time, 97 to_char(calls, 'FM999G999G990') AS ncalls, 98 (interval '1 millisecond' * (blk_read_time + blk_write_time))::text AS sync_io_time 99 FROM pg_stat_statements 100 ORDER BY calls DESC 101 LIMIT 10` 102 103 const INDEX_SIZES_QUERY = `SELECT 104 n.nspname || '.' || c.relname AS name, 105 pg_size_pretty(sum(c.relpages::bigint*8192)::bigint) AS size 106 FROM pg_class c 107 LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) 108 WHERE NOT n.nspname LIKE ANY($1) 109 AND c.relkind = 'i' 110 GROUP BY n.nspname, c.relname 111 ORDER BY sum(c.relpages) DESC` 112 113 const INDEX_USAGE_QUERY = `SELECT 114 schemaname || '.' || relname AS name, 115 CASE 116 WHEN idx_scan IS NULL THEN 'Insufficient data' 117 WHEN idx_scan = 0 THEN 'Insufficient data' 118 ELSE ROUND(100.0 * idx_scan / (seq_scan + idx_scan), 1) || '%' 119 END percent_of_times_index_used, 120 n_live_tup rows_in_table 121 FROM pg_stat_user_tables 122 WHERE NOT schemaname LIKE ANY($1) 123 ORDER BY 124 CASE 125 WHEN idx_scan is null then 1 126 WHEN idx_scan = 0 then 1 127 ELSE 0 128 END, 129 n_live_tup DESC` 130 131 const LOCKS_QUERY = `SELECT 132 pg_stat_activity.pid, 133 COALESCE(pg_class.relname, 'null') AS relname, 134 COALESCE(pg_locks.transactionid, 'null') AS transactionid, 135 pg_locks.granted, 136 pg_stat_activity.query, 137 age(now(), pg_stat_activity.query_start)::text AS age 138 FROM pg_stat_activity, pg_locks LEFT OUTER JOIN pg_class ON (pg_locks.relation = pg_class.oid) 139 WHERE pg_stat_activity.query <> '<insufficient privilege>' 140 AND pg_locks.pid = pg_stat_activity.pid 141 AND pg_locks.mode = 'ExclusiveLock' 142 ORDER BY query_start` 143 144 const LONG_RUNNING_QUERY = `SELECT 145 pid, 146 age(now(), pg_stat_activity.query_start)::text AS duration, 147 query AS query 148 FROM 149 pg_stat_activity 150 WHERE 151 pg_stat_activity.query <> ''::text 152 AND state <> 'idle' 153 AND age(now(), pg_stat_activity.query_start) > interval '5 minutes' 154 ORDER BY 155 age(now(), pg_stat_activity.query_start) DESC` 156 157 const OUTLIERS_QUERY = `SELECT 158 (interval '1 millisecond' * total_exec_time)::text AS total_exec_time, 159 to_char((total_exec_time/sum(total_exec_time) OVER()) * 100, 'FM90D0') || '%' AS prop_exec_time, 160 to_char(calls, 'FM999G999G999G990') AS ncalls, 161 (interval '1 millisecond' * (blk_read_time + blk_write_time))::text AS sync_io_time, 162 query 163 FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1) 164 ORDER BY total_exec_time DESC 165 LIMIT 10` 166 167 const REPLICATION_SLOTS_QUERY = `SELECT 168 s.slot_name, 169 s.active, 170 COALESCE(r.state, 'N/A') as state, 171 CASE WHEN r.client_addr IS NULL 172 THEN 'N/A' 173 ELSE r.client_addr::text 174 END replication_client_address, 175 GREATEST(0, ROUND((redo_lsn-restart_lsn)/1024/1024/1024, 2)) as replication_lag_gb 176 FROM pg_control_checkpoint(), pg_replication_slots s 177 LEFT JOIN pg_stat_replication r ON (r.pid = s.active_pid)` 178 179 const ROLE_CONNECTIONS_QUERY = `SELECT 180 rolname, 181 ( 182 SELECT 183 count(*) 184 FROM 185 pg_stat_activity 186 WHERE 187 pg_roles.rolname = pg_stat_activity.usename 188 ) AS active_connections, 189 CASE WHEN rolconnlimit = -1 190 THEN current_setting('max_connections')::int8 191 ELSE rolconnlimit 192 END AS connection_limit 193 FROM pg_roles 194 ORDER BY 2 DESC` 195 196 const SEQ_SCANS_QUERY = `SELECT 197 schemaname || '.' || relname AS name, 198 seq_scan as count 199 FROM pg_stat_user_tables 200 WHERE NOT schemaname LIKE ANY($1) 201 ORDER BY seq_scan DESC` 202 203 const TABLE_INDEX_SIZES_QUERY = `SELECT 204 n.nspname || '.' || c.relname AS table, 205 pg_size_pretty(pg_indexes_size(c.oid)) AS index_size 206 FROM pg_class c 207 LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) 208 WHERE NOT n.nspname LIKE ANY($1) 209 AND c.relkind = 'r' 210 ORDER BY pg_indexes_size(c.oid) DESC` 211 212 const TABLE_RECORD_COUNTS_QUERY = `SELECT 213 schemaname AS schema, 214 relname AS name, 215 n_live_tup AS estimated_count 216 FROM pg_stat_user_tables 217 WHERE NOT schemaname LIKE ANY($1) 218 ORDER BY n_live_tup DESC` 219 220 const TABLE_SIZES_QUERY = `SELECT 221 n.nspname AS schema, 222 c.relname AS name, 223 pg_size_pretty(pg_table_size(c.oid)) AS size 224 FROM pg_class c 225 LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) 226 WHERE NOT n.nspname LIKE ANY($1) 227 AND c.relkind = 'r' 228 ORDER BY pg_table_size(c.oid) DESC` 229 230 const TOTAL_INDEX_SIZE_QUERY = `SELECT 231 pg_size_pretty(sum(c.relpages::bigint*8192)::bigint) AS size 232 FROM pg_class c 233 LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) 234 WHERE NOT n.nspname LIKE ANY($1) 235 AND c.relkind = 'i'` 236 237 const TOTAL_TABLE_SIZES_QUERY = `SELECT 238 n.nspname AS schema, 239 c.relname AS name, 240 pg_size_pretty(pg_total_relation_size(c.oid)) AS size 241 FROM pg_class c 242 LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) 243 WHERE NOT n.nspname LIKE ANY($1) 244 AND c.relkind = 'r' 245 ORDER BY pg_total_relation_size(c.oid) DESC` 246 247 const UNUSED_INDEXES_QUERY = `SELECT 248 schemaname || '.' || relname AS table, 249 indexrelname AS index, 250 pg_size_pretty(pg_relation_size(i.indexrelid)) AS index_size, 251 idx_scan as index_scans 252 FROM pg_stat_user_indexes ui 253 JOIN pg_index i ON ui.indexrelid = i.indexrelid 254 WHERE 255 NOT indisunique AND idx_scan < 50 AND pg_relation_size(relid) > 5 * 8192 256 AND NOT schemaname LIKE ANY($1) 257 ORDER BY 258 pg_relation_size(i.indexrelid) / nullif(idx_scan, 0) DESC NULLS FIRST, 259 pg_relation_size(i.indexrelid) DESC` 260 261 const VACUUM_STATS_QUERY = `WITH table_opts AS ( 262 SELECT 263 pg_class.oid, relname, nspname, array_to_string(reloptions, '') AS relopts 264 FROM 265 pg_class INNER JOIN pg_namespace ns ON relnamespace = ns.oid 266 ), vacuum_settings AS ( 267 SELECT 268 oid, relname, nspname, 269 CASE 270 WHEN relopts LIKE '%autovacuum_vacuum_threshold%' 271 THEN substring(relopts, '.*autovacuum_vacuum_threshold=([0-9.]+).*')::integer 272 ELSE current_setting('autovacuum_vacuum_threshold')::integer 273 END AS autovacuum_vacuum_threshold, 274 CASE 275 WHEN relopts LIKE '%autovacuum_vacuum_scale_factor%' 276 THEN substring(relopts, '.*autovacuum_vacuum_scale_factor=([0-9.]+).*')::real 277 ELSE current_setting('autovacuum_vacuum_scale_factor')::real 278 END AS autovacuum_vacuum_scale_factor 279 FROM 280 table_opts 281 ) 282 SELECT 283 vacuum_settings.nspname AS schema, 284 vacuum_settings.relname AS table, 285 coalesce(to_char(psut.last_vacuum, 'YYYY-MM-DD HH24:MI'), '') AS last_vacuum, 286 coalesce(to_char(psut.last_autovacuum, 'YYYY-MM-DD HH24:MI'), '') AS last_autovacuum, 287 to_char(pg_class.reltuples, '9G999G999G999') AS rowcount, 288 to_char(psut.n_dead_tup, '9G999G999G999') AS dead_rowcount, 289 to_char(autovacuum_vacuum_threshold 290 + (autovacuum_vacuum_scale_factor::numeric * pg_class.reltuples), '9G999G999G999') AS autovacuum_threshold, 291 CASE 292 WHEN autovacuum_vacuum_threshold + (autovacuum_vacuum_scale_factor::numeric * pg_class.reltuples) < psut.n_dead_tup 293 THEN 'yes' 294 ELSE 'no' 295 END AS expect_autovacuum 296 FROM 297 pg_stat_user_tables psut INNER JOIN pg_class ON psut.relid = pg_class.oid 298 INNER JOIN vacuum_settings ON pg_class.oid = vacuum_settings.oid 299 WHERE NOT vacuum_settings.nspname LIKE ANY($1) 300 ORDER BY 301 case 302 when pg_class.reltuples = -1 then 1 303 else 0 304 end, 305 1`