diff --git a/lib/ecto_psql_extras.ex b/lib/ecto_psql_extras.ex index 8b233aa..cc062f7 100644 --- a/lib/ecto_psql_extras.ex +++ b/lib/ecto_psql_extras.ex @@ -12,7 +12,7 @@ defmodule EctoPSQLExtras do optional(:args_for_select) => list } - @callback query :: binary + @callback query(args :: keyword) :: {binary, list} @type repo :: module() | {module(), node()} @@ -138,10 +138,13 @@ defmodule EctoPSQLExtras do query_module = Map.fetch!(queries(repo), name) opts = prepare_opts(opts, query_module.info()[:default_args]) + {statement, params} = query_module.query(Keyword.fetch!(opts, :args)) + result = query!( repo, - query_module.query(Keyword.fetch!(opts, :args)), + statement, + params, Keyword.get(opts, :query_opts, @default_query_opts) ) @@ -152,10 +155,10 @@ defmodule EctoPSQLExtras do ) end - defp query!(repo, query, query_opts \\ @default_query_opts) + defp query!(repo, query, params \\ [], query_opts \\ @default_query_opts) - defp query!({repo, node}, query, query_opts) do - case :rpc.call(node, repo, :query!, [query, [], query_opts]) do + defp query!({repo, node}, query, params, query_opts) do + case :rpc.call(node, repo, :query!, [query, params, query_opts]) do {:badrpc, {:EXIT, {:undef, _}}} -> raise "repository is not defined on remote node" @@ -167,8 +170,8 @@ defmodule EctoPSQLExtras do end end - defp query!(repo, query, query_opts) do - repo.query!(query, [], query_opts) + defp query!(repo, query, params, query_opts) do + repo.query!(query, params, query_opts) end @doc """ diff --git a/lib/queries/all_locks.ex b/lib/queries/all_locks.ex index edda8c0..6268dd1 100644 --- a/lib/queries/all_locks.ex +++ b/lib/queries/all_locks.ex @@ -18,23 +18,23 @@ defmodule EctoPSQLExtras.AllLocks do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Queries with active locks */ + {""" + /* ECTO_PSQL_EXTRAS: Queries with active locks */ - SELECT - pg_stat_activity.pid, - pg_class.relname, - pg_locks.transactionid, - pg_locks.granted, - pg_locks.mode, - pg_stat_activity.query AS query_snippet, - age(now(),pg_stat_activity.query_start) AS "age" - FROM pg_stat_activity,pg_locks left - OUTER JOIN pg_class - ON (pg_locks.relation = pg_class.oid) - WHERE pg_stat_activity.query <> '' - AND pg_locks.pid = pg_stat_activity.pid - AND pg_stat_activity.pid <> pg_backend_pid() order by query_start; - """ + SELECT + pg_stat_activity.pid, + pg_class.relname, + pg_locks.transactionid, + pg_locks.granted, + pg_locks.mode, + pg_stat_activity.query AS query_snippet, + age(now(),pg_stat_activity.query_start) AS "age" + FROM pg_stat_activity,pg_locks left + OUTER JOIN pg_class + ON (pg_locks.relation = pg_class.oid) + WHERE pg_stat_activity.query <> '' + AND pg_locks.pid = pg_stat_activity.pid + AND pg_stat_activity.pid <> pg_backend_pid() order by query_start; + """, []} end end diff --git a/lib/queries/bloat.ex b/lib/queries/bloat.ex index 7c4c770..a063368 100644 --- a/lib/queries/bloat.ex +++ b/lib/queries/bloat.ex @@ -17,69 +17,69 @@ defmodule EctoPSQLExtras.Bloat do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Table and index bloat in your database ordered by most wasteful */ + {""" + /* ECTO_PSQL_EXTRAS: Table and index bloat in your database ordered by most wasteful */ - WITH constants AS ( - SELECT current_setting('block_size')::numeric AS bs, 23 AS hdr, 4 AS ma - ), bloat_info AS ( - SELECT - ma,bs,schemaname,tablename, - (datawidth+(hdr+ma-(case when hdr%ma=0 THEN ma ELSE hdr%ma END)))::numeric AS datahdr, - (maxfracsum*(nullhdr+ma-(case when nullhdr%ma=0 THEN ma ELSE nullhdr%ma END))) AS nullhdr2 - FROM ( - SELECT - schemaname, tablename, hdr, ma, bs, - SUM((1-null_frac)*avg_width) AS datawidth, - MAX(null_frac) AS maxfracsum, - hdr+( - SELECT 1+count(*)/8 - FROM pg_stats s2 - WHERE null_frac<>0 AND s2.schemaname = s.schemaname AND s2.tablename = s.tablename - ) AS nullhdr - FROM pg_stats s, constants - GROUP BY 1,2,3,4,5 - ) AS foo - ), table_bloat AS ( - SELECT - schemaname, tablename, cc.relpages, bs, - CEIL((cc.reltuples*((datahdr+ma- - (CASE WHEN datahdr%ma=0 THEN ma ELSE datahdr%ma END))+nullhdr2+4))/(bs-20::float)) AS otta - FROM bloat_info - JOIN pg_class cc ON cc.relname = bloat_info.tablename - JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname = bloat_info.schemaname AND nn.nspname <> 'information_schema' - ), index_bloat AS ( - SELECT - schemaname, tablename, bs, - COALESCE(c2.relname,'?') AS iname, COALESCE(c2.reltuples,0) AS ituples, COALESCE(c2.relpages,0) AS ipages, - COALESCE(CEIL((c2.reltuples*(datahdr-12))/(bs-20::float)),0) AS iotta -- very rough approximation, assumes all cols - FROM bloat_info - JOIN pg_class cc ON cc.relname = bloat_info.tablename - JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname = bloat_info.schemaname AND nn.nspname <> 'information_schema' - JOIN pg_index i ON indrelid = cc.oid - JOIN pg_class c2 ON c2.oid = i.indexrelid - ) - SELECT - type, schemaname, object_name, bloat, waste - FROM - (SELECT - 'table' as type, - schemaname, - tablename as object_name, - ROUND(CASE WHEN otta=0 THEN 0.0 ELSE table_bloat.relpages/otta::numeric END,1) AS bloat, - CASE WHEN relpages < otta THEN 0 ELSE (bs*(table_bloat.relpages-otta)::bigint)::bigint END AS waste - FROM - table_bloat - UNION - SELECT - 'index' as type, - schemaname, - tablename || '::' || iname as object_name, - ROUND(CASE WHEN iotta=0 OR ipages=0 THEN 0.0 ELSE ipages/iotta::numeric END,1) AS bloat, - CASE WHEN ipages < iotta THEN 0 ELSE (bs*(ipages-iotta))::bigint END AS waste - FROM - index_bloat) bloat_summary - ORDER BY waste DESC, bloat DESC; - """ + WITH constants AS ( + SELECT current_setting('block_size')::numeric AS bs, 23 AS hdr, 4 AS ma + ), bloat_info AS ( + SELECT + ma,bs,schemaname,tablename, + (datawidth+(hdr+ma-(case when hdr%ma=0 THEN ma ELSE hdr%ma END)))::numeric AS datahdr, + (maxfracsum*(nullhdr+ma-(case when nullhdr%ma=0 THEN ma ELSE nullhdr%ma END))) AS nullhdr2 + FROM ( + SELECT + schemaname, tablename, hdr, ma, bs, + SUM((1-null_frac)*avg_width) AS datawidth, + MAX(null_frac) AS maxfracsum, + hdr+( + SELECT 1+count(*)/8 + FROM pg_stats s2 + WHERE null_frac<>0 AND s2.schemaname = s.schemaname AND s2.tablename = s.tablename + ) AS nullhdr + FROM pg_stats s, constants + GROUP BY 1,2,3,4,5 + ) AS foo + ), table_bloat AS ( + SELECT + schemaname, tablename, cc.relpages, bs, + CEIL((cc.reltuples*((datahdr+ma- + (CASE WHEN datahdr%ma=0 THEN ma ELSE datahdr%ma END))+nullhdr2+4))/(bs-20::float)) AS otta + FROM bloat_info + JOIN pg_class cc ON cc.relname = bloat_info.tablename + JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname = bloat_info.schemaname AND nn.nspname <> 'information_schema' + ), index_bloat AS ( + SELECT + schemaname, tablename, bs, + COALESCE(c2.relname,'?') AS iname, COALESCE(c2.reltuples,0) AS ituples, COALESCE(c2.relpages,0) AS ipages, + COALESCE(CEIL((c2.reltuples*(datahdr-12))/(bs-20::float)),0) AS iotta -- very rough approximation, assumes all cols + FROM bloat_info + JOIN pg_class cc ON cc.relname = bloat_info.tablename + JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname = bloat_info.schemaname AND nn.nspname <> 'information_schema' + JOIN pg_index i ON indrelid = cc.oid + JOIN pg_class c2 ON c2.oid = i.indexrelid + ) + SELECT + type, schemaname, object_name, bloat, waste + FROM + (SELECT + 'table' as type, + schemaname, + tablename as object_name, + ROUND(CASE WHEN otta=0 THEN 0.0 ELSE table_bloat.relpages/otta::numeric END,1) AS bloat, + CASE WHEN relpages < otta THEN 0 ELSE (bs*(table_bloat.relpages-otta)::bigint)::bigint END AS waste + FROM + table_bloat + UNION + SELECT + 'index' as type, + schemaname, + tablename || '::' || iname as object_name, + ROUND(CASE WHEN iotta=0 OR ipages=0 THEN 0.0 ELSE ipages/iotta::numeric END,1) AS bloat, + CASE WHEN ipages < iotta THEN 0 ELSE (bs*(ipages-iotta))::bigint END AS waste + FROM + index_bloat) bloat_summary + ORDER BY waste DESC, bloat DESC; + """, []} end end diff --git a/lib/queries/blocking.ex b/lib/queries/blocking.ex index 2cc8ea6..8c54846 100644 --- a/lib/queries/blocking.ex +++ b/lib/queries/blocking.ex @@ -17,23 +17,23 @@ defmodule EctoPSQLExtras.Blocking do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Queries holding locks other queries are waiting to be released */ + {""" + /* ECTO_PSQL_EXTRAS: Queries holding locks other queries are waiting to be released */ - SELECT bl.pid AS blocked_pid, - ka.query AS blocking_statement, - now() - ka.query_start AS blocking_duration, - kl.pid AS blocking_pid, - a.query AS blocked_statement, - now() - a.query_start AS blocked_duration - FROM pg_catalog.pg_locks bl - JOIN pg_catalog.pg_stat_activity a - ON bl.pid = a.pid - JOIN pg_catalog.pg_locks kl - JOIN pg_catalog.pg_stat_activity ka - ON kl.pid = ka.pid - ON bl.transactionid = kl.transactionid AND bl.pid != kl.pid - WHERE NOT bl.granted; - """ + SELECT bl.pid AS blocked_pid, + ka.query AS blocking_statement, + now() - ka.query_start AS blocking_duration, + kl.pid AS blocking_pid, + a.query AS blocked_statement, + now() - a.query_start AS blocked_duration + FROM pg_catalog.pg_locks bl + JOIN pg_catalog.pg_stat_activity a + ON bl.pid = a.pid + JOIN pg_catalog.pg_locks kl + JOIN pg_catalog.pg_stat_activity ka + ON kl.pid = ka.pid + ON bl.transactionid = kl.transactionid AND bl.pid != kl.pid + WHERE NOT bl.granted; + """, []} end end diff --git a/lib/queries/cache_hit.ex b/lib/queries/cache_hit.ex index 567d356..59ae6dc 100644 --- a/lib/queries/cache_hit.ex +++ b/lib/queries/cache_hit.ex @@ -13,18 +13,18 @@ defmodule EctoPSQLExtras.CacheHit do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Index and table hit rate */ + {""" + /* ECTO_PSQL_EXTRAS: Index and table hit rate */ - SELECT - 'index hit rate' AS name, - (sum(idx_blks_hit)) / nullif(sum(idx_blks_hit + idx_blks_read),0) AS ratio - FROM pg_statio_user_indexes - UNION ALL - SELECT - 'table hit rate' AS name, - sum(heap_blks_hit) / nullif(sum(heap_blks_hit) + sum(heap_blks_read),0) AS ratio - FROM pg_statio_user_tables; - """ + SELECT + 'index hit rate' AS name, + (sum(idx_blks_hit)) / nullif(sum(idx_blks_hit + idx_blks_read),0) AS ratio + FROM pg_statio_user_indexes + UNION ALL + SELECT + 'table hit rate' AS name, + sum(heap_blks_hit) / nullif(sum(heap_blks_hit) + sum(heap_blks_read),0) AS ratio + FROM pg_statio_user_tables; + """, []} end end diff --git a/lib/queries/calls.ex b/lib/queries/calls.ex index 1c712c4..34469ce 100644 --- a/lib/queries/calls.ex +++ b/lib/queries/calls.ex @@ -18,19 +18,18 @@ defmodule EctoPSQLExtras.Calls do end def query(args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Queries that have the highest frequency of execution */ + {""" + /* ECTO_PSQL_EXTRAS: Queries that have the highest frequency of execution */ - SELECT query AS query, - interval '1 millisecond' * total_exec_time AS exec_time, - (total_exec_time/sum(total_exec_time) OVER()) AS exec_time_ratio, - calls, - interval '1 millisecond' * (blk_read_time + blk_write_time) AS sync_io_time - FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1) - AND query NOT LIKE '/* ECTO_PSQL_EXTRAS:%' - ORDER BY calls DESC - LIMIT <%= limit %>; - """ - |> EEx.eval_string(args) + SELECT query AS query, + interval '1 millisecond' * total_exec_time AS exec_time, + (total_exec_time/sum(total_exec_time) OVER()) AS exec_time_ratio, + calls, + interval '1 millisecond' * (blk_read_time + blk_write_time) AS sync_io_time + FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1) + AND query NOT LIKE '/* ECTO_PSQL_EXTRAS:%' + ORDER BY calls DESC + LIMIT $1; + """, [args[:limit]]} end end diff --git a/lib/queries/calls_17.ex b/lib/queries/calls_17.ex index 120dfca..a32e73b 100644 --- a/lib/queries/calls_17.ex +++ b/lib/queries/calls_17.ex @@ -18,19 +18,18 @@ defmodule EctoPSQLExtras.Calls17 do end def query(args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Queries that have the highest frequency of execution */ + {""" + /* ECTO_PSQL_EXTRAS: Queries that have the highest frequency of execution */ - SELECT query AS query, - interval '1 millisecond' * total_exec_time AS exec_time, - (total_exec_time/sum(total_exec_time) OVER()) AS exec_time_ratio, - calls, - interval '1 millisecond' * (shared_blk_read_time + shared_blk_write_time) AS sync_io_time - FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1) - AND query NOT LIKE '/* ECTO_PSQL_EXTRAS:%' - ORDER BY calls DESC - LIMIT <%= limit %>; - """ - |> EEx.eval_string(args) + SELECT query AS query, + interval '1 millisecond' * total_exec_time AS exec_time, + (total_exec_time/sum(total_exec_time) OVER()) AS exec_time_ratio, + calls, + interval '1 millisecond' * (shared_blk_read_time + shared_blk_write_time) AS sync_io_time + FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1) + AND query NOT LIKE '/* ECTO_PSQL_EXTRAS:%' + ORDER BY calls DESC + LIMIT $1; + """, [args[:limit]]} end end diff --git a/lib/queries/calls_legacy.ex b/lib/queries/calls_legacy.ex index dbb1fbc..4192c3f 100644 --- a/lib/queries/calls_legacy.ex +++ b/lib/queries/calls_legacy.ex @@ -17,19 +17,18 @@ defmodule EctoPSQLExtras.CallsLegacy do end def query(args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Queries that have the highest frequency of execution */ + {""" + /* ECTO_PSQL_EXTRAS: Queries that have the highest frequency of execution */ - SELECT query AS query, - interval '1 millisecond' * total_time AS exec_time, - (total_time/sum(total_time) OVER()) AS exec_time_ratio, - calls, - interval '1 millisecond' * (blk_read_time + blk_write_time) AS sync_io_time - FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1) - AND query NOT LIKE '/* ECTO_PSQL_EXTRAS:%' - ORDER BY calls DESC - LIMIT <%= limit %>; - """ - |> EEx.eval_string(args) + SELECT query AS query, + interval '1 millisecond' * total_time AS exec_time, + (total_time/sum(total_time) OVER()) AS exec_time_ratio, + calls, + interval '1 millisecond' * (blk_read_time + blk_write_time) AS sync_io_time + FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1) + AND query NOT LIKE '/* ECTO_PSQL_EXTRAS:%' + ORDER BY calls DESC + LIMIT $1; + """, [args[:limit]]} end end diff --git a/lib/queries/connections.ex b/lib/queries/connections.ex index 8dd55e0..14a3f1c 100644 --- a/lib/queries/connections.ex +++ b/lib/queries/connections.ex @@ -13,11 +13,11 @@ defmodule EctoPSQLExtras.Connections do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Returns the list of all active database connections */ + {""" + /* ECTO_PSQL_EXTRAS: Returns the list of all active database connections */ - SELECT usename as username, client_addr::text as client_address, application_name FROM pg_stat_activity - WHERE datname = current_database(); - """ + SELECT usename as username, client_addr::text as client_address, application_name FROM pg_stat_activity + WHERE datname = current_database(); + """, []} end end diff --git a/lib/queries/db_settings.ex b/lib/queries/db_settings.ex index 7120f98..37542f0 100644 --- a/lib/queries/db_settings.ex +++ b/lib/queries/db_settings.ex @@ -15,16 +15,16 @@ defmodule EctoPSQLExtras.DbSettings do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Values of selected PostgreSQL settings */ + {""" + /* ECTO_PSQL_EXTRAS: Values of selected PostgreSQL settings */ - SELECT name, setting, unit, short_desc FROM pg_settings - WHERE name IN ( - 'max_connections', 'shared_buffers', 'effective_cache_size', - 'maintenance_work_mem', 'checkpoint_completion_target', 'wal_buffers', - 'default_statistics_target', 'random_page_cost', 'effective_io_concurrency', - 'work_mem', 'min_wal_size', 'max_wal_size' - ); - """ + SELECT name, setting, unit, short_desc FROM pg_settings + WHERE name IN ( + 'max_connections', 'shared_buffers', 'effective_cache_size', + 'maintenance_work_mem', 'checkpoint_completion_target', 'wal_buffers', + 'default_statistics_target', 'random_page_cost', 'effective_io_concurrency', + 'work_mem', 'min_wal_size', 'max_wal_size' + ); + """, []} end end diff --git a/lib/queries/duplicate_indexes.ex b/lib/queries/duplicate_indexes.ex index f1f0954..55f5c70 100644 --- a/lib/queries/duplicate_indexes.ex +++ b/lib/queries/duplicate_indexes.ex @@ -16,20 +16,19 @@ defmodule EctoPSQLExtras.DuplicateIndexes do } end - def query(args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Multiple indexes that have the same set of columns, same opclass, expression and predicate */ + def query(_args \\ []) do + {""" + /* ECTO_PSQL_EXTRAS: Multiple indexes that have the same set of columns, same opclass, expression and predicate */ - SELECT pg_size_pretty(sum(pg_relation_size(idx))::bigint) as size, - (array_agg(idx))[1]::text as idx1, (array_agg(idx))[2]::text as idx2, - (array_agg(idx))[3]::text as idx3, (array_agg(idx))[4]::text as idx4 - FROM ( - SELECT indexrelid::regclass as idx, (indrelid::text ||E'\n'|| indclass::text ||E'\n'|| indkey::text ||E'\n'|| - coalesce(indexprs::text,'')||E'\n' || coalesce(indpred::text,'')) as key - FROM pg_index) sub - GROUP BY key HAVING count(*)>1 - ORDER BY sum(pg_relation_size(idx)) DESC; - """ - |> EEx.eval_string(args) + SELECT pg_size_pretty(sum(pg_relation_size(idx))::bigint) as size, + (array_agg(idx))[1]::text as idx1, (array_agg(idx))[2]::text as idx2, + (array_agg(idx))[3]::text as idx3, (array_agg(idx))[4]::text as idx4 + FROM ( + SELECT indexrelid::regclass as idx, (indrelid::text ||E'\n'|| indclass::text ||E'\n'|| indkey::text ||E'\n'|| + coalesce(indexprs::text,'')||E'\n' || coalesce(indpred::text,'')) as key + FROM pg_index) sub + GROUP BY key HAVING count(*)>1 + ORDER BY sum(pg_relation_size(idx)) DESC; + """, []} end end diff --git a/lib/queries/extensions.ex b/lib/queries/extensions.ex index be4f8e5..5b6506a 100644 --- a/lib/queries/extensions.ex +++ b/lib/queries/extensions.ex @@ -16,12 +16,12 @@ defmodule EctoPSQLExtras.Extensions do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Available and installed extensions */ + {""" + /* ECTO_PSQL_EXTRAS: Available and installed extensions */ - SELECT name, default_version, installed_version, comment - FROM pg_available_extensions - ORDER BY installed_version; - """ + SELECT name, default_version, installed_version, comment + FROM pg_available_extensions + ORDER BY installed_version; + """, []} end end diff --git a/lib/queries/index_cache_hit.ex b/lib/queries/index_cache_hit.ex index e88492d..d0b4c97 100644 --- a/lib/queries/index_cache_hit.ex +++ b/lib/queries/index_cache_hit.ex @@ -18,22 +18,22 @@ defmodule EctoPSQLExtras.IndexCacheHit do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Calculates your cache hit rate for reading indexes */ + {""" + /* ECTO_PSQL_EXTRAS: Calculates your cache hit rate for reading indexes */ - SELECT - schemaname AS schema, relname AS name, - idx_blks_hit AS buffer_hits, - idx_blks_read AS block_reads, - idx_blks_hit + idx_blks_read AS total_read, - CASE (idx_blks_hit + idx_blks_read)::float - WHEN 0 THEN NULL - ELSE (idx_blks_hit / (idx_blks_hit + idx_blks_read)::float) - END ratio - FROM - pg_statio_user_tables - ORDER BY - idx_blks_hit / (idx_blks_hit + idx_blks_read + 1)::float DESC; - """ + SELECT + schemaname AS schema, relname AS name, + idx_blks_hit AS buffer_hits, + idx_blks_read AS block_reads, + idx_blks_hit + idx_blks_read AS total_read, + CASE (idx_blks_hit + idx_blks_read)::float + WHEN 0 THEN NULL + ELSE (idx_blks_hit / (idx_blks_hit + idx_blks_read)::float) + END ratio + FROM + pg_statio_user_tables + ORDER BY + idx_blks_hit / (idx_blks_hit + idx_blks_read + 1)::float DESC; + """, []} end end diff --git a/lib/queries/index_size.ex b/lib/queries/index_size.ex index 86e111d..aa3285f 100644 --- a/lib/queries/index_size.ex +++ b/lib/queries/index_size.ex @@ -15,17 +15,17 @@ defmodule EctoPSQLExtras.IndexSize do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: The size of indexes, descending by size */ + {""" + /* ECTO_PSQL_EXTRAS: The size of indexes, descending by size */ - SELECT n.nspname AS schema, c.relname AS name, sum(c.relpages::bigint*8192)::bigint AS size - FROM pg_class c - LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) - WHERE n.nspname NOT IN ('pg_catalog', 'information_schema') - AND n.nspname !~ '^pg_toast' - AND c.relkind='i' - GROUP BY (n.nspname, c.relname) - ORDER BY sum(c.relpages) DESC; - """ + SELECT n.nspname AS schema, c.relname AS name, sum(c.relpages::bigint*8192)::bigint AS size + FROM pg_class c + LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) + WHERE n.nspname NOT IN ('pg_catalog', 'information_schema') + AND n.nspname !~ '^pg_toast' + AND c.relkind='i' + GROUP BY (n.nspname, c.relname) + ORDER BY sum(c.relpages) DESC; + """, []} end end diff --git a/lib/queries/index_usage.ex b/lib/queries/index_usage.ex index fc8510a..70966d7 100644 --- a/lib/queries/index_usage.ex +++ b/lib/queries/index_usage.ex @@ -15,19 +15,19 @@ defmodule EctoPSQLExtras.IndexUsage do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Index hit rate (effective databases are at 99% and up) */ + {""" + /* ECTO_PSQL_EXTRAS: Index hit rate (effective databases are at 99% and up) */ - SELECT schemaname AS schema, relname AS name, - CASE idx_scan - WHEN 0 THEN NULL - ELSE (100 * idx_scan / (seq_scan + idx_scan)) - END percent_of_times_index_used, - n_live_tup rows_in_table - FROM - pg_stat_user_tables - ORDER BY - n_live_tup DESC; - """ + SELECT schemaname AS schema, relname AS name, + CASE idx_scan + WHEN 0 THEN NULL + ELSE (100 * idx_scan / (seq_scan + idx_scan)) + END percent_of_times_index_used, + n_live_tup rows_in_table + FROM + pg_stat_user_tables + ORDER BY + n_live_tup DESC; + """, []} end end diff --git a/lib/queries/indexes.ex b/lib/queries/indexes.ex index 29586fb..4a4df55 100644 --- a/lib/queries/indexes.ex +++ b/lib/queries/indexes.ex @@ -15,15 +15,15 @@ defmodule EctoPSQLExtras.Indexes do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: List all the indexes with their corresponding tables and columns */ - SELECT - schemaname, - indexname, - tablename, - rtrim(split_part(indexdef, '(', 2), ')') as columns - FROM pg_indexes - where tablename in (select relname from pg_statio_user_tables); - """ + {""" + /* ECTO_PSQL_EXTRAS: List all the indexes with their corresponding tables and columns */ + SELECT + schemaname, + indexname, + tablename, + rtrim(split_part(indexdef, '(', 2), ')') as columns + FROM pg_indexes + where tablename in (select relname from pg_statio_user_tables); + """, []} end end diff --git a/lib/queries/kill_all.ex b/lib/queries/kill_all.ex index 8e57684..9f0c957 100644 --- a/lib/queries/kill_all.ex +++ b/lib/queries/kill_all.ex @@ -11,13 +11,13 @@ defmodule EctoPSQLExtras.KillAll do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Kill all the active database connections */ + {""" + /* ECTO_PSQL_EXTRAS: Kill all the active database connections */ - SELECT pg_terminate_backend(pid) AS killed FROM pg_stat_activity - WHERE pid <> pg_backend_pid() - AND query <> '' - AND datname = current_database(); - """ + SELECT pg_terminate_backend(pid) AS killed FROM pg_stat_activity + WHERE pid <> pg_backend_pid() + AND query <> '' + AND datname = current_database(); + """, []} end end diff --git a/lib/queries/locks.ex b/lib/queries/locks.ex index 0ddc42a..cd24e0d 100644 --- a/lib/queries/locks.ex +++ b/lib/queries/locks.ex @@ -18,24 +18,24 @@ defmodule EctoPSQLExtras.Locks do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Queries with active exclusive locks */ + {""" + /* ECTO_PSQL_EXTRAS: Queries with active exclusive locks */ - SELECT - pg_stat_activity.pid, - pg_class.relname, - pg_locks.transactionid, - pg_locks.granted, - pg_locks.mode, - pg_stat_activity.query AS query_snippet, - age(now(),pg_stat_activity.query_start) AS "age" - FROM pg_stat_activity,pg_locks left - OUTER JOIN pg_class - ON (pg_locks.relation = pg_class.oid) - WHERE pg_stat_activity.query <> '' - AND pg_locks.pid = pg_stat_activity.pid - AND pg_locks.mode IN ('ExclusiveLock', 'AccessExclusiveLock', 'RowExclusiveLock') - AND pg_stat_activity.pid <> pg_backend_pid() order by query_start; - """ + SELECT + pg_stat_activity.pid, + pg_class.relname, + pg_locks.transactionid, + pg_locks.granted, + pg_locks.mode, + pg_stat_activity.query AS query_snippet, + age(now(),pg_stat_activity.query_start) AS "age" + FROM pg_stat_activity,pg_locks left + OUTER JOIN pg_class + ON (pg_locks.relation = pg_class.oid) + WHERE pg_stat_activity.query <> '' + AND pg_locks.pid = pg_stat_activity.pid + AND pg_locks.mode IN ('ExclusiveLock', 'AccessExclusiveLock', 'RowExclusiveLock') + AND pg_stat_activity.pid <> pg_backend_pid() order by query_start; + """, []} end end diff --git a/lib/queries/long_running_queries.ex b/lib/queries/long_running_queries.ex index 0b2dc81..eb50634 100644 --- a/lib/queries/long_running_queries.ex +++ b/lib/queries/long_running_queries.ex @@ -16,22 +16,21 @@ defmodule EctoPSQLExtras.LongRunningQueries do end def query(args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: All queries longer than the threshold by descending duration */ + {""" + /* ECTO_PSQL_EXTRAS: All queries longer than the threshold by descending duration */ - SELECT - pid, - now() - pg_stat_activity.query_start AS duration, - query AS query - FROM - pg_stat_activity - WHERE - pg_stat_activity.query <> ''::text - AND state <> 'idle' - AND now() - pg_stat_activity.query_start > interval '<%= threshold %>' - ORDER BY - now() - pg_stat_activity.query_start DESC; - """ - |> EEx.eval_string(args) + SELECT + pid, + now() - pg_stat_activity.query_start AS duration, + query AS query + FROM + pg_stat_activity + WHERE + pg_stat_activity.query <> ''::text + AND state <> 'idle' + AND now() - pg_stat_activity.query_start > $1::text::interval + ORDER BY + now() - pg_stat_activity.query_start DESC; + """, [to_string(args[:threshold])]} end end diff --git a/lib/queries/null_indexes.ex b/lib/queries/null_indexes.ex index 3824b81..ba031e3 100644 --- a/lib/queries/null_indexes.ex +++ b/lib/queries/null_indexes.ex @@ -19,41 +19,40 @@ defmodule EctoPSQLExtras.NullIndexes do end def query(args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Find indexes with a high ratio of NULL values */ + {""" + /* ECTO_PSQL_EXTRAS: Find indexes with a high ratio of NULL values */ - SELECT - c.oid, - c.relname AS index, - pg_size_pretty(pg_relation_size(c.oid)) AS index_size, - i.indisunique AS unique, - a.attname AS indexed_column, - CASE s.null_frac - WHEN 0 THEN '' - ELSE to_char(s.null_frac * 100, '999.00%') - END AS null_frac, - pg_size_pretty((pg_relation_size(c.oid) * s.null_frac)::bigint) AS expected_saving - FROM - pg_class c - JOIN pg_index i ON i.indexrelid = c.oid - JOIN pg_attribute a ON a.attrelid = c.oid - JOIN pg_class c_table ON c_table.oid = i.indrelid - JOIN pg_indexes ixs ON c.relname = ixs.indexname - LEFT JOIN pg_stats s ON s.tablename = c_table.relname AND a.attname = s.attname - WHERE - -- Primary key cannot be partial - NOT i.indisprimary - -- Exclude already partial indexes - AND i.indpred IS NULL - -- Exclude composite indexes - AND array_length(i.indkey, 1) = 1 - -- Exclude indexes without null_frac ratio - AND coalesce(s.null_frac, 0) != 0 - -- Larger than threshold - AND pg_relation_size(c.oid) > <%= min_relation_size_mb %> * 1024 ^ 2 - ORDER BY - pg_relation_size(c.oid) * s.null_frac DESC; - """ - |> EEx.eval_string(args) + SELECT + c.oid, + c.relname AS index, + pg_size_pretty(pg_relation_size(c.oid)) AS index_size, + i.indisunique AS unique, + a.attname AS indexed_column, + CASE s.null_frac + WHEN 0 THEN '' + ELSE to_char(s.null_frac * 100, '999.00%') + END AS null_frac, + pg_size_pretty((pg_relation_size(c.oid) * s.null_frac)::bigint) AS expected_saving + FROM + pg_class c + JOIN pg_index i ON i.indexrelid = c.oid + JOIN pg_attribute a ON a.attrelid = c.oid + JOIN pg_class c_table ON c_table.oid = i.indrelid + JOIN pg_indexes ixs ON c.relname = ixs.indexname + LEFT JOIN pg_stats s ON s.tablename = c_table.relname AND a.attname = s.attname + WHERE + -- Primary key cannot be partial + NOT i.indisprimary + -- Exclude already partial indexes + AND i.indpred IS NULL + -- Exclude composite indexes + AND array_length(i.indkey, 1) = 1 + -- Exclude indexes without null_frac ratio + AND coalesce(s.null_frac, 0) != 0 + -- Larger than threshold + AND pg_relation_size(c.oid) > $1 * 1024 ^ 2 + ORDER BY + pg_relation_size(c.oid) * s.null_frac DESC; + """, [args[:min_relation_size_mb]]} end end diff --git a/lib/queries/outliers.ex b/lib/queries/outliers.ex index aece13e..e1d7ee2 100644 --- a/lib/queries/outliers.ex +++ b/lib/queries/outliers.ex @@ -18,19 +18,18 @@ defmodule EctoPSQLExtras.Outliers do end def query(args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Queries that have longest execution time in aggregate */ + {""" + /* ECTO_PSQL_EXTRAS: Queries that have longest execution time in aggregate */ - SELECT query AS query, - interval '1 millisecond' * total_exec_time AS exec_time, - (total_exec_time/sum(total_exec_time) OVER()) AS prop_exec_time, - calls, - interval '1 millisecond' * (blk_read_time + blk_write_time) AS sync_io_time - FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1) - AND query NOT LIKE '/* ECTO_PSQL_EXTRAS:%' - ORDER BY total_exec_time DESC - LIMIT <%= limit %>; - """ - |> EEx.eval_string(args) + SELECT query AS query, + interval '1 millisecond' * total_exec_time AS exec_time, + (total_exec_time/sum(total_exec_time) OVER()) AS prop_exec_time, + calls, + interval '1 millisecond' * (blk_read_time + blk_write_time) AS sync_io_time + FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1) + AND query NOT LIKE '/* ECTO_PSQL_EXTRAS:%' + ORDER BY total_exec_time DESC + LIMIT $1; + """, [args[:limit]]} end end diff --git a/lib/queries/outliers_17.ex b/lib/queries/outliers_17.ex index 3b8c06e..aa3c668 100644 --- a/lib/queries/outliers_17.ex +++ b/lib/queries/outliers_17.ex @@ -18,19 +18,18 @@ defmodule EctoPSQLExtras.Outliers17 do end def query(args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Queries that have longest execution time in aggregate */ + {""" + /* ECTO_PSQL_EXTRAS: Queries that have longest execution time in aggregate */ - SELECT query AS query, - interval '1 millisecond' * total_exec_time AS exec_time, - (total_exec_time/sum(total_exec_time) OVER()) AS prop_exec_time, - calls, - interval '1 millisecond' * (shared_blk_read_time + shared_blk_write_time) AS sync_io_time - FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1) - AND query NOT LIKE '/* ECTO_PSQL_EXTRAS:%' - ORDER BY total_exec_time DESC - LIMIT <%= limit %>; - """ - |> EEx.eval_string(args) + SELECT query AS query, + interval '1 millisecond' * total_exec_time AS exec_time, + (total_exec_time/sum(total_exec_time) OVER()) AS prop_exec_time, + calls, + interval '1 millisecond' * (shared_blk_read_time + shared_blk_write_time) AS sync_io_time + FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1) + AND query NOT LIKE '/* ECTO_PSQL_EXTRAS:%' + ORDER BY total_exec_time DESC + LIMIT $1; + """, [args[:limit]]} end end diff --git a/lib/queries/outliers_legacy.ex b/lib/queries/outliers_legacy.ex index 6d96f36..0f96fff 100644 --- a/lib/queries/outliers_legacy.ex +++ b/lib/queries/outliers_legacy.ex @@ -18,19 +18,18 @@ defmodule EctoPSQLExtras.OutliersLegacy do end def query(args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Queries that have longest execution time in aggregate */ + {""" + /* ECTO_PSQL_EXTRAS: Queries that have longest execution time in aggregate */ - SELECT query AS query, - interval '1 millisecond' * total_time AS exec_time, - (total_time/sum(total_time) OVER()) AS prop_exec_time, - calls, - interval '1 millisecond' * (blk_read_time + blk_write_time) AS sync_io_time - FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1) - AND query NOT LIKE '/* ECTO_PSQL_EXTRAS:%' - ORDER BY total_time DESC - LIMIT <%= limit %>; - """ - |> EEx.eval_string(args) + SELECT query AS query, + interval '1 millisecond' * total_time AS exec_time, + (total_time/sum(total_time) OVER()) AS prop_exec_time, + calls, + interval '1 millisecond' * (blk_read_time + blk_write_time) AS sync_io_time + FROM pg_stat_statements WHERE userid = (SELECT usesysid FROM pg_user WHERE usename = current_user LIMIT 1) + AND query NOT LIKE '/* ECTO_PSQL_EXTRAS:%' + ORDER BY total_time DESC + LIMIT $1; + """, [args[:limit]]} end end diff --git a/lib/queries/records_rank.ex b/lib/queries/records_rank.ex index a5ed096..6c63ddf 100644 --- a/lib/queries/records_rank.ex +++ b/lib/queries/records_rank.ex @@ -15,16 +15,16 @@ defmodule EctoPSQLExtras.RecordsRank do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: All tables and the number of rows in each ordered by number of rows descending */ + {""" + /* ECTO_PSQL_EXTRAS: All tables and the number of rows in each ordered by number of rows descending */ - SELECT - schemaname AS schema, relname AS name, - n_live_tup AS estimated_count - FROM - pg_stat_user_tables - ORDER BY - n_live_tup DESC; - """ + SELECT + schemaname AS schema, relname AS name, + n_live_tup AS estimated_count + FROM + pg_stat_user_tables + ORDER BY + n_live_tup DESC; + """, []} end end diff --git a/lib/queries/seq_scans.ex b/lib/queries/seq_scans.ex index eab8ec0..3ee4c7d 100644 --- a/lib/queries/seq_scans.ex +++ b/lib/queries/seq_scans.ex @@ -15,14 +15,14 @@ defmodule EctoPSQLExtras.SeqScans do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Count of sequential scans by table descending by order */ + {""" + /* ECTO_PSQL_EXTRAS: Count of sequential scans by table descending by order */ - SELECT schemaname AS schema, relname AS name, - seq_scan as count - FROM - pg_stat_user_tables - ORDER BY seq_scan DESC; - """ + SELECT schemaname AS schema, relname AS name, + seq_scan as count + FROM + pg_stat_user_tables + ORDER BY seq_scan DESC; + """, []} end end diff --git a/lib/queries/ssl_used.ex b/lib/queries/ssl_used.ex index 1e7fdcf..20c1af7 100644 --- a/lib/queries/ssl_used.ex +++ b/lib/queries/ssl_used.ex @@ -12,9 +12,9 @@ defmodule EctoPSQLExtras.SSLUsed do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Check if SSL connection is used */ - SELECT ssl_is_used(); - """ + {""" + /* ECTO_PSQL_EXTRAS: Check if SSL connection is used */ + SELECT ssl_is_used(); + """, []} end end diff --git a/lib/queries/table_cache_hit.ex b/lib/queries/table_cache_hit.ex index 2779ea6..fc84ba6 100644 --- a/lib/queries/table_cache_hit.ex +++ b/lib/queries/table_cache_hit.ex @@ -18,22 +18,22 @@ defmodule EctoPSQLExtras.TableCacheHit do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Calculates your cache hit rate for reading tables */ + {""" + /* ECTO_PSQL_EXTRAS: Calculates your cache hit rate for reading tables */ - SELECT - schemaname AS schema, relname AS name, - heap_blks_hit AS buffer_hits, - heap_blks_read AS block_reads, - heap_blks_hit + heap_blks_read AS total_read, - CASE (heap_blks_hit + heap_blks_read)::float - WHEN 0 THEN NULL - ELSE (heap_blks_hit / (heap_blks_hit + heap_blks_read)::float) - END ratio - FROM - pg_statio_user_tables - ORDER BY - heap_blks_hit / (heap_blks_hit + heap_blks_read + 1)::float DESC; - """ + SELECT + schemaname AS schema, relname AS name, + heap_blks_hit AS buffer_hits, + heap_blks_read AS block_reads, + heap_blks_hit + heap_blks_read AS total_read, + CASE (heap_blks_hit + heap_blks_read)::float + WHEN 0 THEN NULL + ELSE (heap_blks_hit / (heap_blks_hit + heap_blks_read)::float) + END ratio + FROM + pg_statio_user_tables + ORDER BY + heap_blks_hit / (heap_blks_hit + heap_blks_read + 1)::float DESC; + """, []} end end diff --git a/lib/queries/table_foreign_keys.ex b/lib/queries/table_foreign_keys.ex index 324d84b..245eb52 100644 --- a/lib/queries/table_foreign_keys.ex +++ b/lib/queries/table_foreign_keys.ex @@ -16,45 +16,44 @@ defmodule EctoPSQLExtras.TableForeignKeys do def query(args \\ []) do if args[:table_name] do - """ - /* ECTO_PSQL_EXTRAS: Foreign keys info for a specific table */ + {""" + /* ECTO_PSQL_EXTRAS: Foreign keys info for a specific table */ - SELECT - conrelid::regclass::text AS table_name, - conname AS constraint_name, - a.attname AS column_name, - confrelid::regclass::text AS foreign_table_name, - af.attname AS foreign_column_name - FROM - pg_constraint AS c - JOIN - pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid - JOIN - pg_attribute AS af ON af.attnum = ANY(c.confkey) AND af.attrelid = c.confrelid - WHERE - c.contype = 'f' - AND c.conrelid = '<%= table_name %>'::regclass; - """ - |> EEx.eval_string(args) + SELECT + conrelid::regclass::text AS table_name, + conname AS constraint_name, + a.attname AS column_name, + confrelid::regclass::text AS foreign_table_name, + af.attname AS foreign_column_name + FROM + pg_constraint AS c + JOIN + pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid + JOIN + pg_attribute AS af ON af.attnum = ANY(c.confkey) AND af.attrelid = c.confrelid + WHERE + c.contype = 'f' + AND c.conrelid = $1::text::regclass; + """, [to_string(args[:table_name])]} else - """ - /* ECTO_PSQL_EXTRAS: Foreign keys info for all tables */ + {""" + /* ECTO_PSQL_EXTRAS: Foreign keys info for all tables */ - SELECT - conrelid::regclass::text AS table_name, - conname AS constraint_name, - a.attname AS column_name, - confrelid::regclass::text AS foreign_table_name, - af.attname AS foreign_column_name - FROM - pg_constraint AS c - JOIN - pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid - JOIN - pg_attribute AS af ON af.attnum = ANY(c.confkey) AND af.attrelid = c.confrelid - WHERE - c.contype = 'f'; - """ + SELECT + conrelid::regclass::text AS table_name, + conname AS constraint_name, + a.attname AS column_name, + confrelid::regclass::text AS foreign_table_name, + af.attname AS foreign_column_name + FROM + pg_constraint AS c + JOIN + pg_attribute AS a ON a.attnum = ANY(c.conkey) AND a.attrelid = c.conrelid + JOIN + pg_attribute AS af ON af.attnum = ANY(c.confkey) AND af.attrelid = c.confrelid + WHERE + c.contype = 'f'; + """, []} end end end diff --git a/lib/queries/table_indexes_size.ex b/lib/queries/table_indexes_size.ex index 24ac61b..216f10e 100644 --- a/lib/queries/table_indexes_size.ex +++ b/lib/queries/table_indexes_size.ex @@ -15,16 +15,16 @@ defmodule EctoPSQLExtras.TableIndexesSize do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Total size of all the indexes on each table, descending by size */ + {""" + /* ECTO_PSQL_EXTRAS: Total size of all the indexes on each table, descending by size */ - SELECT n.nspname AS schema, c.relname AS table, pg_indexes_size(c.oid) AS index_size - FROM pg_class c - LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) - WHERE n.nspname NOT IN ('pg_catalog', 'information_schema') - AND n.nspname !~ '^pg_toast' - AND c.relkind IN ('r', 'm') - ORDER BY pg_indexes_size(c.oid) DESC; - """ + SELECT n.nspname AS schema, c.relname AS table, pg_indexes_size(c.oid) AS index_size + FROM pg_class c + LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) + WHERE n.nspname NOT IN ('pg_catalog', 'information_schema') + AND n.nspname !~ '^pg_toast' + AND c.relkind IN ('r', 'm') + ORDER BY pg_indexes_size(c.oid) DESC; + """, []} end end diff --git a/lib/queries/table_schema.ex b/lib/queries/table_schema.ex index f55bf2a..db7d5f8 100644 --- a/lib/queries/table_schema.ex +++ b/lib/queries/table_schema.ex @@ -16,21 +16,20 @@ defmodule EctoPSQLExtras.TableSchema do def query(args \\ []) do if args[:table_name] do - """ - /* ECTO_PSQL_EXTRAS: Table column names and types */ + {""" + /* ECTO_PSQL_EXTRAS: Table column names and types */ - SELECT column_name, data_type, is_nullable, column_default, table_name - FROM information_schema.columns - WHERE table_name = '<%= table_name %>'; - """ - |> EEx.eval_string(args) + SELECT column_name, data_type, is_nullable, column_default, table_name + FROM information_schema.columns + WHERE table_name = $1; + """, [to_string(args[:table_name])]} else - """ - /* ECTO_PSQL_EXTRAS: All database column names and types */ + {""" + /* ECTO_PSQL_EXTRAS: All database column names and types */ - SELECT column_name, data_type, is_nullable, column_default, table_name - FROM information_schema.columns; - """ + SELECT column_name, data_type, is_nullable, column_default, table_name + FROM information_schema.columns; + """, []} end end end diff --git a/lib/queries/table_size.ex b/lib/queries/table_size.ex index 2758aed..8449670 100644 --- a/lib/queries/table_size.ex +++ b/lib/queries/table_size.ex @@ -15,16 +15,16 @@ defmodule EctoPSQLExtras.TableSize do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Size of the tables (excluding indexes), descending by size */ + {""" + /* ECTO_PSQL_EXTRAS: Size of the tables (excluding indexes), descending by size */ - SELECT n.nspname AS schema, c.relname AS name, pg_table_size(c.oid) AS size - FROM pg_class c - LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) - WHERE n.nspname NOT IN ('pg_catalog', 'information_schema') - AND n.nspname !~ '^pg_toast' - AND c.relkind IN ('r', 'm') - ORDER BY pg_table_size(c.oid) DESC; - """ + SELECT n.nspname AS schema, c.relname AS name, pg_table_size(c.oid) AS size + FROM pg_class c + LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) + WHERE n.nspname NOT IN ('pg_catalog', 'information_schema') + AND n.nspname !~ '^pg_toast' + AND c.relkind IN ('r', 'm') + ORDER BY pg_table_size(c.oid) DESC; + """, []} end end diff --git a/lib/queries/total_index_size.ex b/lib/queries/total_index_size.ex index da9f9cf..7082fd1 100644 --- a/lib/queries/total_index_size.ex +++ b/lib/queries/total_index_size.ex @@ -12,15 +12,15 @@ defmodule EctoPSQLExtras.TotalIndexSize do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Total size of all indexes in MB */ + {""" + /* ECTO_PSQL_EXTRAS: Total size of all indexes in MB */ - SELECT sum(c.relpages::bigint*8192)::bigint AS size - FROM pg_class c - LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) - WHERE n.nspname NOT IN ('pg_catalog', 'information_schema') - AND n.nspname !~ '^pg_toast' - AND c.relkind='i'; - """ + SELECT sum(c.relpages::bigint*8192)::bigint AS size + FROM pg_class c + LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) + WHERE n.nspname NOT IN ('pg_catalog', 'information_schema') + AND n.nspname !~ '^pg_toast' + AND c.relkind='i'; + """, []} end end diff --git a/lib/queries/total_table_size.ex b/lib/queries/total_table_size.ex index e589b57..0201c8c 100644 --- a/lib/queries/total_table_size.ex +++ b/lib/queries/total_table_size.ex @@ -15,16 +15,16 @@ defmodule EctoPSQLExtras.TotalTableSize do end def query(_args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Size of the tables (including indexes), descending by size */ + {""" + /* ECTO_PSQL_EXTRAS: Size of the tables (including indexes), descending by size */ - SELECT n.nspname AS schema, c.relname AS name, pg_total_relation_size(c.oid) AS size - FROM pg_class c - LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) - WHERE n.nspname NOT IN ('pg_catalog', 'information_schema') - AND n.nspname !~ '^pg_toast' - AND c.relkind IN ('r', 'm') - ORDER BY pg_total_relation_size(c.oid) DESC; - """ + SELECT n.nspname AS schema, c.relname AS name, pg_total_relation_size(c.oid) AS size + FROM pg_class c + LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace) + WHERE n.nspname NOT IN ('pg_catalog', 'information_schema') + AND n.nspname !~ '^pg_toast' + AND c.relkind IN ('r', 'm') + ORDER BY pg_total_relation_size(c.oid) DESC; + """, []} end end diff --git a/lib/queries/unused_indexes.ex b/lib/queries/unused_indexes.ex index f8b7493..0be7a1d 100644 --- a/lib/queries/unused_indexes.ex +++ b/lib/queries/unused_indexes.ex @@ -17,25 +17,24 @@ defmodule EctoPSQLExtras.UnusedIndexes do end def query(args \\ []) do - """ - /* ECTO_PSQL_EXTRAS: Unused and almost unused indexes */ - /* Ordered by their size relative to the number of index scans. - Exclude indexes of very small tables (less than 5 pages), - where the planner will almost invariably select a sequential scan, - but may not in the future as the table grows */ + {""" + /* ECTO_PSQL_EXTRAS: Unused and almost unused indexes */ + /* Ordered by their size relative to the number of index scans. + Exclude indexes of very small tables (less than 5 pages), + where the planner will almost invariably select a sequential scan, + but may not in the future as the table grows */ - SELECT - schemaname AS schema, - relname AS table, - indexrelname AS index, - pg_relation_size(i.indexrelid) AS index_size, - idx_scan as index_scans - FROM pg_stat_user_indexes ui - JOIN pg_index i ON ui.indexrelid = i.indexrelid - WHERE NOT indisunique AND idx_scan < <%= min_scans %> AND pg_relation_size(relid) > 5 * 8192 - ORDER BY pg_relation_size(i.indexrelid) / nullif(idx_scan, 0) DESC NULLS FIRST, - pg_relation_size(i.indexrelid) DESC; - """ - |> EEx.eval_string(args) + SELECT + schemaname AS schema, + relname AS table, + indexrelname AS index, + pg_relation_size(i.indexrelid) AS index_size, + idx_scan as index_scans + FROM pg_stat_user_indexes ui + JOIN pg_index i ON ui.indexrelid = i.indexrelid + WHERE NOT indisunique AND idx_scan < $1 AND pg_relation_size(relid) > 5 * 8192 + ORDER BY pg_relation_size(i.indexrelid) / nullif(idx_scan, 0) DESC NULLS FIRST, + pg_relation_size(i.indexrelid) DESC; + """, [args[:min_scans]]} end end diff --git a/lib/queries/vacuum_stats.ex b/lib/queries/vacuum_stats.ex index 37fdd5d..9919fb7 100644 --- a/lib/queries/vacuum_stats.ex +++ b/lib/queries/vacuum_stats.ex @@ -19,47 +19,47 @@ defmodule EctoPSQLExtras.VacuumStats do end def query(_args \\ %{}) do - """ - /* ECTO_PSQL_EXTRAS: Dead rows and whether an automatic vacuum is expected to be triggered */ + {""" + /* ECTO_PSQL_EXTRAS: Dead rows and whether an automatic vacuum is expected to be triggered */ - WITH table_opts AS ( - SELECT - pg_class.oid, relname, nspname, array_to_string(reloptions, '') AS relopts - FROM - pg_class INNER JOIN pg_namespace ns ON relnamespace = ns.oid - ), vacuum_settings AS ( - SELECT - oid, relname, nspname, - CASE - WHEN relopts LIKE '%autovacuum_vacuum_threshold%' - THEN substring(relopts, '.*autovacuum_vacuum_threshold=([0-9.]+).*')::integer - ELSE current_setting('autovacuum_vacuum_threshold')::integer - END AS autovacuum_vacuum_threshold, - CASE - WHEN relopts LIKE '%autovacuum_vacuum_scale_factor%' - THEN substring(relopts, '.*autovacuum_vacuum_scale_factor=([0-9.]+).*')::real - ELSE current_setting('autovacuum_vacuum_scale_factor')::real - END AS autovacuum_vacuum_scale_factor - FROM - table_opts - ) - SELECT - vacuum_settings.nspname AS schema, - vacuum_settings.relname AS table, - to_char(psut.last_vacuum, 'YYYY-MM-DD HH24:MI') AS last_vacuum, - to_char(psut.last_autovacuum, 'YYYY-MM-DD HH24:MI') AS last_autovacuum, - to_char(pg_class.reltuples, '9G999G999G999') AS rowcount, - to_char(psut.n_dead_tup, '9G999G999G999') AS dead_rowcount, - to_char(autovacuum_vacuum_threshold - + (autovacuum_vacuum_scale_factor::numeric * pg_class.reltuples), '9G999G999G999') AS autovacuum_threshold, - CASE - WHEN autovacuum_vacuum_threshold + (autovacuum_vacuum_scale_factor::numeric * pg_class.reltuples) < psut.n_dead_tup - THEN 'yes' - END AS expect_autovacuum - FROM - pg_stat_user_tables psut INNER JOIN pg_class ON psut.relid = pg_class.oid - INNER JOIN vacuum_settings ON pg_class.oid = vacuum_settings.oid - ORDER BY 1; - """ + WITH table_opts AS ( + SELECT + pg_class.oid, relname, nspname, array_to_string(reloptions, '') AS relopts + FROM + pg_class INNER JOIN pg_namespace ns ON relnamespace = ns.oid + ), vacuum_settings AS ( + SELECT + oid, relname, nspname, + CASE + WHEN relopts LIKE '%autovacuum_vacuum_threshold%' + THEN substring(relopts, '.*autovacuum_vacuum_threshold=([0-9.]+).*')::integer + ELSE current_setting('autovacuum_vacuum_threshold')::integer + END AS autovacuum_vacuum_threshold, + CASE + WHEN relopts LIKE '%autovacuum_vacuum_scale_factor%' + THEN substring(relopts, '.*autovacuum_vacuum_scale_factor=([0-9.]+).*')::real + ELSE current_setting('autovacuum_vacuum_scale_factor')::real + END AS autovacuum_vacuum_scale_factor + FROM + table_opts + ) + SELECT + vacuum_settings.nspname AS schema, + vacuum_settings.relname AS table, + to_char(psut.last_vacuum, 'YYYY-MM-DD HH24:MI') AS last_vacuum, + to_char(psut.last_autovacuum, 'YYYY-MM-DD HH24:MI') AS last_autovacuum, + to_char(pg_class.reltuples, '9G999G999G999') AS rowcount, + to_char(psut.n_dead_tup, '9G999G999G999') AS dead_rowcount, + to_char(autovacuum_vacuum_threshold + + (autovacuum_vacuum_scale_factor::numeric * pg_class.reltuples), '9G999G999G999') AS autovacuum_threshold, + CASE + WHEN autovacuum_vacuum_threshold + (autovacuum_vacuum_scale_factor::numeric * pg_class.reltuples) < psut.n_dead_tup + THEN 'yes' + END AS expect_autovacuum + FROM + pg_stat_user_tables psut INNER JOIN pg_class ON psut.relid = pg_class.oid + INNER JOIN vacuum_settings ON pg_class.oid = vacuum_settings.oid + ORDER BY 1; + """, []} end end