db-storage-manager 1.0.8__py3-none-any.whl

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 (200) hide show
  1. db_storage_manager/__init__.py +7 -0
  2. db_storage_manager/analysis/__init__.py +27 -0
  3. db_storage_manager/analysis/capacity.py +181 -0
  4. db_storage_manager/analysis/optimization.py +155 -0
  5. db_storage_manager/analysis/performance.py +127 -0
  6. db_storage_manager/analysis/prediction.py +169 -0
  7. db_storage_manager/analysis/profiling.py +101 -0
  8. db_storage_manager/analysis/query_suggestions.py +515 -0
  9. db_storage_manager/api/__init__.py +19 -0
  10. db_storage_manager/api/__main__.py +5 -0
  11. db_storage_manager/api/sanitize.py +55 -0
  12. db_storage_manager/api/server.py +529 -0
  13. db_storage_manager/api/service.py +247 -0
  14. db_storage_manager/api/static/app.js +135 -0
  15. db_storage_manager/api/static/index.html +47 -0
  16. db_storage_manager/api/static/style.css +145 -0
  17. db_storage_manager/audit.py +91 -0
  18. db_storage_manager/backups/__init__.py +46 -0
  19. db_storage_manager/backups/base.py +70 -0
  20. db_storage_manager/backups/googledrive.py +276 -0
  21. db_storage_manager/backups/local.py +354 -0
  22. db_storage_manager/backups/manager.py +958 -0
  23. db_storage_manager/backups/pipeline.py +230 -0
  24. db_storage_manager/backups/protondrive.py +1076 -0
  25. db_storage_manager/backups/retention.py +175 -0
  26. db_storage_manager/backups/s3.py +392 -0
  27. db_storage_manager/backups/scheduler.py +406 -0
  28. db_storage_manager/backups/scratch.py +107 -0
  29. db_storage_manager/bundled_plugins/__init__.py +34 -0
  30. db_storage_manager/bundled_plugins/__pycache__/__init__.cpython-312.pyc +0 -0
  31. db_storage_manager/bundled_plugins/connection-email-alerts/__pycache__/plugin.cpython-312.pyc +0 -0
  32. db_storage_manager/bundled_plugins/connection-email-alerts/plugin.json +11 -0
  33. db_storage_manager/bundled_plugins/connection-email-alerts/plugin.py +38 -0
  34. db_storage_manager/bundled_plugins/multi-connection-monitor/plugin.json +11 -0
  35. db_storage_manager/bundled_plugins/multi-connection-monitor/plugin.py +37 -0
  36. db_storage_manager/cloud/__init__.py +31 -0
  37. db_storage_manager/cloud/aws.py +245 -0
  38. db_storage_manager/cloud/azure.py +210 -0
  39. db_storage_manager/cloud/connect_auth.py +114 -0
  40. db_storage_manager/cloud/credentials.py +226 -0
  41. db_storage_manager/cloud/engines.py +86 -0
  42. db_storage_manager/cloud/errors.py +15 -0
  43. db_storage_manager/cloud/gcp.py +248 -0
  44. db_storage_manager/cloud/manager.py +175 -0
  45. db_storage_manager/config.py +291 -0
  46. db_storage_manager/dashboard_prefs.py +140 -0
  47. db_storage_manager/data/__init__.py +15 -0
  48. db_storage_manager/data/comparison.py +87 -0
  49. db_storage_manager/data/migration.py +48 -0
  50. db_storage_manager/data/quality.py +114 -0
  51. db_storage_manager/data/sync.py +62 -0
  52. db_storage_manager/db/__init__.py +35 -0
  53. db_storage_manager/db/base.py +143 -0
  54. db_storage_manager/db/clickhouse.py +194 -0
  55. db_storage_manager/db/factory.py +75 -0
  56. db_storage_manager/db/influxdb.py +247 -0
  57. db_storage_manager/db/mongo.py +313 -0
  58. db_storage_manager/db/mysql.py +424 -0
  59. db_storage_manager/db/optional_deps.py +53 -0
  60. db_storage_manager/db/oracle.py +280 -0
  61. db_storage_manager/db/pool.py +338 -0
  62. db_storage_manager/db/postgres.py +518 -0
  63. db_storage_manager/db/redis.py +284 -0
  64. db_storage_manager/db/session.py +64 -0
  65. db_storage_manager/db/sqlite.py +272 -0
  66. db_storage_manager/db/sqlserver.py +319 -0
  67. db_storage_manager/desktop_identity.py +64 -0
  68. db_storage_manager/experimental.py +67 -0
  69. db_storage_manager/export/__init__.py +17 -0
  70. db_storage_manager/export/companion.py +198 -0
  71. db_storage_manager/gui/__init__.py +3 -0
  72. db_storage_manager/gui/about.py +174 -0
  73. db_storage_manager/gui/advanced_dashboard.py +174 -0
  74. db_storage_manager/gui/backups.py +1552 -0
  75. db_storage_manager/gui/changelog.py +63 -0
  76. db_storage_manager/gui/charts.py +157 -0
  77. db_storage_manager/gui/connections.py +1124 -0
  78. db_storage_manager/gui/dashboard.py +598 -0
  79. db_storage_manager/gui/enhanced_query.py +299 -0
  80. db_storage_manager/gui/icons.py +326 -0
  81. db_storage_manager/gui/jobs.py +134 -0
  82. db_storage_manager/gui/login.py +125 -0
  83. db_storage_manager/gui/main_window.py +578 -0
  84. db_storage_manager/gui/monitoring.py +1135 -0
  85. db_storage_manager/gui/plugins.py +764 -0
  86. db_storage_manager/gui/proton_drive_config.py +181 -0
  87. db_storage_manager/gui/query.py +365 -0
  88. db_storage_manager/gui/restore_dialog.py +172 -0
  89. db_storage_manager/gui/s3_config.py +146 -0
  90. db_storage_manager/gui/settings.py +1445 -0
  91. db_storage_manager/gui/sidebar.py +125 -0
  92. db_storage_manager/gui/tray.py +225 -0
  93. db_storage_manager/gui/updates.py +269 -0
  94. db_storage_manager/gui/users.py +606 -0
  95. db_storage_manager/gui/utils.py +733 -0
  96. db_storage_manager/i18n/__init__.py +8 -0
  97. db_storage_manager/i18n/languages.py +22 -0
  98. db_storage_manager/i18n/manager.py +133 -0
  99. db_storage_manager/i18n/translations/de.json +723 -0
  100. db_storage_manager/i18n/translations/en.json +723 -0
  101. db_storage_manager/i18n/translations/es.json +723 -0
  102. db_storage_manager/i18n/translations/et.json +723 -0
  103. db_storage_manager/i18n/translations/fr.json +723 -0
  104. db_storage_manager/i18n/translations/id.json +723 -0
  105. db_storage_manager/i18n/translations/ja.json +723 -0
  106. db_storage_manager/i18n/translations/ko.json +723 -0
  107. db_storage_manager/i18n/translations/pt.json +723 -0
  108. db_storage_manager/i18n/translations/ru.json +723 -0
  109. db_storage_manager/i18n/translations/zh.json +723 -0
  110. db_storage_manager/importers/__init__.py +29 -0
  111. db_storage_manager/importers/env_file.py +328 -0
  112. db_storage_manager/importers/oauth_platforms.py +138 -0
  113. db_storage_manager/importers/persist.py +175 -0
  114. db_storage_manager/importers/redact.py +100 -0
  115. db_storage_manager/importers/ssh_keys.py +352 -0
  116. db_storage_manager/main.py +66 -0
  117. db_storage_manager/monitoring/__init__.py +27 -0
  118. db_storage_manager/monitoring/alerts.py +302 -0
  119. db_storage_manager/monitoring/anomaly.py +251 -0
  120. db_storage_manager/monitoring/connection_email.py +255 -0
  121. db_storage_manager/monitoring/health.py +211 -0
  122. db_storage_manager/monitoring/metrics.py +247 -0
  123. db_storage_manager/monitoring/monitor.py +248 -0
  124. db_storage_manager/monitoring/multi_watch.py +215 -0
  125. db_storage_manager/monitoring/notifications.py +941 -0
  126. db_storage_manager/native/__init__.py +340 -0
  127. db_storage_manager/plugins/__init__.py +34 -0
  128. db_storage_manager/plugins/base.py +135 -0
  129. db_storage_manager/plugins/data_comparison.py +45 -0
  130. db_storage_manager/plugins/licensing.py +616 -0
  131. db_storage_manager/plugins/loader.py +132 -0
  132. db_storage_manager/plugins/manifest.py +261 -0
  133. db_storage_manager/plugins/marketplace.py +566 -0
  134. db_storage_manager/plugins/migration.py +51 -0
  135. db_storage_manager/plugins/pricing.py +219 -0
  136. db_storage_manager/plugins/registry.py +92 -0
  137. db_storage_manager/plugins/sdk.py +82 -0
  138. db_storage_manager/premium_plugins/__init__.py +46 -0
  139. db_storage_manager/premium_plugins/__pycache__/__init__.cpython-312.pyc +0 -0
  140. db_storage_manager/premium_plugins/encrypted-export/__pycache__/plugin.cpython-312.pyc +0 -0
  141. db_storage_manager/premium_plugins/encrypted-export/plugin.json +21 -0
  142. db_storage_manager/premium_plugins/encrypted-export/plugin.py +349 -0
  143. db_storage_manager/premium_plugins/extension-inventory/__pycache__/plugin.cpython-312.pyc +0 -0
  144. db_storage_manager/premium_plugins/extension-inventory/__pycache__/plugin.cpython-314.pyc +0 -0
  145. db_storage_manager/premium_plugins/extension-inventory/plugin.json +21 -0
  146. db_storage_manager/premium_plugins/extension-inventory/plugin.py +191 -0
  147. db_storage_manager/premium_plugins/maintenance-window-watch/__pycache__/plugin.cpython-312.pyc +0 -0
  148. db_storage_manager/premium_plugins/maintenance-window-watch/__pycache__/plugin.cpython-314.pyc +0 -0
  149. db_storage_manager/premium_plugins/maintenance-window-watch/plugin.json +21 -0
  150. db_storage_manager/premium_plugins/maintenance-window-watch/plugin.py +380 -0
  151. db_storage_manager/premium_plugins/multi-cloud-discovery/__pycache__/plugin.cpython-312.pyc +0 -0
  152. db_storage_manager/premium_plugins/multi-cloud-discovery/__pycache__/plugin.cpython-314.pyc +0 -0
  153. db_storage_manager/premium_plugins/multi-cloud-discovery/plugin.json +21 -0
  154. db_storage_manager/premium_plugins/multi-cloud-discovery/plugin.py +401 -0
  155. db_storage_manager/premium_plugins/pitr-verify/__pycache__/plugin.cpython-312.pyc +0 -0
  156. db_storage_manager/premium_plugins/pitr-verify/plugin.json +21 -0
  157. db_storage_manager/premium_plugins/pitr-verify/plugin.py +1329 -0
  158. db_storage_manager/premium_plugins/pooler-health/__pycache__/plugin.cpython-312.pyc +0 -0
  159. db_storage_manager/premium_plugins/pooler-health/plugin.json +21 -0
  160. db_storage_manager/premium_plugins/pooler-health/plugin.py +243 -0
  161. db_storage_manager/premium_plugins/rls-audit/__pycache__/plugin.cpython-312.pyc +0 -0
  162. db_storage_manager/premium_plugins/rls-audit/plugin.json +21 -0
  163. db_storage_manager/premium_plugins/rls-audit/plugin.py +244 -0
  164. db_storage_manager/premium_plugins/schema-drift/__pycache__/plugin.cpython-312.pyc +0 -0
  165. db_storage_manager/premium_plugins/schema-drift/plugin.json +21 -0
  166. db_storage_manager/premium_plugins/schema-drift/plugin.py +315 -0
  167. db_storage_manager/premium_plugins/trusted-sources-check/__pycache__/plugin.cpython-312.pyc +0 -0
  168. db_storage_manager/premium_plugins/trusted-sources-check/__pycache__/plugin.cpython-314.pyc +0 -0
  169. db_storage_manager/premium_plugins/trusted-sources-check/plugin.json +21 -0
  170. db_storage_manager/premium_plugins/trusted-sources-check/plugin.py +380 -0
  171. db_storage_manager/resources/__init__.py +60 -0
  172. db_storage_manager/resources/__pycache__/__init__.cpython-312.pyc +0 -0
  173. db_storage_manager/resources/icons/db-storage-manager-128.png +0 -0
  174. db_storage_manager/resources/icons/db-storage-manager-16.png +0 -0
  175. db_storage_manager/resources/icons/db-storage-manager-256.png +0 -0
  176. db_storage_manager/resources/icons/db-storage-manager-32.png +0 -0
  177. db_storage_manager/resources/icons/db-storage-manager-48.png +0 -0
  178. db_storage_manager/resources/icons/db-storage-manager-512.png +0 -0
  179. db_storage_manager/resources/icons/db-storage-manager-64.png +0 -0
  180. db_storage_manager/resources/icons/db-storage-manager.ico +0 -0
  181. db_storage_manager/resources/icons/db-storage-manager.png +0 -0
  182. db_storage_manager/resources/icons/db-storage-manager.svg +39 -0
  183. db_storage_manager/security/__init__.py +21 -0
  184. db_storage_manager/security/auth.py +800 -0
  185. db_storage_manager/security/bootstrap.py +114 -0
  186. db_storage_manager/security/mfa_qr.py +70 -0
  187. db_storage_manager/security/pairing.py +200 -0
  188. db_storage_manager/security/store.py +316 -0
  189. db_storage_manager/ssh/__init__.py +25 -0
  190. db_storage_manager/ssh/tunnel.py +481 -0
  191. db_storage_manager/themes/__init__.py +8 -0
  192. db_storage_manager/themes/manager.py +113 -0
  193. db_storage_manager/themes/themes.py +130 -0
  194. db_storage_manager/updates.py +205 -0
  195. db_storage_manager-1.0.8.dist-info/METADATA +261 -0
  196. db_storage_manager-1.0.8.dist-info/RECORD +200 -0
  197. db_storage_manager-1.0.8.dist-info/WHEEL +5 -0
  198. db_storage_manager-1.0.8.dist-info/entry_points.txt +6 -0
  199. db_storage_manager-1.0.8.dist-info/licenses/LICENSE +674 -0
  200. db_storage_manager-1.0.8.dist-info/top_level.txt +1 -0
@@ -0,0 +1,7 @@
1
+ """
2
+ DB Storage Manager
3
+ A professional desktop application for managing database storage and backups.
4
+ """
5
+
6
+ __version__ = "1.0.8"
7
+ __author__ = "VoxHash"
@@ -0,0 +1,27 @@
1
+ """
2
+ Advanced database analysis features
3
+ """
4
+
5
+ from .performance import QueryPerformanceAnalyzer
6
+ from .optimization import IndexOptimizer
7
+ from .prediction import StorageGrowthPredictor
8
+ from .capacity import CapacityPlanner
9
+ from .query_suggestions import (
10
+ QuerySuggestion,
11
+ analyze_explain_plan,
12
+ analyze_sql_heuristics,
13
+ fetch_explain_plan,
14
+ suggest_for_query,
15
+ )
16
+
17
+ __all__ = [
18
+ "QueryPerformanceAnalyzer",
19
+ "IndexOptimizer",
20
+ "StorageGrowthPredictor",
21
+ "CapacityPlanner",
22
+ "QuerySuggestion",
23
+ "analyze_sql_heuristics",
24
+ "analyze_explain_plan",
25
+ "fetch_explain_plan",
26
+ "suggest_for_query",
27
+ ]
@@ -0,0 +1,181 @@
1
+ """
2
+ Capacity planning tools
3
+ """
4
+
5
+ from typing import Dict, List, Any, Optional
6
+ from datetime import datetime, timedelta
7
+ from ..db.base import DatabaseConnection, StorageAnalysis
8
+ from .prediction import StorageGrowthPredictor
9
+
10
+
11
+ class CapacityPlanner:
12
+ """Capacity planning and forecasting"""
13
+
14
+ def __init__(self, connection: DatabaseConnection):
15
+ self.connection = connection
16
+ self.predictor = StorageGrowthPredictor(connection)
17
+
18
+ def add_analysis(self, analysis: StorageAnalysis) -> None:
19
+ """Add analysis for capacity planning"""
20
+ self.predictor.add_analysis(analysis)
21
+
22
+ def calculate_capacity_utilization(
23
+ self, analysis: StorageAnalysis, total_capacity: int
24
+ ) -> Dict[str, Any]:
25
+ """Calculate current capacity utilization"""
26
+ current_size = analysis.get("totalSize", 0)
27
+ utilization_percent = (
28
+ (current_size / total_capacity * 100) if total_capacity > 0 else 0
29
+ )
30
+
31
+ status = (
32
+ "critical"
33
+ if utilization_percent > 90
34
+ else "warning" if utilization_percent > 75 else "healthy"
35
+ )
36
+
37
+ return {
38
+ "currentSize": current_size,
39
+ "totalCapacity": total_capacity,
40
+ "availableSpace": total_capacity - current_size,
41
+ "utilizationPercent": utilization_percent,
42
+ "status": status,
43
+ "timestamp": datetime.now().isoformat(),
44
+ }
45
+
46
+ def estimate_time_to_capacity(
47
+ self, total_capacity: int, threshold_percent: float = 90.0
48
+ ) -> Dict[str, Any]:
49
+ """Estimate time until capacity threshold is reached"""
50
+ if len(self.predictor.history) < 2:
51
+ return {
52
+ "estimatedDays": None,
53
+ "estimatedDate": None,
54
+ "confidence": "low",
55
+ "message": "Insufficient historical data",
56
+ }
57
+
58
+ current_size = self.predictor.history[-1]["totalSize"]
59
+ threshold_size = total_capacity * (threshold_percent / 100)
60
+
61
+ if current_size >= threshold_size:
62
+ return {
63
+ "estimatedDays": 0,
64
+ "estimatedDate": datetime.now().isoformat(),
65
+ "status": "threshold_reached",
66
+ "message": f"Already at {threshold_percent}% capacity",
67
+ }
68
+
69
+ # Predict growth
70
+ prediction = self.predictor.predict_growth(days=365)
71
+ growth_rate = prediction.get("growthRate", 0) / 100
72
+
73
+ if growth_rate <= 0:
74
+ return {
75
+ "estimatedDays": None,
76
+ "estimatedDate": None,
77
+ "status": "no_growth",
78
+ "message": "No growth detected, capacity threshold not expected",
79
+ }
80
+
81
+ # Calculate days to threshold
82
+ remaining_space = threshold_size - current_size
83
+ daily_growth = current_size * growth_rate
84
+ estimated_days = (
85
+ int(remaining_space / daily_growth) if daily_growth > 0 else None
86
+ )
87
+
88
+ if estimated_days:
89
+ estimated_date = datetime.now() + timedelta(days=estimated_days)
90
+ return {
91
+ "estimatedDays": estimated_days,
92
+ "estimatedDate": estimated_date.isoformat(),
93
+ "thresholdPercent": threshold_percent,
94
+ "thresholdSize": int(threshold_size),
95
+ "currentSize": current_size,
96
+ "growthRate": growth_rate * 100,
97
+ "confidence": prediction.get("confidence", "low"),
98
+ }
99
+ else:
100
+ return {
101
+ "estimatedDays": None,
102
+ "estimatedDate": None,
103
+ "status": "unable_to_calculate",
104
+ }
105
+
106
+ def get_capacity_recommendations(
107
+ self, analysis: StorageAnalysis, total_capacity: int
108
+ ) -> List[Dict[str, Any]]:
109
+ """Get capacity planning recommendations"""
110
+ recommendations = []
111
+ utilization = self.calculate_capacity_utilization(analysis, total_capacity)
112
+
113
+ if utilization["utilizationPercent"] > 90:
114
+ recommendations.append(
115
+ {
116
+ "type": "urgent",
117
+ "severity": "critical",
118
+ "message": "Database is at critical capacity (>90%)",
119
+ "recommendation": "Immediate action required: Consider archiving old data or increasing capacity",
120
+ }
121
+ )
122
+ elif utilization["utilizationPercent"] > 75:
123
+ recommendations.append(
124
+ {
125
+ "type": "warning",
126
+ "severity": "high",
127
+ "message": "Database is approaching capacity (>75%)",
128
+ "recommendation": "Plan for capacity increase or data archiving in the near future",
129
+ }
130
+ )
131
+
132
+ # Check growth trends
133
+ trends = self.predictor.get_growth_trends()
134
+ if trends.get("trend") == "rapid_growth":
135
+ recommendations.append(
136
+ {
137
+ "type": "growth_alert",
138
+ "severity": "medium",
139
+ "message": "Rapid growth detected",
140
+ "recommendation": "Monitor growth closely and plan capacity expansion",
141
+ }
142
+ )
143
+
144
+ # Time to capacity estimate
145
+ time_estimate = self.estimate_time_to_capacity(total_capacity)
146
+ if time_estimate.get("estimatedDays") and time_estimate["estimatedDays"] < 90:
147
+ recommendations.append(
148
+ {
149
+ "type": "capacity_forecast",
150
+ "severity": "high",
151
+ "message": f"Capacity threshold may be reached in {time_estimate['estimatedDays']} days",
152
+ "recommendation": "Plan capacity increase or data management strategy",
153
+ }
154
+ )
155
+
156
+ return recommendations
157
+
158
+ def generate_capacity_report(
159
+ self, analysis: StorageAnalysis, total_capacity: int
160
+ ) -> Dict[str, Any]:
161
+ """Generate comprehensive capacity planning report"""
162
+ utilization = self.calculate_capacity_utilization(analysis, total_capacity)
163
+ trends = self.predictor.get_growth_trends()
164
+ time_estimate = self.estimate_time_to_capacity(total_capacity)
165
+ recommendations = self.get_capacity_recommendations(analysis, total_capacity)
166
+
167
+ # Predictions for different timeframes
168
+ predictions = {
169
+ "30days": self.predictor.predict_growth(30),
170
+ "90days": self.predictor.predict_growth(90),
171
+ "365days": self.predictor.predict_growth(365),
172
+ }
173
+
174
+ return {
175
+ "currentState": utilization,
176
+ "growthTrends": trends,
177
+ "timeToCapacity": time_estimate,
178
+ "predictions": predictions,
179
+ "recommendations": recommendations,
180
+ "reportDate": datetime.now().isoformat(),
181
+ }
@@ -0,0 +1,155 @@
1
+ """
2
+ Index optimization suggestions
3
+ """
4
+
5
+ from typing import Dict, List, Any
6
+ from ..db.base import DatabaseConnection, StorageAnalysis
7
+
8
+
9
+ class IndexOptimizer:
10
+ """Provide index optimization suggestions"""
11
+
12
+ def __init__(self, connection: DatabaseConnection):
13
+ self.connection = connection
14
+
15
+ async def analyze_indexes(self, analysis: StorageAnalysis) -> List[Dict[str, Any]]:
16
+ """Analyze indexes and provide optimization suggestions"""
17
+ suggestions = []
18
+ db_type = self.connection.config.type.lower()
19
+
20
+ if db_type in ["postgresql", "postgres"]:
21
+ suggestions = await self._analyze_postgres_indexes(analysis)
22
+ elif db_type in ["mysql", "mariadb"]:
23
+ suggestions = await self._analyze_mysql_indexes(analysis)
24
+ elif db_type == "sqlserver" or db_type == "mssql":
25
+ suggestions = await self._analyze_sqlserver_indexes(analysis)
26
+
27
+ return suggestions
28
+
29
+ async def _analyze_postgres_indexes(
30
+ self, analysis: StorageAnalysis
31
+ ) -> List[Dict[str, Any]]:
32
+ """Analyze PostgreSQL indexes"""
33
+ suggestions = []
34
+
35
+ # Check for tables without indexes
36
+ for table in analysis.get("tables", []):
37
+ table_indexes = [
38
+ idx
39
+ for idx in analysis.get("indexes", [])
40
+ if idx.get("tableName") == table.get("name")
41
+ ]
42
+
43
+ if not table_indexes and table.get("rowCount", 0) > 1000:
44
+ suggestions.append(
45
+ {
46
+ "type": "missing_index",
47
+ "severity": "medium",
48
+ "table": table.get("name"),
49
+ "message": f"Table {table.get('name')} has {table.get('rowCount')} rows but no indexes",
50
+ "recommendation": f"Consider adding indexes on frequently queried columns in {table.get('name')}",
51
+ }
52
+ )
53
+
54
+ # Check for high bloat indexes
55
+ for idx in table_indexes:
56
+ if idx.get("bloat", 0) > 30.0:
57
+ suggestions.append(
58
+ {
59
+ "type": "index_bloat",
60
+ "severity": "high",
61
+ "index": idx.get("name"),
62
+ "table": idx.get("tableName"),
63
+ "bloat": idx.get("bloat"),
64
+ "message": f"Index {idx.get('name')} has {idx.get('bloat'):.2f}% bloat",
65
+ "recommendation": f"Consider rebuilding index {idx.get('name')} to reduce bloat",
66
+ }
67
+ )
68
+
69
+ return suggestions
70
+
71
+ async def _analyze_mysql_indexes(
72
+ self, analysis: StorageAnalysis
73
+ ) -> List[Dict[str, Any]]:
74
+ """Analyze MySQL indexes"""
75
+ suggestions = []
76
+
77
+ # Check for large tables without indexes
78
+ for table in analysis.get("tables", []):
79
+ table_indexes = [
80
+ idx
81
+ for idx in analysis.get("indexes", [])
82
+ if idx.get("tableName") == table.get("name")
83
+ ]
84
+
85
+ if not table_indexes and table.get("rowCount", 0) > 1000:
86
+ suggestions.append(
87
+ {
88
+ "type": "missing_index",
89
+ "severity": "medium",
90
+ "table": table.get("name"),
91
+ "message": f"Table {table.get('name')} has {table.get('rowCount')} rows but no indexes",
92
+ "recommendation": f"Add indexes on foreign keys and frequently queried columns",
93
+ }
94
+ )
95
+
96
+ return suggestions
97
+
98
+ async def _analyze_sqlserver_indexes(
99
+ self, analysis: StorageAnalysis
100
+ ) -> List[Dict[str, Any]]:
101
+ """Analyze SQL Server indexes"""
102
+ suggestions = []
103
+
104
+ # Check for missing indexes (SQL Server specific)
105
+ if self.connection.connected:
106
+ try:
107
+ result = await self.connection.execute_query(
108
+ """
109
+ SELECT
110
+ OBJECT_NAME(d.object_id) AS table_name,
111
+ d.equality_columns,
112
+ d.inequality_columns,
113
+ d.included_columns,
114
+ s.avg_user_impact
115
+ FROM sys.dm_db_missing_index_details d
116
+ INNER JOIN sys.dm_db_missing_index_groups g ON d.index_handle = g.index_handle
117
+ INNER JOIN sys.dm_db_missing_index_group_stats s ON g.index_group_handle = s.group_handle
118
+ WHERE s.avg_user_impact > 50
119
+ ORDER BY s.avg_user_impact DESC
120
+ """,
121
+ safe_mode=True,
122
+ )
123
+
124
+ for row in result.get("rows", []):
125
+ suggestions.append(
126
+ {
127
+ "type": "missing_index",
128
+ "severity": "high",
129
+ "table": row.get("table_name"),
130
+ "message": f"Missing index on {row.get('table_name')} could improve performance by {row.get('avg_user_impact')}%",
131
+ "recommendation": f"Create index on {row.get('equality_columns')} for table {row.get('table_name')}",
132
+ }
133
+ )
134
+ except Exception:
135
+ pass
136
+
137
+ return suggestions
138
+
139
+ async def get_optimization_recommendations(
140
+ self, analysis: StorageAnalysis
141
+ ) -> Dict[str, Any]:
142
+ """Get comprehensive optimization recommendations"""
143
+ suggestions = await self.analyze_indexes(analysis)
144
+
145
+ high_priority = [s for s in suggestions if s.get("severity") == "high"]
146
+ medium_priority = [s for s in suggestions if s.get("severity") == "medium"]
147
+ low_priority = [s for s in suggestions if s.get("severity") == "low"]
148
+
149
+ return {
150
+ "totalSuggestions": len(suggestions),
151
+ "highPriority": high_priority,
152
+ "mediumPriority": medium_priority,
153
+ "lowPriority": low_priority,
154
+ "suggestions": suggestions,
155
+ }
@@ -0,0 +1,127 @@
1
+ """
2
+ Query performance analysis
3
+ """
4
+
5
+ import time
6
+ from typing import Dict, List, Any, Optional
7
+ from datetime import datetime
8
+ from ..db.base import DatabaseConnection, QueryResult
9
+
10
+
11
+ class QueryPerformanceAnalyzer:
12
+ """Analyze query performance"""
13
+
14
+ def __init__(self, connection: DatabaseConnection):
15
+ self.connection = connection
16
+ self.query_history: List[Dict[str, Any]] = []
17
+
18
+ async def analyze_query(self, query: str) -> Dict[str, Any]:
19
+ """Analyze a query's performance"""
20
+ start_time = time.time()
21
+
22
+ try:
23
+ result = await self.connection.execute_query(query, safe_mode=True)
24
+ execution_time = (
25
+ time.time() - start_time
26
+ ) * 1000 # Convert to milliseconds
27
+
28
+ analysis = {
29
+ "query": query,
30
+ "executionTime": execution_time,
31
+ "rowCount": result.get("rowCount", 0),
32
+ "columns": result.get("columns", []),
33
+ "timestamp": datetime.now().isoformat(),
34
+ "status": "success",
35
+ }
36
+
37
+ # Get explain plan if available
38
+ try:
39
+ explain_plan = await self._get_explain_plan(query)
40
+ analysis["explainPlan"] = explain_plan
41
+ except Exception:
42
+ analysis["explainPlan"] = None
43
+
44
+ self.query_history.append(analysis)
45
+ return analysis
46
+
47
+ except Exception as e:
48
+ execution_time = (time.time() - start_time) * 1000
49
+ analysis = {
50
+ "query": query,
51
+ "executionTime": execution_time,
52
+ "rowCount": 0,
53
+ "columns": [],
54
+ "timestamp": datetime.now().isoformat(),
55
+ "status": "error",
56
+ "error": str(e),
57
+ }
58
+ self.query_history.append(analysis)
59
+ return analysis
60
+
61
+ async def _get_explain_plan(self, query: str) -> Optional[Dict[str, Any]]:
62
+ """Get query explain plan"""
63
+ db_type = self.connection.config.type.lower()
64
+
65
+ if db_type in ["postgresql", "postgres"]:
66
+ explain_query = f"EXPLAIN ANALYZE {query}"
67
+ elif db_type in ["mysql", "mariadb"]:
68
+ explain_query = f"EXPLAIN {query}"
69
+ elif db_type == "sqlite":
70
+ explain_query = f"EXPLAIN QUERY PLAN {query}"
71
+ else:
72
+ return None
73
+
74
+ try:
75
+ result = await self.connection.execute_query(explain_query, safe_mode=True)
76
+ return {
77
+ "plan": result.get("rows", []),
78
+ "columns": result.get("columns", []),
79
+ }
80
+ except Exception:
81
+ return None
82
+
83
+ def get_slow_queries(self, threshold_ms: float = 1000.0) -> List[Dict[str, Any]]:
84
+ """Get queries that took longer than threshold"""
85
+ return [
86
+ q
87
+ for q in self.query_history
88
+ if q.get("executionTime", 0) > threshold_ms and q.get("status") == "success"
89
+ ]
90
+
91
+ def get_query_statistics(self) -> Dict[str, Any]:
92
+ """Get query performance statistics"""
93
+ if not self.query_history:
94
+ return {
95
+ "totalQueries": 0,
96
+ "averageExecutionTime": 0,
97
+ "slowQueries": 0,
98
+ "errorQueries": 0,
99
+ }
100
+
101
+ successful_queries = [
102
+ q for q in self.query_history if q.get("status") == "success"
103
+ ]
104
+
105
+ if successful_queries:
106
+ from ..native import aggregate_f64
107
+
108
+ times = [float(q.get("executionTime", 0) or 0) for q in successful_queries]
109
+ _count, _total, min_time, max_time, avg_time = aggregate_f64(times)
110
+ slow_queries = len(self.get_slow_queries())
111
+ else:
112
+ avg_time = 0
113
+ min_time = 0
114
+ max_time = 0
115
+ slow_queries = 0
116
+
117
+ return {
118
+ "totalQueries": len(self.query_history),
119
+ "averageExecutionTime": avg_time,
120
+ "minExecutionTime": min_time,
121
+ "maxExecutionTime": max_time,
122
+ "slowQueries": slow_queries,
123
+ "errorQueries": len(
124
+ [q for q in self.query_history if q.get("status") == "error"]
125
+ ),
126
+ "successfulQueries": len(successful_queries),
127
+ }
@@ -0,0 +1,169 @@
1
+ """
2
+ Storage growth predictions
3
+ """
4
+
5
+ from typing import Dict, List, Any, Optional
6
+ from datetime import datetime, timedelta
7
+ import statistics
8
+ from ..db.base import DatabaseConnection, StorageAnalysis
9
+
10
+
11
+ class StorageGrowthPredictor:
12
+ """Predict storage growth based on historical data"""
13
+
14
+ def __init__(self, connection: DatabaseConnection):
15
+ self.connection = connection
16
+ self.history: List[Dict[str, Any]] = []
17
+
18
+ def add_analysis(self, analysis: StorageAnalysis) -> None:
19
+ """Add analysis data point to history"""
20
+ self.history.append(
21
+ {
22
+ "timestamp": datetime.now().isoformat(),
23
+ "totalSize": analysis.get("totalSize", 0),
24
+ "tableCount": analysis.get("tableCount", 0),
25
+ "analysis": analysis,
26
+ }
27
+ )
28
+
29
+ def predict_growth(self, days: int = 30) -> Dict[str, Any]:
30
+ """Predict storage growth for the next N days"""
31
+ if len(self.history) < 2:
32
+ return {
33
+ "predictedSize": 0,
34
+ "growthRate": 0,
35
+ "confidence": "low",
36
+ "message": "Insufficient historical data for prediction",
37
+ }
38
+
39
+ # Calculate growth rate
40
+ sizes = [h["totalSize"] for h in self.history]
41
+ growth_rates = []
42
+
43
+ for i in range(1, len(sizes)):
44
+ if sizes[i - 1] > 0:
45
+ rate = (sizes[i] - sizes[i - 1]) / sizes[i - 1]
46
+ growth_rates.append(rate)
47
+
48
+ if not growth_rates:
49
+ return {
50
+ "predictedSize": sizes[-1] if sizes else 0,
51
+ "growthRate": 0,
52
+ "confidence": "low",
53
+ "message": "No growth detected",
54
+ }
55
+
56
+ avg_growth_rate = statistics.mean(growth_rates)
57
+ current_size = sizes[-1]
58
+
59
+ # Predict future size
60
+ predicted_size = current_size * (1 + avg_growth_rate) ** days
61
+
62
+ # Calculate confidence based on data points
63
+ confidence = (
64
+ "high"
65
+ if len(self.history) >= 10
66
+ else "medium" if len(self.history) >= 5 else "low"
67
+ )
68
+
69
+ return {
70
+ "predictedSize": int(predicted_size),
71
+ "currentSize": current_size,
72
+ "growthRate": avg_growth_rate * 100, # Percentage
73
+ "growthAmount": int(predicted_size - current_size),
74
+ "days": days,
75
+ "confidence": confidence,
76
+ "predictedDate": (datetime.now() + timedelta(days=days)).isoformat(),
77
+ }
78
+
79
+ def predict_table_growth(self, table_name: str, days: int = 30) -> Dict[str, Any]:
80
+ """Predict growth for a specific table"""
81
+ table_history = []
82
+
83
+ for h in self.history:
84
+ analysis = h.get("analysis", {})
85
+ for table in analysis.get("tables", []):
86
+ if table.get("name") == table_name:
87
+ table_history.append(
88
+ {
89
+ "timestamp": h["timestamp"],
90
+ "size": table.get("size", 0),
91
+ "rowCount": table.get("rowCount", 0),
92
+ }
93
+ )
94
+
95
+ if len(table_history) < 2:
96
+ return {
97
+ "predictedSize": 0,
98
+ "growthRate": 0,
99
+ "confidence": "low",
100
+ "message": f"Insufficient data for table {table_name}",
101
+ }
102
+
103
+ sizes = [h["size"] for h in table_history]
104
+ growth_rates = []
105
+
106
+ for i in range(1, len(sizes)):
107
+ if sizes[i - 1] > 0:
108
+ rate = (sizes[i] - sizes[i - 1]) / sizes[i - 1]
109
+ growth_rates.append(rate)
110
+
111
+ if not growth_rates:
112
+ return {
113
+ "predictedSize": sizes[-1] if sizes else 0,
114
+ "growthRate": 0,
115
+ "confidence": "low",
116
+ }
117
+
118
+ avg_growth_rate = statistics.mean(growth_rates)
119
+ current_size = sizes[-1]
120
+ predicted_size = current_size * (1 + avg_growth_rate) ** days
121
+
122
+ return {
123
+ "tableName": table_name,
124
+ "predictedSize": int(predicted_size),
125
+ "currentSize": current_size,
126
+ "growthRate": avg_growth_rate * 100,
127
+ "growthAmount": int(predicted_size - current_size),
128
+ "days": days,
129
+ "confidence": "high" if len(table_history) >= 10 else "medium",
130
+ }
131
+
132
+ def get_growth_trends(self) -> Dict[str, Any]:
133
+ """Get overall growth trends"""
134
+ if len(self.history) < 2:
135
+ return {
136
+ "trend": "insufficient_data",
137
+ "message": "Need at least 2 data points",
138
+ }
139
+
140
+ sizes = [h["totalSize"] for h in self.history]
141
+ growth_rates = []
142
+
143
+ for i in range(1, len(sizes)):
144
+ if sizes[i - 1] > 0:
145
+ rate = (sizes[i] - sizes[i - 1]) / sizes[i - 1]
146
+ growth_rates.append(rate)
147
+
148
+ if not growth_rates:
149
+ return {"trend": "stable", "growthRate": 0}
150
+
151
+ avg_rate = statistics.mean(growth_rates)
152
+
153
+ if avg_rate > 0.1:
154
+ trend = "rapid_growth"
155
+ elif avg_rate > 0.01:
156
+ trend = "moderate_growth"
157
+ elif avg_rate > 0:
158
+ trend = "slow_growth"
159
+ elif avg_rate == 0:
160
+ trend = "stable"
161
+ else:
162
+ trend = "declining"
163
+
164
+ return {
165
+ "trend": trend,
166
+ "growthRate": avg_rate * 100,
167
+ "dataPoints": len(self.history),
168
+ "totalGrowth": sizes[-1] - sizes[0] if sizes else 0,
169
+ }