209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
|
# File 'lib/pg_metrics/metrics.rb', line 209
def self.database_metrics(server_version)
{
Functions => {
prefix: %w(function),
query: Gem::Version.new(server_version) >= Gem::Version.new('8.4') \
? %q{SELECT schemaname AS key,
array_to_string(ARRAY[funcname, '-', pronargs::TEXT,
CASE WHEN pronargs = 0 THEN ''
ELSE '-' || array_to_string(CASE WHEN pronargs > 16
THEN ARRAY(SELECT args[i]
FROM generate_series(1, 8) AS _(i))
|| '-'::TEXT
|| ARRAY(SELECT args[i]
FROM generate_series(pronargs - 7, pronargs) AS _ (i))
|| funcid::TEXT
ELSE args END, '-') END], '') AS key2,
calls, total_time, self_time
FROM (SELECT funcid, schemaname, funcname::TEXT, pronargs,
ARRAY(SELECT typname::TEXT
FROM pg_type
JOIN (SELECT args.i, proargtypes[args.i] AS typid
FROM pg_catalog.generate_series(0, array_upper(proargtypes, 1)) AS args (i))
AS args (i, typid) ON typid = pg_type.oid
ORDER BY i) AS args,
calls, total_time, self_time
FROM pg_stat_user_functions
JOIN pg_proc ON pg_proc.oid = funcid
WHERE schemaname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')) AS funcs}
: nil
},
Locks => {
prefix: %w(table),
query: %q{SELECT nspname AS key,
CASE rel.relkind WHEN 'r' THEN rel.relname ELSE crel.relname END AS key2,
CASE rel.relkind WHEN 'r' THEN 'locks' ELSE 'index' END AS key3,
CASE rel.relkind WHEN 'r' THEN mode ELSE rel.relname END AS key4,
CASE rel.relkind WHEN 'r' THEN NULL ELSE 'locks' END AS key5,
CASE rel.relkind WHEN 'r' THEN NULL ELSE mode END AS key6,
count(*) AS value
FROM pg_locks
JOIN pg_database dat ON dat.oid = database
JOIN pg_class rel ON rel.oid = relation
LEFT JOIN pg_index ON indexrelid = rel.oid
LEFT JOIN pg_class crel ON indrelid = crel.oid
JOIN pg_namespace nsp ON nsp.oid = rel.relnamespace
WHERE locktype = 'relation' AND nspname <> 'pg_catalog' AND rel.relkind in ('r', 'i')
GROUP BY 1, 2, 3, 4, 5, 6}
},
TableSizes => {
prefix: %w(table),
query: %q{SELECT n.nspname AS key, r.relname AS key2,
pg_relation_size(r.oid) AS size,
pg_total_relation_size(r.oid) AS total_size
FROM pg_class r
JOIN pg_namespace n ON r.relnamespace = n.oid
WHERE r.relkind = 'r'
AND n.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')}
},
TableFreeSpace => {
prefix: %w(table),
query: Gem::Version.new(server_version) >= Gem::Version.new('8.4') \
? %{SELECT n.nspname AS key, t.relname AS key2,
COALESCE((SELECT sum(pg_freespace.avail) AS sum
FROM pg_freespace(t.oid::regclass) AS pg_freespace(blkno, avail)), 0::bigint) AS free_space
FROM pg_class t
JOIN pg_namespace n ON t.relnamespace = n.oid
WHERE t.relkind = 'r'::"char"
AND n.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')}
: %{SELECT n.nspname AS key, t.relname AS key2, fsm.bytes AS free_space
FROM pg_class t
JOIN pg_namespace n ON t.relnamespace = n.oid
LEFT JOIN (SELECT fsm.relfilenode, sum(fsm.bytes) AS bytes
FROM pg_freespacemap_pages fsm
JOIN pg_database db ON db.oid = fsm.reldatabase
AND db.datname = current_database()
GROUP BY fsm.relfilenode) fsm ON t.relfilenode = fsm.relfilenode
WHERE t.relkind = 'r'::"char"
AND n.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')}
},
IndexSizes => {
prefix: %w(table),
query: %q{SELECT n.nspname AS key, cr.relname AS key2, 'index' AS key3,
ci.relname AS key4, pg_relation_size(ci.oid) AS size
FROM pg_class ci JOIN pg_index i ON ci.oid = i.indexrelid
JOIN pg_class cr ON cr.oid = i.indrelid
JOIN pg_namespace n on ci.relnamespace = n.oid
WHERE ci.relkind = 'i' AND cr.relkind = 'r'
AND n.nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')}
},
IndexIdealSizes => {
prefix: %w(table),
query: %{SELECT pg_namespace.nspname AS key, rel.relname AS key2,
'index' AS key3, idx.relname AS key4,
((ceil(idx.reltuples * ((constants.index_tuple_header_size
+ constants.item_id_data_size
+ CASE WHEN (COALESCE(sum(CASE WHEN statts.staattnotnull THEN 0 ELSE 1 END), 0::bigint)
+ ((SELECT COALESCE(sum(CASE WHEN atts.attnotnull THEN 0 ELSE 1 END), 0::bigint) AS "coalesce"
FROM pg_attribute atts
JOIN (SELECT pg_index.indkey[the.i] AS attnum
FROM generate_series(0, pg_index.indnatts - 1) the(i)) cols ON atts.attnum = cols.attnum
WHERE atts.attrelid = pg_index.indrelid))) > 0
THEN (SELECT the.null_bitmap_size
+ constants.max_align
- CASE WHEN (the.null_bitmap_size % constants.max_align) = 0
THEN constants.max_align
ELSE the.null_bitmap_size % constants.max_align END
FROM (VALUES (ceil(pg_index.indnatts::real / 8)::int)) the (null_bitmap_size))
ELSE 0 END)::double precision
+ COALESCE(sum(statts.stawidth::double precision * (1::double precision - statts.stanullfrac)), 0::double precision)
+ COALESCE((SELECT sum(atts.stawidth::double precision * (1::double precision - atts.stanullfrac)) AS sum
FROM pg_statistic atts
JOIN (SELECT pg_index.indkey[the.i] AS attnum
FROM generate_series(0, pg_index.indnatts - 1) the(i)) cols ON atts.staattnum = cols.attnum
WHERE atts.starelid = pg_index.indrelid), 0::double precision))
/ (constants.block_size - constants.page_header_data_size::numeric - constants.special_space::numeric)::double precision)
+ constants.index_metadata_pages::double precision)
* constants.block_size::double precision)::bigint AS ideal_size
FROM pg_index
JOIN pg_class idx ON pg_index.indexrelid = idx.oid
JOIN pg_class rel ON pg_index.indrelid = rel.oid
JOIN pg_namespace ON idx.relnamespace = pg_namespace.oid
LEFT JOIN (SELECT pg_statistic.starelid, pg_statistic.staattnum, pg_statistic.stanullfrac, pg_statistic.stawidth, pg_attribute.attnotnull AS staattnotnull
FROM pg_statistic
JOIN pg_attribute ON (pg_statistic.starelid,pg_statistic.staattnum) = (pg_attribute.attrelid, pg_attribute.attnum)) statts
ON statts.starelid = idx.oid
CROSS JOIN (SELECT current_setting('block_size'::text)::numeric AS block_size,
CASE WHEN "substring"(version(), 12, 3) = ANY (ARRAY['8.0'::text, '8.1'::text, '8.2'::text]) THEN 27
ELSE 23 END AS tuple_header_size,
CASE WHEN version() ~ 'mingw32'::text THEN 8 ELSE 4 END AS max_align,
8 AS index_tuple_header_size,
4 AS item_id_data_size,
24 AS page_header_data_size,
0 AS special_space,
1 AS index_metadata_pages) AS constants
WHERE nspname NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
GROUP BY pg_namespace.nspname, rel.relname, rel.oid, idx.relname, idx.reltuples, idx.relpages, pg_index.indexrelid,
pg_index.indrelid, pg_index.indkey, pg_index.indnatts,
constants.block_size, constants.tuple_header_size, constants.max_align,
constants.index_tuple_header_size, constants.item_id_data_size, constants.page_header_data_size,
constants.index_metadata_pages, constants.special_space;}
},
TableStatio => {
prefix: %w(table),
query: %q{SELECT schemaname AS key, relname AS key2, 'statio' AS key3,
nullif(heap_blks_read, 0) AS heap_blks_read,
nullif(heap_blks_hit, 0) AS heap_blks_hit,
nullif(idx_blks_read, 0) AS idx_blks_read,
nullif(idx_blks_hit, 0) AS idx_blks_hit,
nullif(toast_blks_read, 0) AS toast_blks_read,
nullif(toast_blks_hit, 0) AS toast_blks_hit,
nullif(tidx_blks_read, 0) AS tidx_blks_read,
nullif(tidx_blks_hit, 0) AS tidx_blks_hit
FROM pg_statio_user_tables}
},
TableStats => {
prefix: %w(table),
query: Gem::Version.new(server_version) >= Gem::Version.new('9.1') \
? %q{SELECT schemaname AS key, relname AS key2, 'stat' AS key3,
nullif(seq_scan, 0) AS seq_scan,
nullif(seq_tup_read, 0) AS seq_tup_read,
nullif(idx_scan, 0) AS idx_scan,
nullif(idx_tup_fetch, 0) AS idx_tup_fetch,
nullif(n_tup_ins, 0) AS n_tup_ins,
nullif(n_tup_upd, 0) AS n_tup_upd,
nullif(n_tup_del, 0) AS n_tup_del,
nullif(n_tup_hot_upd, 0) AS n_tup_hot_upd,
nullif(n_live_tup, 0) AS n_live_tup,
nullif(n_dead_tup, 0) AS n_dead_tup,
nullif(vacuum_count, 0) AS vacuum_count,
nullif(autovacuum_count, 0) AS autovacuum_count,
nullif(analyze_count, 0) AS analyze_count,
nullif(autoanalyze_count, 0) AS autoanalyze_count
FROM pg_stat_user_tables} \
: %q{SELECT schemaname AS key, relname AS key2, 'stat' AS key3,
nullif(seq_scan, 0) AS seq_scan,
nullif(seq_tup_read, 0) AS seq_tup_read,
nullif(idx_scan, 0) AS idx_scan,
nullif(idx_tup_fetch, 0) AS idx_tup_fetch,
nullif(n_tup_ins, 0) AS n_tup_ins,
nullif(n_tup_upd, 0) AS n_tup_upd,
nullif(n_tup_del, 0) AS n_tup_del,
nullif(n_tup_hot_upd, 0) AS n_tup_hot_upd,
nullif(n_live_tup, 0) AS n_live_tup,
nullif(n_dead_tup, 0) AS n_dead_tup
FROM pg_stat_user_tables},
},
IndexStatio => {
prefix: %w(table),
query: %q{SELECT schemaname AS key, relname AS key2, 'index' AS key3,
indexrelname AS key4, 'statio' AS key5,
nullif(idx_blks_read, 0) AS idx_blks_read,
nullif(idx_blks_hit, 0) AS idx_blks_hit
FROM pg_statio_user_indexes},
},
IndexStats => {
prefix: %w(table),
query: %q{SELECT schemaname AS key, relname AS key2, 'index' AS key3,
indexrelname AS key4, 'stat' AS key5,
nullif(idx_scan, 0) AS idx_scan,
nullif(idx_tup_read, 0) AS idx_tup_read,
nullif(idx_tup_fetch, 0) AS idx_tup_fetch
FROM pg_stat_user_indexes}
}
}
end
|