Spaces:
Running
Running
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()
|