Fix migration

This commit is contained in:
2025-08-25 18:49:33 +09:00
parent 6886ba15c8
commit 8e3f7024a2
12 changed files with 2254 additions and 20 deletions

179
check_null_values.py Normal file
View File

@ -0,0 +1,179 @@
#!/usr/bin/env python3
"""
NULL値チェック・デフォルト値テストスクリプト
"""
import os
import psycopg2
import logging
# ログ設定
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# データベース設定
OLD_ROGDB_CONFIG = {
'host': os.getenv('OLD_ROGDB_HOST', 'postgres-db'),
'database': os.getenv('OLD_ROGDB_NAME', 'old_rogdb'),
'user': os.getenv('OLD_ROGDB_USER', 'admin'),
'password': os.getenv('OLD_ROGDB_PASSWORD', 'admin123456'),
'port': int(os.getenv('OLD_ROGDB_PORT', 5432))
}
ROGDB_CONFIG = {
'host': os.getenv('ROGDB_HOST', 'postgres-db'),
'database': os.getenv('ROGDB_NAME', 'rogdb'),
'user': os.getenv('ROGDB_USER', 'admin'),
'password': os.getenv('ROGDB_PASSWORD', 'admin123456'),
'port': int(os.getenv('ROGDB_PORT', 5432))
}
def check_null_values():
"""NULL値の問題を事前チェック"""
try:
old_conn = psycopg2.connect(**OLD_ROGDB_CONFIG)
new_conn = psycopg2.connect(**ROGDB_CONFIG)
old_conn.autocommit = True
new_conn.autocommit = True
old_cursor = old_conn.cursor()
new_cursor = new_conn.cursor()
# 共通テーブル取得
old_cursor.execute("""
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public' AND table_name LIKE 'rog_%'
""")
old_tables = [row[0] for row in old_cursor.fetchall()]
new_cursor.execute("""
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public' AND table_name LIKE 'rog_%'
""")
new_tables = [row[0] for row in new_cursor.fetchall()]
common_tables = list(set(old_tables) & set(new_tables))
logger.info(f"チェック対象テーブル: {len(common_tables)}")
null_issues = {}
for table_name in common_tables:
logger.info(f"=== {table_name} NULL値チェック ===")
# 新しいDBのNOT NULL制約確認
new_cursor.execute("""
SELECT column_name, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = %s AND table_schema = 'public'
AND is_nullable = 'NO'
ORDER BY ordinal_position
""", (table_name,))
not_null_columns = new_cursor.fetchall()
if not not_null_columns:
logger.info(f" NOT NULL制約なし")
continue
logger.info(f" NOT NULL制約カラム: {[col[0] for col in not_null_columns]}")
# 古いDBのNULL値チェック
for col_name, is_nullable, default_val in not_null_columns:
try:
# PostgreSQL予約語とcamelCaseカラムのクォート処理
reserved_words = ['group', 'like', 'order', 'user', 'table', 'index', 'where', 'from', 'select']
quoted_col = f'"{col_name}"' if (col_name.lower() in reserved_words or any(c.isupper() for c in col_name)) else col_name
# カラム存在チェック
old_cursor.execute("""
SELECT COUNT(*) FROM information_schema.columns
WHERE table_name = %s AND column_name = %s AND table_schema = 'public'
""", (table_name, col_name))
if old_cursor.fetchone()[0] == 0:
logger.warning(f" ⚠️ {col_name}: 古いDBに存在しないカラム")
continue
old_cursor.execute(f"""
SELECT COUNT(*) FROM {table_name}
WHERE {quoted_col} IS NULL
""")
null_count = old_cursor.fetchone()[0]
if null_count > 0:
logger.warning(f" ⚠️ {col_name}: {null_count}件のNULL値あり (デフォルト: {default_val})")
if table_name not in null_issues:
null_issues[table_name] = []
null_issues[table_name].append((col_name, null_count, default_val))
else:
logger.info(f"{col_name}: NULL値なし")
except Exception as e:
logger.error(f"{col_name}: チェックエラー: {e}")
# サマリー
if null_issues:
logger.warning("=" * 60)
logger.warning("NULL値問題のあるテーブル:")
for table, issues in null_issues.items():
logger.warning(f" {table}:")
for col, count, default in issues:
logger.warning(f" - {col}: {count}件 (デフォルト: {default})")
else:
logger.info("✅ NULL値の問題はありません")
old_cursor.close()
new_cursor.close()
old_conn.close()
new_conn.close()
return null_issues
except Exception as e:
logger.error(f"❌ NULL値チェックエラー: {e}")
return {}
def suggest_default_values(null_issues):
"""デフォルト値の提案"""
if not null_issues:
return
logger.info("=" * 60)
logger.info("推奨デフォルト値設定:")
for table_name, issues in null_issues.items():
logger.info(f" '{table_name}': {{")
for col_name, count, default in issues:
# データ型に基づくデフォルト値推測
if 'trial' in col_name.lower() or 'is_' in col_name.lower():
suggested = 'False'
elif 'public' in col_name.lower():
suggested = 'True'
elif 'name' in col_name.lower() or 'description' in col_name.lower():
suggested = "''"
elif 'order' in col_name.lower() or 'sort' in col_name.lower():
suggested = '0'
else:
suggested = 'None # 要確認'
logger.info(f" '{col_name}': {suggested}, # {count}件のNULL")
logger.info(" },")
def main():
logger.info("=" * 60)
logger.info("NULL値チェック・デフォルト値提案スクリプト")
logger.info("=" * 60)
null_issues = check_null_values()
suggest_default_values(null_issues)
logger.info("=" * 60)
logger.info("チェック完了")
logger.info("=" * 60)
if __name__ == "__main__":
main()