Barouia commited on
Commit
86eaf80
·
verified ·
1 Parent(s): 5e09314

Create data_base_engineer.py

Browse files
Files changed (1) hide show
  1. data_base_engineer.py +581 -0
data_base_engineer.py ADDED
@@ -0,0 +1,581 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Ingénieur Automatique de Bases de Données
4
+ Système intelligent de correction, exécution et mise à jour automatique
5
+ """
6
+
7
+ import sqlite3
8
+ import mysql.connector
9
+ import psycopg2
10
+ import pandas as pd
11
+ import logging
12
+ import asyncio
13
+ from typing import Dict, List, Any, Optional, Tuple
14
+ from dataclasses import dataclass
15
+ from datetime import datetime
16
+ import re
17
+ import json
18
+ import hashlib
19
+
20
+ @dataclass
21
+ class DatabaseSchema:
22
+ """Schéma de base de données"""
23
+ tables: Dict[str, Any]
24
+ indexes: List[Dict]
25
+ relationships: List[Dict]
26
+ metadata: Dict[str, Any]
27
+
28
+ @dataclass
29
+ class QueryAnalysis:
30
+ """Analyse de requête SQL"""
31
+ query_type: str
32
+ tables_affected: List[str]
33
+ columns_affected: List[str]
34
+ potential_issues: List[str]
35
+ optimization_suggestions: List[str]
36
+ execution_plan: Optional[Dict]
37
+
38
+ class AutomaticDatabaseEngineer:
39
+ """
40
+ Ingénieur automatique pour bases de données
41
+ """
42
+
43
+ def __init__(self):
44
+ self.logger = logging.getLogger("db_engineer")
45
+ self.connections: Dict[str, Any] = {}
46
+ self.schemas: Dict[str, DatabaseSchema] = {}
47
+
48
+ # Règles de correction automatique
49
+ self.auto_fix_rules = {
50
+ "syntax_error": {
51
+ "patterns": [
52
+ r"near\s+\"([^\"]+)\"",
53
+ r"syntax\s+error",
54
+ r"unexpected\s+token"
55
+ ],
56
+ "fixes": self._fix_syntax_errors
57
+ },
58
+ "table_not_found": {
59
+ "patterns": [
60
+ r"table\s+[\"']?([\w]+)[\"']?\s+not\s+found",
61
+ r"no\s+such\s+table"
62
+ ],
63
+ "fixes": self._fix_table_issues
64
+ },
65
+ "column_not_found": {
66
+ "patterns": [
67
+ r"column\s+[\"']?([\w]+)[\"']?\s+not\s+found",
68
+ r"no\s+such\s+column"
69
+ ],
70
+ "fixes": self._fix_column_issues
71
+ },
72
+ "constraint_violation": {
73
+ "patterns": [
74
+ r"constraint\s+failed",
75
+ r"unique\s+constraint",
76
+ r"foreign\s+key\s+constraint"
77
+ ],
78
+ "fixes": self._fix_constraint_issues
79
+ },
80
+ "performance_issue": {
81
+ "patterns": [
82
+ r"slow\s+query",
83
+ r"full\s+table\s+scan",
84
+ r"index\s+missing"
85
+ ],
86
+ "fixes": self._fix_performance_issues
87
+ }
88
+ }
89
+
90
+ # Templates de schémas optimisés
91
+ self.optimized_templates = {
92
+ "ecommerce": self._get_ecommerce_schema(),
93
+ "blog": self._get_blog_schema(),
94
+ "analytics": self._get_analytics_schema(),
95
+ "user_management": self._get_user_management_schema()
96
+ }
97
+
98
+ async def connect_database(self, db_type: str, connection_params: Dict) -> str:
99
+ """Établit une connexion à la base de données"""
100
+ connection_id = f"{db_type}_{hashlib.md5(str(connection_params).encode()).hexdigest()[:8]}"
101
+
102
+ try:
103
+ if db_type == "sqlite":
104
+ self.connections[connection_id] = sqlite3.connect(
105
+ connection_params['database'],
106
+ check_same_thread=False
107
+ )
108
+ elif db_type == "mysql":
109
+ self.connections[connection_id] = mysql.connector.connect(
110
+ host=connection_params.get('host', 'localhost'),
111
+ user=connection_params.get('user', 'root'),
112
+ password=connection_params.get('password', ''),
113
+ database=connection_params.get('database', '')
114
+ )
115
+ elif db_type == "postgresql":
116
+ self.connections[connection_id] = psycopg2.connect(
117
+ host=connection_params.get('host', 'localhost'),
118
+ user=connection_params.get('user', 'postgres'),
119
+ password=connection_params.get('password', ''),
120
+ database=connection_params.get('database', 'postgres')
121
+ )
122
+ else:
123
+ raise ValueError(f"Type de base de données non supporté: {db_type}")
124
+
125
+ # Analyse du schéma existant
126
+ await self._analyze_schema(connection_id, db_type)
127
+
128
+ self.logger.info(f"Connexion établie: {connection_id}")
129
+ return connection_id
130
+
131
+ except Exception as e:
132
+ self.logger.error(f"Erreur connexion {db_type}: {e}")
133
+ raise
134
+
135
+ async def execute_and_fix_query(self, connection_id: str, query: str, max_attempts: int = 3) -> Dict[str, Any]:
136
+ """Exécute une requête avec correction automatique en cas d'erreur"""
137
+ attempt = 0
138
+ original_query = query
139
+ fixes_applied = []
140
+
141
+ while attempt < max_attempts:
142
+ try:
143
+ result = await self._execute_query(connection_id, query)
144
+
145
+ return {
146
+ "success": True,
147
+ "result": result,
148
+ "query_executed": query,
149
+ "fixes_applied": fixes_applied,
150
+ "attempts": attempt + 1
151
+ }
152
+
153
+ except Exception as e:
154
+ error_msg = str(e)
155
+ self.logger.warning(f"Erreur exécution (tentative {attempt + 1}): {error_msg}")
156
+
157
+ # Tentative de correction automatique
158
+ fixed_query = await self._auto_fix_query(query, error_msg, connection_id)
159
+
160
+ if fixed_query and fixed_query != query:
161
+ query = fixed_query
162
+ fixes_applied.append({
163
+ "original_error": error_msg,
164
+ "fix_description": "Correction automatique appliquée",
165
+ "fixed_query": fixed_query
166
+ })
167
+ attempt += 1
168
+ else:
169
+ # Impossible de corriger automatiquement
170
+ return {
171
+ "success": False,
172
+ "error": error_msg,
173
+ "original_query": original_query,
174
+ "fixes_attempted": fixes_applied,
175
+ "suggested_fix": await self._suggest_manual_fix(original_query, error_msg)
176
+ }
177
+
178
+ return {
179
+ "success": False,
180
+ "error": "Échec après plusieurs tentatives de correction",
181
+ "original_query": original_query,
182
+ "fixes_applied": fixes_applied
183
+ }
184
+
185
+ async def auto_optimize_database(self, connection_id: str, optimization_type: str = "full") -> Dict[str, Any]:
186
+ """Optimisation automatique de la base de données"""
187
+ optimizations_applied = []
188
+
189
+ try:
190
+ # Analyse des performances
191
+ performance_analysis = await self._analyze_performance(connection_id)
192
+
193
+ if optimization_type in ["full", "indexes"]:
194
+ # Optimisation des index
195
+ index_optimizations = await self._optimize_indexes(connection_id)
196
+ optimizations_applied.extend(index_optimizations)
197
+
198
+ if optimization_type in ["full", "schema"]:
199
+ # Optimisation du schéma
200
+ schema_optimizations = await self._optimize_schema(connection_id)
201
+ optimizations_applied.extend(schema_optimizations)
202
+
203
+ if optimization_type in ["full", "maintenance"]:
204
+ # Maintenance générale
205
+ maintenance_ops = await self._perform_maintenance(connection_id)
206
+ optimizations_applied.extend(maintenance_ops)
207
+
208
+ return {
209
+ "success": True,
210
+ "optimizations_applied": optimizations_applied,
211
+ "performance_improvement": await self._measure_performance_improvement(connection_id),
212
+ "recommendations": await self._generate_recommendations(connection_id)
213
+ }
214
+
215
+ except Exception as e:
216
+ self.logger.error(f"Erreur optimisation: {e}")
217
+ return {
218
+ "success": False,
219
+ "error": str(e),
220
+ "optimizations_applied": optimizations_applied
221
+ }
222
+
223
+ async def intelligent_migration(self, connection_id: str, target_schema: Dict) -> Dict[str, Any]:
224
+ """Migration intelligente vers un nouveau schéma"""
225
+ migration_steps = []
226
+ current_schema = self.schemas[connection_id]
227
+
228
+ try:
229
+ # Analyse des différences
230
+ schema_diff = await self._compare_schemas(current_schema, target_schema)
231
+
232
+ # Génération des étapes de migration
233
+ migration_plan = await self._generate_migration_plan(schema_diff)
234
+
235
+ # Exécution sécurisée de la migration
236
+ for step in migration_plan:
237
+ try:
238
+ result = await self.execute_and_fix_query(connection_id, step['query'])
239
+
240
+ migration_steps.append({
241
+ "step": step['description'],
242
+ "query": step['query'],
243
+ "success": result['success'],
244
+ "details": result
245
+ })
246
+
247
+ if not result['success']:
248
+ # Rollback partiel ou correction
249
+ await self._handle_migration_failure(connection_id, migration_steps, step)
250
+ break
251
+
252
+ except Exception as e:
253
+ migration_steps.append({
254
+ "step": step['description'],
255
+ "error": str(e),
256
+ "success": False
257
+ })
258
+ break
259
+
260
+ # Vérification finale
261
+ if all(step.get('success', False) for step in migration_steps):
262
+ await self._verify_migration(connection_id, target_schema)
263
+ return {
264
+ "success": True,
265
+ "migration_steps": migration_steps,
266
+ "message": "Migration terminée avec succès"
267
+ }
268
+ else:
269
+ return {
270
+ "success": False,
271
+ "migration_steps": migration_steps,
272
+ "error": "Migration échouée partiellement"
273
+ }
274
+
275
+ except Exception as e:
276
+ self.logger.error(f"Erreur migration: {e}")
277
+ return {
278
+ "success": False,
279
+ "error": str(e),
280
+ "migration_steps": migration_steps
281
+ }
282
+
283
+ async def real_time_monitoring(self, connection_id: str) -> Dict[str, Any]:
284
+ """Surveillance en temps réel de la base de données"""
285
+ monitoring_data = {
286
+ "timestamp": datetime.now().isoformat(),
287
+ "performance_metrics": await self._get_performance_metrics(connection_id),
288
+ "query_analysis": await self._analyze_active_queries(connection_id),
289
+ "resource_usage": await self._get_resource_usage(connection_id),
290
+ "alerts": await self._check_alerts(connection_id),
291
+ "recommendations": await self._generate_realtime_recommendations(connection_id)
292
+ }
293
+
294
+ return monitoring_data
295
+
296
+ async def _auto_fix_query(self, query: str, error: str, connection_id: str) -> Optional[str]:
297
+ """Correction automatique des requêtes basée sur les erreurs"""
298
+ for rule_type, rule in self.auto_fix_rules.items():
299
+ for pattern in rule['patterns']:
300
+ if re.search(pattern, error, re.IGNORECASE):
301
+ fixed_query = await rule['fixes'](query, error, connection_id)
302
+ if fixed_query:
303
+ self.logger.info(f"Correction appliquée ({rule_type}): {fixed_query}")
304
+ return fixed_query
305
+
306
+ return None
307
+
308
+ async def _fix_syntax_errors(self, query: str, error: str, connection_id: str) -> Optional[str]:
309
+ """Correction des erreurs de syntaxe"""
310
+ # Correction des guillemets mal fermés
311
+ if query.count('"') % 2 != 0:
312
+ fixed = query + '"'
313
+ return fixed
314
+
315
+ # Correction des parenthèses mal fermées
316
+ if query.count('(') > query.count(')'):
317
+ fixed = query + ')' * (query.count('(') - query.count(')'))
318
+ return fixed
319
+
320
+ # Correction des virgules en fin de SELECT
321
+ if re.search(r"SELECT\s+.+,\s+FROM", query, re.IGNORECASE):
322
+ fixed = re.sub(r",\s+FROM", " FROM", query, flags=re.IGNORECASE)
323
+ return fixed
324
+
325
+ return None
326
+
327
+ async def _fix_table_issues(self, query: str, error: str, connection_id: str) -> Optional[str]:
328
+ """Correction des problèmes de tables"""
329
+ # Extraction du nom de table de l'erreur
330
+ table_match = re.search(r"table\s+[\"']?([\w]+)[\"']?", error, re.IGNORECASE)
331
+ if table_match:
332
+ table_name = table_match.group(1)
333
+
334
+ # Vérification si la table existe avec une casse différente
335
+ schema = self.schemas[connection_id]
336
+ actual_tables = list(schema.tables.keys())
337
+
338
+ for actual_table in actual_tables:
339
+ if actual_table.lower() == table_name.lower():
340
+ # Correction de la casse
341
+ fixed = re.sub(
342
+ f"\\b{table_name}\\b",
343
+ actual_table,
344
+ query,
345
+ flags=re.IGNORECASE
346
+ )
347
+ return fixed
348
+
349
+ # Suggestion de création de table si approprié
350
+ if "CREATE" not in query.upper() and "DROP" not in query.upper():
351
+ create_query = await self._suggest_table_creation(table_name, query)
352
+ return create_query
353
+
354
+ return None
355
+
356
+ async def _fix_column_issues(self, query: str, error: str, connection_id: str) -> Optional[str]:
357
+ """Correction des problèmes de colonnes"""
358
+ column_match = re.search(r"column\s+[\"']?([\w]+)[\"']?", error, re.IGNORECASE)
359
+ if column_match:
360
+ column_name = column_match.group(1)
361
+
362
+ # Recherche de la colonne correcte dans le schéma
363
+ schema = self.schemas[connection_id]
364
+
365
+ for table_name, table_info in schema.tables.items():
366
+ if column_name in table_info.get('columns', {}):
367
+ # La colonne existe dans cette table
368
+ return query
369
+ else:
370
+ # Recherche de colonnes similaires
371
+ for actual_column in table_info.get('columns', {}).keys():
372
+ if actual_column.lower() == column_name.lower():
373
+ fixed = re.sub(
374
+ f"\\b{column_name}\\b",
375
+ actual_column,
376
+ query,
377
+ flags=re.IGNORECASE
378
+ )
379
+ return fixed
380
+
381
+ return None
382
+
383
+ async def _analyze_schema(self, connection_id: str, db_type: str):
384
+ """Analyse complète du schéma de base de données"""
385
+ schema = DatabaseSchema(tables={}, indexes=[], relationships=[], metadata={})
386
+
387
+ try:
388
+ # Récupération des tables
389
+ if db_type == "sqlite":
390
+ tables_query = "SELECT name FROM sqlite_master WHERE type='table';"
391
+ elif db_type == "mysql":
392
+ tables_query = "SHOW TABLES;"
393
+ elif db_type == "postgresql":
394
+ tables_query = """
395
+ SELECT table_name
396
+ FROM information_schema.tables
397
+ WHERE table_schema = 'public';
398
+ """
399
+
400
+ cursor = self.connections[connection_id].cursor()
401
+ cursor.execute(tables_query)
402
+ tables = cursor.fetchall()
403
+
404
+ for table in tables:
405
+ table_name = table[0] if isinstance(table, (list, tuple)) else table
406
+ schema.tables[table_name] = await self._analyze_table(connection_id, table_name, db_type)
407
+
408
+ # Analyse des indexes
409
+ schema.indexes = await self._analyze_indexes(connection_id, db_type)
410
+
411
+ # Analyse des relations
412
+ schema.relationships = await self._analyze_relationships(connection_id, db_type)
413
+
414
+ self.schemas[connection_id] = schema
415
+
416
+ except Exception as e:
417
+ self.logger.error(f"Erreur analyse schéma: {e}")
418
+
419
+ async def _analyze_table(self, connection_id: str, table_name: str, db_type: str) -> Dict[str, Any]:
420
+ """Analyse détaillée d'une table"""
421
+ table_info = {"columns": {}, "constraints": [], "indexes": []}
422
+
423
+ try:
424
+ cursor = self.connections[connection_id].cursor()
425
+
426
+ if db_type == "sqlite":
427
+ # Structure des colonnes
428
+ cursor.execute(f"PRAGMA table_info({table_name});")
429
+ columns = cursor.fetchall()
430
+ for col in columns:
431
+ table_info["columns"][col[1]] = {
432
+ "type": col[2],
433
+ "nullable": not col[3],
434
+ "default": col[4],
435
+ "primary_key": col[5] == 1
436
+ }
437
+
438
+ elif db_type == "mysql":
439
+ cursor.execute(f"DESCRIBE {table_name};")
440
+ columns = cursor.fetchall()
441
+ for col in columns:
442
+ table_info["columns"][col[0]] = {
443
+ "type": col[1],
444
+ "nullable": col[2] == "YES",
445
+ "default": col[4],
446
+ "primary_key": col[3] == "PRI"
447
+ }
448
+
449
+ # Statistiques de la table
450
+ cursor.execute(f"SELECT COUNT(*) FROM {table_name};")
451
+ table_info["row_count"] = cursor.fetchone()[0]
452
+
453
+ except Exception as e:
454
+ self.logger.error(f"Erreur analyse table {table_name}: {e}")
455
+
456
+ return table_info
457
+
458
+ async def _execute_query(self, connection_id: str, query: str) -> Any:
459
+ """Exécution sécurisée d'une requête"""
460
+ connection = self.connections[connection_id]
461
+ cursor = connection.cursor()
462
+
463
+ try:
464
+ cursor.execute(query)
465
+
466
+ if query.strip().upper().startswith(('SELECT', 'SHOW', 'DESCRIBE', 'EXPLAIN')):
467
+ result = cursor.fetchall()
468
+ columns = [desc[0] for desc in cursor.description] if cursor.description else []
469
+ return {"data": result, "columns": columns}
470
+ else:
471
+ connection.commit()
472
+ return {"rows_affected": cursor.rowcount}
473
+
474
+ finally:
475
+ cursor.close()
476
+
477
+ async def _suggest_manual_fix(self, query: str, error: str) -> Dict[str, Any]:
478
+ """Suggestion de correction manuelle"""
479
+ suggestions = {
480
+ "original_error": error,
481
+ "suggested_fixes": [],
482
+ "alternative_queries": [],
483
+ "documentation_references": []
484
+ }
485
+
486
+ # Suggestions basées sur le type d'erreur
487
+ if "syntax" in error.lower():
488
+ suggestions["suggested_fixes"].append("Vérifiez la syntaxe SQL, particulièrement les guillemets et parenthèses")
489
+ suggestions["suggested_fixes"].append("Utilisez un outil de formatage SQL pour identifier les erreurs")
490
+
491
+ if "table" in error.lower() and "not found" in error.lower():
492
+ suggestions["suggested_fixes"].append("Vérifiez le nom de la table dans le schéma de la base de données")
493
+ suggestions["suggested_fixes"].append("Assurez-vous que la table existe et que vous avez les permissions nécessaires")
494
+
495
+ if "column" in error.lower() and "not found" in error.lower():
496
+ suggestions["suggested_fixes"].append("Vérifiez le nom des colonnes dans la structure de la table")
497
+ suggestions["suggested_fixes"].append("Utilisez SELECT * FROM table LIMIT 1 pour voir la structure")
498
+
499
+ # Génération de requêtes alternatives
500
+ query_upper = query.upper()
501
+ if "SELECT" in query_upper:
502
+ # Suggestion d'index
503
+ suggestions["alternative_queries"].append(
504
+ "Pensez à ajouter des INDEX sur les colonnes utilisées dans WHERE et JOIN"
505
+ )
506
+
507
+ return suggestions
508
+
509
+ # Méthodes d'optimisation (implémentations simplifiées)
510
+ async def _optimize_indexes(self, connection_id: str) -> List[Dict]:
511
+ """Optimisation automatique des index"""
512
+ optimizations = []
513
+ schema = self.schemas[connection_id]
514
+
515
+ # Analyse des colonnes fréquemment utilisées dans les WHERE
516
+ for table_name, table_info in schema.tables.items():
517
+ columns_usage = await self._analyze_column_usage(connection_id, table_name)
518
+
519
+ for column, usage in columns_usage.items():
520
+ if usage['filter_usage'] > 10 and not self._has_index(table_name, column, schema):
521
+ # Création d'index suggérée
522
+ index_query = f"CREATE INDEX idx_{table_name}_{column} ON {table_name}({column});"
523
+ try:
524
+ await self._execute_query(connection_id, index_query)
525
+ optimizations.append({
526
+ "type": "index_creation",
527
+ "table": table_name,
528
+ "column": column,
529
+ "query": index_query,
530
+ "impact": "high"
531
+ })
532
+ except Exception as e:
533
+ self.logger.warning(f"Impossible de créer l'index: {e}")
534
+
535
+ return optimizations
536
+
537
+ async def _analyze_performance(self, connection_id: str) -> Dict[str, Any]:
538
+ """Analyse des performances de la base de données"""
539
+ analysis = {
540
+ "slow_queries": [],
541
+ "missing_indexes": [],
542
+ "table_scans": [],
543
+ "lock_contention": []
544
+ }
545
+
546
+ # Implémentation simplifiée
547
+ # Dans une version complète, on utiliserait les métriques système
548
+
549
+ return analysis
550
+
551
+ def _get_ecommerce_schema(self) -> Dict:
552
+ """Template de schéma e-commerce optimisé"""
553
+ return {
554
+ "users": {
555
+ "columns": {
556
+ "id": "INT PRIMARY KEY AUTO_INCREMENT",
557
+ "email": "VARCHAR(255) UNIQUE NOT NULL",
558
+ "created_at": "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
559
+ },
560
+ "indexes": ["email"]
561
+ },
562
+ "products": {
563
+ "columns": {
564
+ "id": "INT PRIMARY KEY AUTO_INCREMENT",
565
+ "name": "VARCHAR(255) NOT NULL",
566
+ "price": "DECIMAL(10,2)",
567
+ "category_id": "INT"
568
+ },
569
+ "indexes": ["name", "category_id", "price"]
570
+ },
571
+ "orders": {
572
+ "columns": {
573
+ "id": "INT PRIMARY KEY AUTO_INCREMENT",
574
+ "user_id": "INT",
575
+ "status": "VARCHAR(50)",
576
+ "total_amount": "DECIMAL(10,2)",
577
+ "created_at": "TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
578
+ },
579
+ "indexes": ["user_id", "status", "created_at"]
580
+ }
581
+ }