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`