pg_reports 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +130 -0
  3. data/README.md +170 -4
  4. data/app/controllers/pg_reports/dashboard_controller.rb +348 -47
  5. data/app/views/layouts/pg_reports/application.html.erb +370 -79
  6. data/app/views/pg_reports/dashboard/_show_modals.html.erb +1 -1
  7. data/app/views/pg_reports/dashboard/_show_scripts.html.erb +254 -29
  8. data/app/views/pg_reports/dashboard/_show_styles.html.erb +373 -0
  9. data/app/views/pg_reports/dashboard/index.html.erb +485 -5
  10. data/config/locales/en.yml +45 -0
  11. data/config/locales/ru.yml +45 -0
  12. data/config/routes.rb +8 -0
  13. data/lib/pg_reports/configuration.rb +21 -0
  14. data/lib/pg_reports/dashboard/reports_registry.rb +24 -1
  15. data/lib/pg_reports/definitions/connections/connection_churn.yml +49 -0
  16. data/lib/pg_reports/definitions/connections/pool_saturation.yml +42 -0
  17. data/lib/pg_reports/definitions/connections/pool_usage.yml +43 -0
  18. data/lib/pg_reports/definitions/connections/pool_wait_times.yml +44 -0
  19. data/lib/pg_reports/definitions/queries/missing_index_queries.yml +3 -3
  20. data/lib/pg_reports/explain_analyzer.rb +338 -0
  21. data/lib/pg_reports/modules/schema_analysis.rb +4 -6
  22. data/lib/pg_reports/modules/system.rb +26 -3
  23. data/lib/pg_reports/query_monitor.rb +293 -0
  24. data/lib/pg_reports/sql/connections/connection_churn.sql +37 -0
  25. data/lib/pg_reports/sql/connections/pool_saturation.sql +90 -0
  26. data/lib/pg_reports/sql/connections/pool_usage.sql +31 -0
  27. data/lib/pg_reports/sql/connections/pool_wait_times.sql +19 -0
  28. data/lib/pg_reports/sql/queries/all_queries.sql +17 -15
  29. data/lib/pg_reports/sql/queries/expensive_queries.sql +9 -4
  30. data/lib/pg_reports/sql/queries/heavy_queries.sql +14 -12
  31. data/lib/pg_reports/sql/queries/low_cache_hit_queries.sql +16 -14
  32. data/lib/pg_reports/sql/queries/missing_index_queries.sql +18 -16
  33. data/lib/pg_reports/sql/queries/slow_queries.sql +14 -12
  34. data/lib/pg_reports/sql/system/databases_list.sql +8 -0
  35. data/lib/pg_reports/version.rb +1 -1
  36. data/lib/pg_reports.rb +2 -0
  37. metadata +55 -2
@@ -1264,4 +1264,377 @@
1264
1264
  border-color: var(--accent-blue);
1265
1265
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
1266
1266
  }
1267
+
1268
+ /* ==========================================
1269
+ EXPLAIN ANALYZE STYLES
1270
+ ========================================== */
1271
+
1272
+ /* Summary card at top */
1273
+ .explain-summary {
1274
+ background: var(--bg-card);
1275
+ border-radius: 12px;
1276
+ padding: 1.25rem;
1277
+ margin-bottom: 1.5rem;
1278
+ border-left: 4px solid var(--accent-blue);
1279
+ }
1280
+
1281
+ .explain-summary-good {
1282
+ border-left-color: var(--accent-green);
1283
+ background: linear-gradient(135deg, rgba(16, 185, 129, 0.05) 0%, var(--bg-card) 100%);
1284
+ }
1285
+
1286
+ .explain-summary-warning {
1287
+ border-left-color: var(--accent-amber);
1288
+ background: linear-gradient(135deg, rgba(245, 158, 11, 0.05) 0%, var(--bg-card) 100%);
1289
+ }
1290
+
1291
+ .explain-summary-critical {
1292
+ border-left-color: #ef4444;
1293
+ background: linear-gradient(135deg, rgba(239, 68, 68, 0.05) 0%, var(--bg-card) 100%);
1294
+ }
1295
+
1296
+ .explain-summary-header {
1297
+ display: flex;
1298
+ align-items: center;
1299
+ gap: 0.75rem;
1300
+ margin-bottom: 0.75rem;
1301
+ }
1302
+
1303
+ .explain-summary-icon {
1304
+ font-size: 1.5rem;
1305
+ }
1306
+
1307
+ .explain-summary-title {
1308
+ font-size: 1.125rem;
1309
+ font-weight: 600;
1310
+ color: var(--text-primary);
1311
+ }
1312
+
1313
+ .explain-summary-stats {
1314
+ display: flex;
1315
+ gap: 1rem;
1316
+ flex-wrap: wrap;
1317
+ }
1318
+
1319
+ .explain-summary-stat {
1320
+ font-size: 0.875rem;
1321
+ padding: 0.375rem 0.75rem;
1322
+ border-radius: 6px;
1323
+ font-weight: 500;
1324
+ }
1325
+
1326
+ .explain-summary-stat.critical {
1327
+ background: rgba(239, 68, 68, 0.1);
1328
+ color: #f87171;
1329
+ }
1330
+
1331
+ .explain-summary-stat.warning {
1332
+ background: rgba(245, 158, 11, 0.1);
1333
+ color: var(--accent-amber);
1334
+ }
1335
+
1336
+ /* Stats grid */
1337
+ .explain-stats {
1338
+ display: grid;
1339
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
1340
+ gap: 1rem;
1341
+ margin-bottom: 1.5rem;
1342
+ }
1343
+
1344
+ .explain-stat {
1345
+ background: var(--bg-card);
1346
+ border: 1px solid var(--border-color);
1347
+ border-radius: 10px;
1348
+ padding: 1rem;
1349
+ display: flex;
1350
+ flex-direction: column;
1351
+ gap: 0.5rem;
1352
+ transition: all 0.15s;
1353
+ }
1354
+
1355
+ .explain-stat:hover {
1356
+ border-color: var(--accent-blue);
1357
+ transform: translateY(-2px);
1358
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
1359
+ }
1360
+
1361
+ .explain-stat.stat-warning {
1362
+ border-color: var(--accent-amber);
1363
+ background: linear-gradient(135deg, rgba(245, 158, 11, 0.03) 0%, var(--bg-card) 100%);
1364
+ }
1365
+
1366
+ .explain-stat.stat-critical {
1367
+ border-color: #ef4444;
1368
+ background: linear-gradient(135deg, rgba(239, 68, 68, 0.03) 0%, var(--bg-card) 100%);
1369
+ }
1370
+
1371
+ .explain-stat-label {
1372
+ font-size: 0.8rem;
1373
+ color: var(--text-secondary);
1374
+ text-transform: uppercase;
1375
+ letter-spacing: 0.05em;
1376
+ font-weight: 500;
1377
+ }
1378
+
1379
+ .explain-stat-value {
1380
+ font-size: 1.5rem;
1381
+ font-weight: 600;
1382
+ color: var(--text-primary);
1383
+ font-family: 'SF Mono', Monaco, Consolas, monospace;
1384
+ }
1385
+
1386
+ /* Problems list */
1387
+ .explain-problems {
1388
+ background: var(--bg-card);
1389
+ border: 1px solid var(--border-color);
1390
+ border-radius: 12px;
1391
+ padding: 1.25rem;
1392
+ margin-bottom: 1.5rem;
1393
+ }
1394
+
1395
+ .explain-problems-header {
1396
+ font-size: 1rem;
1397
+ font-weight: 600;
1398
+ color: var(--text-primary);
1399
+ margin-bottom: 1rem;
1400
+ display: flex;
1401
+ align-items: center;
1402
+ gap: 0.5rem;
1403
+ }
1404
+
1405
+ .explain-problems-list {
1406
+ display: flex;
1407
+ flex-direction: column;
1408
+ gap: 0.75rem;
1409
+ }
1410
+
1411
+ .explain-problem {
1412
+ background: var(--bg-tertiary);
1413
+ border-left: 3px solid var(--border-color);
1414
+ border-radius: 8px;
1415
+ padding: 1rem;
1416
+ transition: all 0.15s;
1417
+ }
1418
+
1419
+ .explain-problem:hover {
1420
+ transform: translateX(4px);
1421
+ }
1422
+
1423
+ .explain-problem.problem-critical {
1424
+ border-left-color: #ef4444;
1425
+ background: linear-gradient(90deg, rgba(239, 68, 68, 0.05) 0%, var(--bg-tertiary) 15%);
1426
+ }
1427
+
1428
+ .explain-problem.problem-warning {
1429
+ border-left-color: var(--accent-amber);
1430
+ background: linear-gradient(90deg, rgba(245, 158, 11, 0.05) 0%, var(--bg-tertiary) 15%);
1431
+ }
1432
+
1433
+ .explain-problem.problem-info {
1434
+ border-left-color: var(--accent-blue);
1435
+ background: linear-gradient(90deg, rgba(59, 130, 246, 0.05) 0%, var(--bg-tertiary) 15%);
1436
+ }
1437
+
1438
+ .explain-problem-header {
1439
+ display: flex;
1440
+ align-items: center;
1441
+ gap: 0.75rem;
1442
+ margin-bottom: 0.5rem;
1443
+ flex-wrap: wrap;
1444
+ }
1445
+
1446
+ .explain-problem-icon {
1447
+ font-size: 1.125rem;
1448
+ }
1449
+
1450
+ .explain-problem-message {
1451
+ font-weight: 600;
1452
+ color: var(--text-primary);
1453
+ flex: 1;
1454
+ }
1455
+
1456
+ .explain-problem-line {
1457
+ font-size: 0.75rem;
1458
+ color: var(--text-secondary);
1459
+ background: var(--bg-primary);
1460
+ padding: 0.25rem 0.5rem;
1461
+ border-radius: 4px;
1462
+ font-family: 'SF Mono', Monaco, Consolas, monospace;
1463
+ }
1464
+
1465
+ .explain-problem-details {
1466
+ font-size: 0.875rem;
1467
+ color: var(--text-secondary);
1468
+ margin-bottom: 0.5rem;
1469
+ margin-left: 2rem;
1470
+ }
1471
+
1472
+ .explain-problem-recommendation {
1473
+ font-size: 0.875rem;
1474
+ color: var(--accent-blue);
1475
+ background: rgba(59, 130, 246, 0.05);
1476
+ padding: 0.5rem 0.75rem;
1477
+ border-radius: 6px;
1478
+ margin-top: 0.5rem;
1479
+ }
1480
+
1481
+ /* EXPLAIN output display */
1482
+ .explain-output {
1483
+ background: var(--bg-card);
1484
+ border: 1px solid var(--border-color);
1485
+ border-radius: 12px;
1486
+ overflow: hidden;
1487
+ }
1488
+
1489
+ .explain-output-header {
1490
+ background: var(--bg-tertiary);
1491
+ padding: 0.875rem 1.25rem;
1492
+ font-weight: 600;
1493
+ color: var(--text-primary);
1494
+ border-bottom: 1px solid var(--border-color);
1495
+ display: flex;
1496
+ align-items: center;
1497
+ justify-content: space-between;
1498
+ }
1499
+
1500
+ .btn-copy-small {
1501
+ font-size: 0.8rem;
1502
+ padding: 0.375rem 0.625rem;
1503
+ border: 1px solid var(--border-color);
1504
+ background: var(--bg-primary);
1505
+ color: var(--text-secondary);
1506
+ border-radius: 6px;
1507
+ cursor: pointer;
1508
+ transition: all 0.15s;
1509
+ }
1510
+
1511
+ .btn-copy-small:hover {
1512
+ background: var(--bg-secondary);
1513
+ border-color: var(--accent-blue);
1514
+ color: var(--accent-blue);
1515
+ }
1516
+
1517
+ .explain-lines {
1518
+ font-family: 'SF Mono', Monaco, Consolas, monospace;
1519
+ font-size: 0.875rem;
1520
+ line-height: 1.6;
1521
+ overflow-x: auto;
1522
+ }
1523
+
1524
+ .explain-line {
1525
+ display: flex;
1526
+ align-items: stretch;
1527
+ border-bottom: 1px solid transparent;
1528
+ transition: all 0.1s;
1529
+ min-height: 1.6rem;
1530
+ }
1531
+
1532
+ .explain-line:hover {
1533
+ background: var(--bg-tertiary);
1534
+ }
1535
+
1536
+ .explain-line-problem {
1537
+ background: rgba(245, 158, 11, 0.03);
1538
+ }
1539
+
1540
+ .explain-line-problem.problem-critical {
1541
+ background: rgba(239, 68, 68, 0.03);
1542
+ border-left: 3px solid #ef4444;
1543
+ }
1544
+
1545
+ .explain-line-problem.problem-warning {
1546
+ background: rgba(245, 158, 11, 0.03);
1547
+ border-left: 3px solid var(--accent-amber);
1548
+ }
1549
+
1550
+ .explain-line-timing {
1551
+ background: var(--bg-tertiary);
1552
+ font-weight: 600;
1553
+ border-top: 1px solid var(--border-color);
1554
+ }
1555
+
1556
+ .explain-line-number {
1557
+ display: inline-block;
1558
+ min-width: 3rem;
1559
+ padding: 0.25rem 0.75rem;
1560
+ text-align: right;
1561
+ color: var(--text-tertiary);
1562
+ background: var(--bg-secondary);
1563
+ border-right: 1px solid var(--border-color);
1564
+ user-select: none;
1565
+ font-size: 0.75rem;
1566
+ }
1567
+
1568
+ .explain-line-content {
1569
+ flex: 1;
1570
+ padding: 0.25rem 1rem;
1571
+ color: var(--text-secondary);
1572
+ white-space: pre;
1573
+ }
1574
+
1575
+ .explain-line-problem-indicator {
1576
+ padding: 0.25rem 0.75rem;
1577
+ font-size: 0.875rem;
1578
+ cursor: help;
1579
+ }
1580
+
1581
+ /* Node type highlighting */
1582
+ .explain-node-type {
1583
+ font-weight: 600;
1584
+ color: var(--text-primary);
1585
+ }
1586
+
1587
+ /* Node color coding */
1588
+ .node-good {
1589
+ color: var(--accent-green);
1590
+ }
1591
+
1592
+ .node-good .explain-node-type {
1593
+ color: var(--accent-green);
1594
+ }
1595
+
1596
+ .node-ok {
1597
+ color: var(--accent-blue);
1598
+ }
1599
+
1600
+ .node-ok .explain-node-type {
1601
+ color: var(--accent-blue);
1602
+ }
1603
+
1604
+ .node-warning {
1605
+ color: var(--accent-amber);
1606
+ }
1607
+
1608
+ .node-warning .explain-node-type {
1609
+ color: var(--accent-amber);
1610
+ font-weight: 700;
1611
+ }
1612
+
1613
+ .node-neutral {
1614
+ color: var(--text-secondary);
1615
+ }
1616
+
1617
+ /* Metric highlighting */
1618
+ .explain-metric {
1619
+ color: var(--text-tertiary);
1620
+ }
1621
+
1622
+ .metric-value {
1623
+ color: var(--accent-purple);
1624
+ font-weight: 600;
1625
+ }
1626
+
1627
+ /* Fallback for raw explain output */
1628
+ .explain-result {
1629
+ font-family: 'SF Mono', Monaco, Consolas, monospace;
1630
+ font-size: 0.875rem;
1631
+ line-height: 1.6;
1632
+ white-space: pre;
1633
+ overflow-x: auto;
1634
+ background: var(--bg-card);
1635
+ border: 1px solid var(--border-color);
1636
+ border-radius: 12px;
1637
+ padding: 1.25rem;
1638
+ color: var(--text-secondary);
1639
+ }
1267
1640
  </style>