@@ -142,7 +142,9 @@ def refresh_collation_version(self):
142142 cursor = conn .cursor ()
143143
144144 db_name = self .staging_db_config ["database" ]
145- cursor .execute (f'ALTER DATABASE "{ db_name } " REFRESH COLLATION VERSION' )
145+ cursor .execute (
146+ f'ALTER DATABASE "{ db_name } " REFRESH COLLATION VERSION'
147+ )
146148 logger .info (f"✓ Refreshed collation version for database { db_name } " )
147149
148150 except psycopg2 .Error as e :
@@ -347,7 +349,9 @@ def copy_recent_scripts(self, superadmin_id):
347349 """
348350 SELECT id, name, slug, description, created_at, updated_at, status,
349351 public, cpu_reservation, cpu_limit, memory_reservation,
350- memory_limit, environment, environment_version
352+ memory_limit, environment, environment_version,
353+ COALESCE(restricted, false) as restricted,
354+ allowed_roles, allowed_users, build_error
351355 FROM script
352356 WHERE created_at >= %s OR updated_at >= %s
353357 ORDER BY created_at ASC
@@ -401,9 +405,11 @@ def copy_recent_scripts(self, superadmin_id):
401405 updated_at, user_id, status, public,
402406 cpu_reservation, cpu_limit,
403407 memory_reservation, memory_limit,
404- environment, environment_version)
408+ environment, environment_version,
409+ restricted, allowed_roles, allowed_users,
410+ build_error)
405411 VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s,
406- %s, %s, %s)
412+ %s, %s, %s, %s, %s, %s, %s )
407413 ON CONFLICT (slug) DO UPDATE SET
408414 id = EXCLUDED.id,
409415 name = EXCLUDED.name,
@@ -417,7 +423,11 @@ def copy_recent_scripts(self, superadmin_id):
417423 memory_reservation = EXCLUDED.memory_reservation,
418424 memory_limit = EXCLUDED.memory_limit,
419425 environment = EXCLUDED.environment,
420- environment_version = EXCLUDED.environment_version
426+ environment_version = EXCLUDED.environment_version,
427+ restricted = EXCLUDED.restricted,
428+ allowed_roles = EXCLUDED.allowed_roles,
429+ allowed_users = EXCLUDED.allowed_users,
430+ build_error = EXCLUDED.build_error
421431 RETURNING id, (xmax = 0) AS inserted
422432 """ ,
423433 (
@@ -436,6 +446,10 @@ def copy_recent_scripts(self, superadmin_id):
436446 script [11 ], # memory_limit
437447 script [12 ], # environment
438448 script [13 ], # environment_version
449+ script [14 ], # restricted
450+ script [15 ], # allowed_roles
451+ script [16 ], # allowed_users
452+ script [17 ], # build_error
439453 ),
440454 )
441455
@@ -597,16 +611,58 @@ def copy_recent_status_logs(self):
597611 # Calculate date one month ago
598612 one_month_ago = datetime .now (UTC ) - timedelta (days = 30 )
599613
600- # Get recent status logs from production (excluding ID for reassignment)
614+ # Get recent status logs from production
615+ # Note: Production may have different column names, so we query
616+ # dynamically based on what exists
601617 prod_cursor .execute (
602618 """
603- SELECT timestamp, executions_active, executions_ready,
604- executions_running, executions_finished, executions_failed,
605- executions_count, users_count, scripts_count
619+ SELECT column_name FROM information_schema.columns
620+ WHERE table_name = 'status_log'
621+ ORDER BY ordinal_position
622+ """
623+ )
624+ prod_columns = [row [0 ] for row in prod_cursor .fetchall ()]
625+ logger .info (f"Production status_log columns: { prod_columns } " )
626+
627+ # Skip status log import if schemas don't match - not critical
628+ # The status_log table may have different schemas between prod and staging
629+ staging_cursor .execute (
630+ """
631+ SELECT column_name FROM information_schema.columns
632+ WHERE table_name = 'status_log'
633+ ORDER BY ordinal_position
634+ """
635+ )
636+ staging_columns = [row [0 ] for row in staging_cursor .fetchall ()]
637+ logger .info (f"Staging status_log columns: { staging_columns } " )
638+
639+ # Find common columns (excluding 'id' which we'll auto-generate)
640+ common_columns = [
641+ col for col in staging_columns
642+ if col in prod_columns and col != 'id'
643+ ]
644+ logger .info (f"Common columns for import: { common_columns } " )
645+
646+ if not common_columns or 'timestamp' not in common_columns :
647+ logger .warning (
648+ "No compatible columns found between production and staging "
649+ "status_log tables. Skipping status log import."
650+ )
651+ return
652+
653+ # Build dynamic query with common columns
654+ columns_str = ', ' .join (common_columns )
655+ placeholders = ', ' .join (['%s' ] * len (common_columns ))
656+
657+ # Note: columns_str is derived from information_schema, not user input
658+ # so this is safe from SQL injection
659+ prod_cursor .execute (
660+ f"""
661+ SELECT { columns_str }
606662 FROM status_log
607663 WHERE timestamp >= %s
608664 ORDER BY timestamp ASC
609- """ ,
665+ """ , # noqa: S608
610666 (one_month_ago ,),
611667 )
612668
@@ -616,16 +672,13 @@ def copy_recent_status_logs(self):
616672 imported_count = 0
617673 for log in status_logs :
618674 try :
619- # Insert without ID to let the sequence generate new IDs
675+ # Insert using dynamic columns (columns derived from
676+ # information_schema, safe from SQL injection)
620677 staging_cursor .execute (
621- """
622- INSERT INTO status_log (timestamp, executions_active,
623- executions_ready, executions_running,
624- executions_finished, executions_failed,
625- executions_count, users_count,
626- scripts_count)
627- VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
628- """ ,
678+ f"""
679+ INSERT INTO status_log ({ columns_str } )
680+ VALUES ({ placeholders } )
681+ """ , # noqa: S608
629682 log ,
630683 )
631684 imported_count += 1
0 commit comments