QueryBuilder/app/services/database_service.py
2025-10-14 21:27:41 +02:00

268 lines
9.7 KiB
Python

import os
from flask import current_app
import logging
# Oracle-Unterstützung
try:
import oracledb
ORACLE_AVAILABLE = True
except ImportError:
try:
import cx_Oracle
ORACLE_AVAILABLE = True
except ImportError:
ORACLE_AVAILABLE = False
logging.warning("Oracle-Treiber nicht verfügbar. Installiere cx-Oracle oder oracledb für Oracle-Unterstützung.")
# Weitere Datenbank-Treiber
try:
import psycopg2
POSTGRESQL_AVAILABLE = True
except ImportError:
POSTGRESQL_AVAILABLE = False
try:
import pymysql
MYSQL_AVAILABLE = True
except ImportError:
MYSQL_AVAILABLE = False
class DatabaseService:
def __init__(self, connection_config=None):
"""
Initialisiere Database Service - Nur Oracle-Unterstützung
connection_config: Dictionary mit Oracle-Verbindungsparametern
"""
if connection_config is None:
raise ValueError("Oracle-Verbindungskonfiguration erforderlich")
self.connection_config = connection_config
self.db_type = connection_config.get('type', 'oracle')
if self.db_type == 'oracle':
self._validate_oracle_config()
else:
raise ValueError(f"Datenbanktyp '{self.db_type}' wird nicht unterstützt. Nur Oracle ist verfügbar.")
def _validate_oracle_config(self):
"""Validiere Oracle-Konfiguration"""
if not ORACLE_AVAILABLE:
raise Exception("Oracle-Treiber nicht verfügbar. Installiere cx-Oracle oder oracledb.")
required_fields = ['host', 'username', 'password']
for field in required_fields:
if not self.connection_config.get(field):
raise Exception(f"Oracle-Konfiguration unvollständig: {field} fehlt")
def execute_query(self, query):
"""Führe Oracle SQL-Query aus und gebe Ergebnisse zurück"""
return self._execute_oracle_query(query)
def _execute_oracle_query(self, query):
"""Führe Oracle Query aus"""
if not ORACLE_AVAILABLE:
raise Exception("Oracle-Treiber nicht verfügbar")
# Erstelle Oracle-Verbindung
config = self.connection_config
dsn = f"{config['host']}:{config.get('port', 1521)}/{config.get('service_name', 'ORCL')}"
try:
# Verwende den neueren oracledb-Treiber wenn verfügbar, sonst cx_Oracle
try:
import oracledb
conn = oracledb.connect(
user=config['username'],
password=config['password'],
dsn=dsn
)
except ImportError:
import cx_Oracle
conn = cx_Oracle.connect(
user=config['username'],
password=config['password'],
dsn=dsn
)
cursor = conn.cursor()
try:
# Entferne Semikolon am Ende (Oracle mag das nicht bei einzelnen Statements)
cleaned_query = query.strip()
if cleaned_query.endswith(';'):
cleaned_query = cleaned_query[:-1]
cursor.execute(cleaned_query)
# Wenn es eine SELECT-Query ist, hole Ergebnisse
if query.strip().lower().startswith('select'):
columns = [desc[0] for desc in cursor.description]
data = cursor.fetchall()
# Konvertiere Oracle-spezifische Datentypen
processed_data = []
for row in data:
processed_row = []
for value in row:
# Konvertiere LOB, CLOB, BLOB zu String
if hasattr(value, 'read'):
processed_row.append(value.read())
# Konvertiere Oracle NUMBER zu Python-Typen
elif str(type(value)).find('NUMBER') != -1:
processed_row.append(float(value) if '.' in str(value) else int(value))
else:
processed_row.append(value)
processed_data.append(processed_row)
return {
'columns': columns,
'data': processed_data,
'row_count': len(processed_data)
}
else:
# Für INSERT, UPDATE, DELETE etc.
conn.commit()
return {
'message': f'Query erfolgreich ausgeführt. {cursor.rowcount} Zeilen betroffen.',
'affected_rows': cursor.rowcount
}
except Exception as e:
conn.rollback()
raise e
finally:
cursor.close()
except Exception as e:
raise Exception(f"Oracle-Datenbankfehler: {str(e)}")
finally:
if 'conn' in locals():
conn.close()
def get_tables(self):
"""Hole alle Oracle Tabellennamen"""
return self._get_oracle_tables()
def _get_oracle_tables(self):
"""Hole Oracle Tabellennamen"""
if not ORACLE_AVAILABLE:
raise Exception("Oracle-Treiber nicht verfügbar")
config = self.connection_config
dsn = f"{config['host']}:{config.get('port', 1521)}/{config.get('service_name', 'ORCL')}"
try:
try:
import oracledb
conn = oracledb.connect(
user=config['username'],
password=config['password'],
dsn=dsn
)
except ImportError:
import cx_Oracle
conn = cx_Oracle.connect(
user=config['username'],
password=config['password'],
dsn=dsn
)
cursor = conn.cursor()
# Hole alle Tabellen für den aktuellen Benutzer
cursor.execute("""
SELECT table_name
FROM user_tables
ORDER BY table_name
""")
tables = [row[0] for row in cursor.fetchall()]
cursor.close()
conn.close()
return tables
except Exception as e:
raise Exception(f"Fehler beim Laden der Oracle-Tabellen: {str(e)}")
def get_table_schema(self, table_name):
"""Hole Oracle Tabellenschema"""
return self._get_oracle_table_schema(table_name)
def _get_oracle_table_schema(self, table_name):
"""Hole Oracle Tabellenschema"""
if not ORACLE_AVAILABLE:
raise Exception("Oracle-Treiber nicht verfügbar")
config = self.connection_config
dsn = f"{config['host']}:{config.get('port', 1521)}/{config.get('service_name', 'ORCL')}"
try:
try:
import oracledb
conn = oracledb.connect(
user=config['username'],
password=config['password'],
dsn=dsn
)
except ImportError:
import cx_Oracle
conn = cx_Oracle.connect(
user=config['username'],
password=config['password'],
dsn=dsn
)
cursor = conn.cursor()
# Hole Spalten-Informationen
cursor.execute("""
SELECT
c.column_name,
c.data_type,
c.data_length,
c.data_precision,
c.data_scale,
c.nullable,
c.data_default,
CASE WHEN p.column_name IS NOT NULL THEN 'Y' ELSE 'N' END as primary_key
FROM user_tab_columns c
LEFT JOIN (
SELECT acc.column_name, acc.table_name
FROM user_constraints ac, user_cons_columns acc
WHERE ac.constraint_name = acc.constraint_name
AND ac.constraint_type = 'P'
AND ac.table_name = :table_name
) p ON c.column_name = p.column_name AND c.table_name = p.table_name
WHERE c.table_name = :table_name
ORDER BY c.column_id
""", {'table_name': table_name.upper()})
columns = []
for row in cursor.fetchall():
data_type = row[1]
if row[2]: # data_length
if row[3]: # data_precision
data_type += f"({row[3]}"
if row[4]: # data_scale
data_type += f",{row[4]}"
data_type += ")"
elif row[1] in ('VARCHAR2', 'CHAR', 'NVARCHAR2', 'NCHAR'):
data_type += f"({row[2]})"
columns.append({
'name': row[0],
'type': data_type,
'not_null': row[5] == 'N',
'default_value': row[6],
'primary_key': row[7] == 'Y'
})
cursor.close()
conn.close()
return columns
except Exception as e:
raise Exception(f"Fehler beim Laden des Oracle-Tabellenschemas: {str(e)}")