Skip to content

Commit 2828893

Browse files
authored
fix: skip migration if old tables are not found (#74)
- Added a function to check for the existence of source tables before migration, improving robustness. - Implemented logging to provide feedback on the migration process, including the number of records processed and any errors encountered. - Refactored migration logic to handle cases where source tables do not exist, ensuring graceful handling of missing data.
1 parent b9b2edf commit 2828893

File tree

1 file changed

+111
-67
lines changed

1 file changed

+111
-67
lines changed

src/backend/database/migrations/versions/2025_05_02_2055-migrate_canvas_data.py

Lines changed: 111 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
Create Date: 2025-05-02 20:55:00.000000
66
77
"""
8+
import logging
89
from alembic import op
910
import sqlalchemy as sa
1011
from sqlalchemy.dialects.postgresql import UUID, JSONB
@@ -35,6 +36,20 @@
3536
# Get SCHEMA_NAME from the loaded module
3637
SCHEMA_NAME = base_model.SCHEMA_NAME
3738

39+
def table_exists(connection, table_name, schema='public'):
40+
"""Check if a table exists in the database"""
41+
query = sa.text(
42+
"""
43+
SELECT EXISTS (
44+
SELECT FROM information_schema.tables
45+
WHERE table_schema = :schema
46+
AND table_name = :table_name
47+
)
48+
"""
49+
)
50+
result = connection.execute(query, {"schema": schema, "table_name": table_name}).scalar()
51+
return bool(result)
52+
3853
def upgrade() -> None:
3954
"""Migrate data from old tables to new schema"""
4055

@@ -44,6 +59,14 @@ def upgrade() -> None:
4459
# Define tables for direct SQL operations
4560
metadata = sa.MetaData()
4661

62+
# Check if the source tables exist
63+
canvas_data_exists = table_exists(connection, 'canvas_data')
64+
canvas_backups_exists = table_exists(connection, 'canvas_backups')
65+
66+
if not canvas_data_exists and not canvas_backups_exists:
67+
logging.info("Source tables 'canvas_data' and 'canvas_backups' do not exist. Skipping data migration.")
68+
return
69+
4770
# Define the old tables in the public schema
4871
canvas_data = sa.Table(
4972
'canvas_data',
@@ -102,86 +125,107 @@ def upgrade() -> None:
102125
session = Session(connection)
103126

104127
try:
105-
# Step 1: Get all canvas_data records
106-
canvas_data_records = session.execute(sa.select(canvas_data)).fetchall()
107-
108128
# Dictionary to store user_id -> pad_id mapping for later use with backups
109129
user_pad_mapping = {}
110130

111-
# Step 2: For each canvas_data record, create a new pad
112-
for record in canvas_data_records:
113-
user_id = record.user_id
114-
115-
# Check if the user exists in the new schema
116-
user_exists = session.execute(
117-
sa.select(users).where(users.c.id == user_id)
118-
).fetchone()
131+
# Step 1: Process canvas_data if it exists
132+
if canvas_data_exists:
133+
try:
134+
# Get all canvas_data records
135+
canvas_data_records = session.execute(sa.select(canvas_data)).fetchall()
136+
logging.info(f"Found {len(canvas_data_records)} records in canvas_data table")
137+
138+
# Step 2: For each canvas_data record, create a new pad
139+
for record in canvas_data_records:
140+
user_id = record.user_id
119141

120-
if not user_exists:
121-
print(f"User {user_id} not found in new schema, creating with placeholder data")
122-
# Create a new user with placeholder data
123-
# The real data will be updated when the user accesses the /me route
124-
session.execute(
125-
users.insert().values(
126-
id=user_id,
127-
username=f"migrated_user_{user_id}",
128-
email=f"migrated_{user_id}@example.com",
129-
email_verified=False,
130-
name="Migrated User",
131-
given_name="Migrated",
132-
family_name="User",
133-
roles=[],
142+
# Check if the user exists in the new schema
143+
user_exists = session.execute(
144+
sa.select(users).where(users.c.id == user_id)
145+
).fetchone()
146+
147+
if not user_exists:
148+
logging.info(f"User {user_id} not found in new schema, creating with placeholder data")
149+
# Create a new user with placeholder data
150+
# The real data will be updated when the user accesses the /me route
151+
session.execute(
152+
users.insert().values(
153+
id=user_id,
154+
username=f"migrated_user_{user_id}",
155+
email=f"migrated_{user_id}@example.com",
156+
email_verified=False,
157+
name="Migrated User",
158+
given_name="Migrated",
159+
family_name="User",
160+
roles=[],
161+
)
162+
)
163+
164+
# Generate a new UUID for the pad
165+
pad_id = uuid.uuid4()
166+
167+
# Store the mapping for later use
168+
user_pad_mapping[user_id] = pad_id
169+
170+
# Insert the pad record
171+
session.execute(
172+
pads.insert().values(
173+
id=pad_id,
174+
owner_id=user_id,
175+
display_name="Untitled",
176+
data=record.data,
177+
)
134178
)
135-
)
136-
137-
# Generate a new UUID for the pad
138-
pad_id = uuid.uuid4()
139-
140-
# Store the mapping for later use
141-
user_pad_mapping[user_id] = pad_id
142-
143-
# Insert the pad record
144-
session.execute(
145-
pads.insert().values(
146-
id=pad_id,
147-
owner_id=user_id,
148-
display_name="Untitled",
149-
data=record.data,
150-
)
151-
)
179+
except Exception as e:
180+
logging.error(f"Error processing canvas_data: {e}")
181+
session.rollback()
182+
raise
152183

153-
# Step 3: Get all canvas_backups records
154-
canvas_backup_records = session.execute(sa.select(canvas_backups)).fetchall()
155-
156-
# Step 4: For each canvas_backup record, create a new backup
157-
for record in canvas_backup_records:
158-
user_id = record.user_id
159-
160-
# Skip if we don't have a pad for this user
161-
if user_id not in user_pad_mapping:
162-
print(f"Warning: No pad found for user {user_id}, skipping backup")
163-
continue
164-
165-
pad_id = user_pad_mapping[user_id]
166-
167-
# Insert the backup record
168-
session.execute(
169-
backups.insert().values(
170-
id=uuid.uuid4(),
171-
source_id=pad_id,
172-
data=record.canvas_data, # Note: using canvas_data field from the record
173-
created_at=record.timestamp,
174-
)
175-
)
184+
# Step 3: Process canvas_backups if it exists
185+
if canvas_backups_exists and user_pad_mapping: # Only process backups if we have pads
186+
try:
187+
# Get all canvas_backups records
188+
canvas_backup_records = session.execute(sa.select(canvas_backups)).fetchall()
189+
logging.info(f"Found {len(canvas_backup_records)} records in canvas_backups table")
190+
191+
# Step 4: For each canvas_backup record, create a new backup
192+
for record in canvas_backup_records:
193+
user_id = record.user_id
194+
195+
# Skip if we don't have a pad for this user
196+
if user_id not in user_pad_mapping:
197+
logging.warning(f"No pad found for user {user_id}, skipping backup")
198+
continue
199+
200+
pad_id = user_pad_mapping[user_id]
201+
202+
# Insert the backup record
203+
session.execute(
204+
backups.insert().values(
205+
id=uuid.uuid4(),
206+
source_id=pad_id,
207+
data=record.canvas_data, # Note: using canvas_data field from the record
208+
created_at=record.timestamp,
209+
)
210+
)
211+
except Exception as e:
212+
logging.error(f"Error processing canvas_backups: {e}")
213+
session.rollback()
214+
raise
176215

177216
# Commit the transaction
178217
session.commit()
179218

180-
print(f"Migration complete: {len(canvas_data_records)} pads and {len(canvas_backup_records)} backups migrated")
219+
if canvas_data_exists or canvas_backups_exists:
220+
pad_count = len(user_pad_mapping) if canvas_data_exists else 0
221+
backup_count = len(canvas_backup_records) if canvas_backups_exists and 'canvas_backup_records' in locals() else 0
222+
logging.info(f"Migration complete: {pad_count} pads and {backup_count} backups migrated")
223+
else:
224+
logging.info("No data to migrate")
181225

182226
except Exception as e:
183227
session.rollback()
184-
print(f"Error during migration: {e}")
228+
logging.error(f"Error during migration: {e}")
185229
raise
186230
finally:
187231
session.close()

0 commit comments

Comments
 (0)