Spaces:
Sleeping
Sleeping
Debug - DB
Browse files- app/database.py +47 -24
app/database.py
CHANGED
|
@@ -2,7 +2,8 @@
|
|
| 2 |
import os
|
| 3 |
from databases import Database
|
| 4 |
from dotenv import load_dotenv
|
| 5 |
-
|
|
|
|
| 6 |
import logging
|
| 7 |
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode
|
| 8 |
|
|
@@ -10,12 +11,10 @@ load_dotenv()
|
|
| 10 |
logger = logging.getLogger(__name__)
|
| 11 |
|
| 12 |
# --- Database URL Configuration ---
|
| 13 |
-
# --- CHANGE THIS LINE: Use the /tmp directory ---
|
| 14 |
DEFAULT_DB_PATH = "/tmp/app.db" # Store DB in the temporary directory
|
| 15 |
-
|
| 16 |
raw_db_url = os.getenv("DATABASE_URL", f"sqlite+aiosqlite:///{DEFAULT_DB_PATH}")
|
| 17 |
|
| 18 |
-
# ---
|
| 19 |
final_database_url = raw_db_url
|
| 20 |
if raw_db_url.startswith("sqlite+aiosqlite"):
|
| 21 |
parsed_url = urlparse(raw_db_url)
|
|
@@ -46,49 +45,73 @@ engine = create_engine(sync_db_url)
|
|
| 46 |
# --- Directory and Table Creation Logic ---
|
| 47 |
db_file_path = ""
|
| 48 |
if sync_db_url.startswith("sqlite"):
|
| 49 |
-
# Path should be absolute starting with /tmp/
|
| 50 |
path_part = sync_db_url.split("sqlite:///")[-1].split("?")[0]
|
| 51 |
db_file_path = path_part # Should be /tmp/app.db
|
| 52 |
|
| 53 |
if db_file_path:
|
| 54 |
-
# --- CHANGE THIS LINE: Check writability of the /tmp directory ---
|
| 55 |
db_dir = os.path.dirname(db_file_path) # Should be /tmp
|
| 56 |
-
logger.info(f"
|
| 57 |
try:
|
| 58 |
-
# /tmp should always exist, but check writability
|
| 59 |
if not os.path.exists(db_dir):
|
| 60 |
-
# This would be very strange, but log it.
|
| 61 |
logger.error(f"CRITICAL: Directory {db_dir} does not exist!")
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
if not os.access(db_dir, os.W_OK):
|
| 65 |
-
# If even /tmp isn't writable, something is very wrong with the environment
|
| 66 |
logger.error(f"CRITICAL: Directory {db_dir} is not writable! Cannot create database.")
|
| 67 |
else:
|
| 68 |
logger.info(f"Database directory {db_dir} appears writable.")
|
| 69 |
-
|
| 70 |
except OSError as e:
|
| 71 |
logger.error(f"Error accessing database directory {db_dir}: {e}")
|
| 72 |
except Exception as e:
|
| 73 |
logger.error(f"Unexpected error checking directory {db_dir}: {e}")
|
| 74 |
|
| 75 |
-
|
|
|
|
| 76 |
try:
|
| 77 |
-
logger.info("Attempting
|
| 78 |
with engine.connect() as connection:
|
|
|
|
| 79 |
try:
|
|
|
|
| 80 |
connection.execute(text("SELECT 1 FROM users LIMIT 1"))
|
| 81 |
-
logger.info("Users table already exists.")
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
except Exception as e:
|
| 88 |
-
#
|
| 89 |
-
logger.exception(f"CRITICAL: Failed
|
|
|
|
| 90 |
|
| 91 |
-
# Async connect/disconnect functions
|
| 92 |
async def connect_db():
|
| 93 |
try:
|
| 94 |
await database.connect()
|
|
|
|
| 2 |
import os
|
| 3 |
from databases import Database
|
| 4 |
from dotenv import load_dotenv
|
| 5 |
+
# --- ADD THIS IMPORT ---
|
| 6 |
+
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, text, exc as sqlalchemy_exc
|
| 7 |
import logging
|
| 8 |
from urllib.parse import urlparse, urlunparse, parse_qs, urlencode
|
| 9 |
|
|
|
|
| 11 |
logger = logging.getLogger(__name__)
|
| 12 |
|
| 13 |
# --- Database URL Configuration ---
|
|
|
|
| 14 |
DEFAULT_DB_PATH = "/tmp/app.db" # Store DB in the temporary directory
|
|
|
|
| 15 |
raw_db_url = os.getenv("DATABASE_URL", f"sqlite+aiosqlite:///{DEFAULT_DB_PATH}")
|
| 16 |
|
| 17 |
+
# --- URL Parsing and Async Database setup (remains the same) ---
|
| 18 |
final_database_url = raw_db_url
|
| 19 |
if raw_db_url.startswith("sqlite+aiosqlite"):
|
| 20 |
parsed_url = urlparse(raw_db_url)
|
|
|
|
| 45 |
# --- Directory and Table Creation Logic ---
|
| 46 |
db_file_path = ""
|
| 47 |
if sync_db_url.startswith("sqlite"):
|
|
|
|
| 48 |
path_part = sync_db_url.split("sqlite:///")[-1].split("?")[0]
|
| 49 |
db_file_path = path_part # Should be /tmp/app.db
|
| 50 |
|
| 51 |
if db_file_path:
|
|
|
|
| 52 |
db_dir = os.path.dirname(db_file_path) # Should be /tmp
|
| 53 |
+
logger.info(f"Checking database directory: {db_dir}")
|
| 54 |
try:
|
|
|
|
| 55 |
if not os.path.exists(db_dir):
|
|
|
|
| 56 |
logger.error(f"CRITICAL: Directory {db_dir} does not exist!")
|
| 57 |
+
elif not os.access(db_dir, os.W_OK):
|
|
|
|
|
|
|
|
|
|
| 58 |
logger.error(f"CRITICAL: Directory {db_dir} is not writable! Cannot create database.")
|
| 59 |
else:
|
| 60 |
logger.info(f"Database directory {db_dir} appears writable.")
|
|
|
|
| 61 |
except OSError as e:
|
| 62 |
logger.error(f"Error accessing database directory {db_dir}: {e}")
|
| 63 |
except Exception as e:
|
| 64 |
logger.error(f"Unexpected error checking directory {db_dir}: {e}")
|
| 65 |
|
| 66 |
+
|
| 67 |
+
# --- Refined Synchronous Table Check/Creation ---
|
| 68 |
try:
|
| 69 |
+
logger.info("Attempting sync connection to check/create table...")
|
| 70 |
with engine.connect() as connection:
|
| 71 |
+
logger.info("Sync engine connection successful.")
|
| 72 |
try:
|
| 73 |
+
# Check if table exists
|
| 74 |
connection.execute(text("SELECT 1 FROM users LIMIT 1"))
|
| 75 |
+
logger.info("Users table already exists (checked via sync connection).")
|
| 76 |
+
# --- Catch specific SQLAlchemy error ---
|
| 77 |
+
except sqlalchemy_exc.OperationalError as table_missing_err:
|
| 78 |
+
# Check if the error message specifically indicates "no such table"
|
| 79 |
+
if "no such table" in str(table_missing_err).lower():
|
| 80 |
+
logger.warning("Users table not found (expected), attempting creation...")
|
| 81 |
+
try:
|
| 82 |
+
# Begin a transaction explicitly (optional, connect() usually does)
|
| 83 |
+
# with connection.begin(): # Alternative way to manage transaction
|
| 84 |
+
# Use the connection object for create_all
|
| 85 |
+
metadata.create_all(bind=connection) # <-- Bind to connection
|
| 86 |
+
# --- Explicitly commit ---
|
| 87 |
+
connection.commit()
|
| 88 |
+
logger.info("Users table creation attempted and committed via sync connection.")
|
| 89 |
+
# --- Verify creation immediately ---
|
| 90 |
+
try:
|
| 91 |
+
connection.execute(text("SELECT 1 FROM users LIMIT 1"))
|
| 92 |
+
logger.info("Users table successfully verified immediately after creation.")
|
| 93 |
+
except Exception as verify_err:
|
| 94 |
+
logger.error(f"Failed to verify table immediately after creation: {verify_err}")
|
| 95 |
+
|
| 96 |
+
except Exception as creation_err:
|
| 97 |
+
logger.exception(f"Error during table creation or commit: {creation_err}")
|
| 98 |
+
# Optionally rollback? connection.rollback()
|
| 99 |
+
|
| 100 |
+
else:
|
| 101 |
+
# Log other OperationalErrors during the check phase
|
| 102 |
+
logger.error(f"OperationalError during table check (but not 'no such table'): {table_missing_err}")
|
| 103 |
+
raise # Re-raise unexpected errors
|
| 104 |
+
|
| 105 |
+
except Exception as table_check_exc: # Catch other unexpected errors during check
|
| 106 |
+
logger.error(f"Unexpected error during table check: {type(table_check_exc).__name__}: {table_check_exc}")
|
| 107 |
+
raise # Re-raise unexpected errors
|
| 108 |
|
| 109 |
except Exception as e:
|
| 110 |
+
# Errors connecting, or unexpected errors during check/create phase
|
| 111 |
+
logger.exception(f"CRITICAL: Failed during sync connection or table setup: {e}")
|
| 112 |
+
|
| 113 |
|
| 114 |
+
# --- Async connect/disconnect functions (remain the same) ---
|
| 115 |
async def connect_db():
|
| 116 |
try:
|
| 117 |
await database.connect()
|