|
@@ -2,6 +2,7 @@ import sqlite3
|
|
|
import psycopg2
|
|
import psycopg2
|
|
|
import os
|
|
import os
|
|
|
import sys
|
|
import sys
|
|
|
|
|
+import datetime
|
|
|
from dotenv import load_dotenv
|
|
from dotenv import load_dotenv
|
|
|
from psycopg2 import sql
|
|
from psycopg2 import sql
|
|
|
from collections import defaultdict
|
|
from collections import defaultdict
|
|
@@ -53,10 +54,25 @@ class SQLiteToPostgresMigrator:
|
|
|
|
|
|
|
|
# 存储字段长度限制
|
|
# 存储字段长度限制
|
|
|
self.column_lengths = {}
|
|
self.column_lengths = {}
|
|
|
|
|
+
|
|
|
|
|
+ # 日志文件
|
|
|
|
|
+ self.log_file = None
|
|
|
|
|
+ self.log_file_path = f"migration_log_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
|
|
|
|
|
+
|
|
|
|
|
+ def log(self, message):
|
|
|
|
|
+ """记录日志到控制台和文件"""
|
|
|
|
|
+ print(message)
|
|
|
|
|
+ if self.log_file:
|
|
|
|
|
+ self.log_file.write(message + "\n")
|
|
|
|
|
+ self.log_file.flush()
|
|
|
|
|
|
|
|
def connect(self):
|
|
def connect(self):
|
|
|
"""连接到数据库"""
|
|
"""连接到数据库"""
|
|
|
try:
|
|
try:
|
|
|
|
|
+ # 打开日志文件
|
|
|
|
|
+ self.log_file = open(self.log_file_path, "w", encoding="utf-8")
|
|
|
|
|
+ self.log(f"迁移日志文件: {self.log_file_path}")
|
|
|
|
|
+
|
|
|
# SQLite 连接
|
|
# SQLite 连接
|
|
|
self.sqlite_conn = sqlite3.connect(self.sqlite_db_path)
|
|
self.sqlite_conn = sqlite3.connect(self.sqlite_db_path)
|
|
|
self.sqlite_cursor = self.sqlite_conn.cursor()
|
|
self.sqlite_cursor = self.sqlite_conn.cursor()
|
|
@@ -64,10 +80,10 @@ class SQLiteToPostgresMigrator:
|
|
|
# PostgreSQL 连接
|
|
# PostgreSQL 连接
|
|
|
self.pg_conn = psycopg2.connect(**self.pg_config)
|
|
self.pg_conn = psycopg2.connect(**self.pg_config)
|
|
|
self.pg_cursor = self.pg_conn.cursor()
|
|
self.pg_cursor = self.pg_conn.cursor()
|
|
|
- print(f"连接成功: SQLite({self.sqlite_db_path}) → PostgreSQL({self.pg_config['dbname']})")
|
|
|
|
|
|
|
+ self.log(f"连接成功: SQLite({self.sqlite_db_path}) → PostgreSQL({self.pg_config['dbname']})")
|
|
|
return True
|
|
return True
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
- print(f"连接失败: {str(e)}")
|
|
|
|
|
|
|
+ self.log(f"连接失败: {str(e)}")
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
def close(self):
|
|
def close(self):
|
|
@@ -76,14 +92,17 @@ class SQLiteToPostgresMigrator:
|
|
|
if cursor: cursor.close()
|
|
if cursor: cursor.close()
|
|
|
for conn in [self.sqlite_conn, self.pg_conn]:
|
|
for conn in [self.sqlite_conn, self.pg_conn]:
|
|
|
if conn: conn.close()
|
|
if conn: conn.close()
|
|
|
|
|
+ if self.log_file:
|
|
|
|
|
+ self.log_file.close()
|
|
|
|
|
+ self.log(f"日志文件已保存: {self.log_file_path}")
|
|
|
|
|
|
|
|
def clear_postgres_data(self):
|
|
def clear_postgres_data(self):
|
|
|
"""清空 PostgreSQL 数据库中的所有数据"""
|
|
"""清空 PostgreSQL 数据库中的所有数据"""
|
|
|
- print("\n警告: 此操作将清空 PostgreSQL 数据库中的所有数据!")
|
|
|
|
|
|
|
+ self.log("\n警告: 此操作将清空 PostgreSQL 数据库中的所有数据!")
|
|
|
confirm = input("确定要清空 PostgreSQL 数据库吗? (y/n): ")
|
|
confirm = input("确定要清空 PostgreSQL 数据库吗? (y/n): ")
|
|
|
|
|
|
|
|
if confirm.lower() != 'y':
|
|
if confirm.lower() != 'y':
|
|
|
- print("取消清空操作")
|
|
|
|
|
|
|
+ self.log("取消清空操作")
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
@@ -99,17 +118,17 @@ class SQLiteToPostgresMigrator:
|
|
|
for table in tables:
|
|
for table in tables:
|
|
|
try:
|
|
try:
|
|
|
self.pg_cursor.execute(f"TRUNCATE TABLE {table} CASCADE;")
|
|
self.pg_cursor.execute(f"TRUNCATE TABLE {table} CASCADE;")
|
|
|
- print(f"已清空表: {table}")
|
|
|
|
|
|
|
+ self.log(f"已清空表: {table}")
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
- print(f"清空表 {table} 时出错: {str(e)}")
|
|
|
|
|
|
|
+ self.log(f"清空表 {table} 时出错: {str(e)}")
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
self.pg_conn.commit()
|
|
self.pg_conn.commit()
|
|
|
- print("成功清空 PostgreSQL 数据库中的所有数据")
|
|
|
|
|
|
|
+ self.log("成功清空 PostgreSQL 数据库中的所有数据")
|
|
|
return True
|
|
return True
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
self.pg_conn.rollback()
|
|
self.pg_conn.rollback()
|
|
|
- print(f"清空数据库时出错: {str(e)}")
|
|
|
|
|
|
|
+ self.log(f"清空数据库时出错: {str(e)}")
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
def analyze_dependencies(self):
|
|
def analyze_dependencies(self):
|
|
@@ -148,7 +167,7 @@ class SQLiteToPostgresMigrator:
|
|
|
visit(table)
|
|
visit(table)
|
|
|
|
|
|
|
|
self.migration_order = order
|
|
self.migration_order = order
|
|
|
- print(f"分析完成: 发现 {len(tables)} 个表, 依赖顺序已确定")
|
|
|
|
|
|
|
+ self.log(f"分析完成: 发现 {len(tables)} 个表, 依赖顺序已确定")
|
|
|
|
|
|
|
|
def get_pg_table_structure(self, table_name):
|
|
def get_pg_table_structure(self, table_name):
|
|
|
"""获取 PostgreSQL 表结构"""
|
|
"""获取 PostgreSQL 表结构"""
|
|
@@ -216,10 +235,33 @@ class SQLiteToPostgresMigrator:
|
|
|
if len(value) > max_length:
|
|
if len(value) > max_length:
|
|
|
# 截断超过长度的字符串
|
|
# 截断超过长度的字符串
|
|
|
truncated = value[:max_length]
|
|
truncated = value[:max_length]
|
|
|
- print(f" 警告: 字段 '{col_name}' 值被截断 ({len(value)} -> {max_length})")
|
|
|
|
|
|
|
+ self.log(f" 警告: 字段 '{col_name}' 值被截断 ({len(value)} -> {max_length})")
|
|
|
transformed.append(truncated)
|
|
transformed.append(truncated)
|
|
|
else:
|
|
else:
|
|
|
transformed.append(value)
|
|
transformed.append(value)
|
|
|
|
|
+ # 处理整数范围限制
|
|
|
|
|
+ elif pg_type in ('integer', 'int', 'int4'):
|
|
|
|
|
+ # 检查值是否在PostgreSQL整数范围内
|
|
|
|
|
+ if isinstance(value, int) and (value < -2147483648 or value > 2147483647):
|
|
|
|
|
+ # 尝试转换为bigint
|
|
|
|
|
+ try:
|
|
|
|
|
+ self.log(f" 警告: 字段 '{col_name}' 值超出整数范围 ({value}),尝试转换为bigint")
|
|
|
|
|
+ transformed.append(value)
|
|
|
|
|
+ except Exception:
|
|
|
|
|
+ self.log(f" 错误: 无法转换字段 '{col_name}' 的值 ({value})")
|
|
|
|
|
+ transformed.append(None)
|
|
|
|
|
+ else:
|
|
|
|
|
+ transformed.append(value)
|
|
|
|
|
+ # 处理bigint范围限制
|
|
|
|
|
+ elif pg_type in ('bigint', 'int8'):
|
|
|
|
|
+ # 检查值是否在bigint范围内
|
|
|
|
|
+ bigint_min = -9223372036854775808
|
|
|
|
|
+ bigint_max = 9223372036854775807
|
|
|
|
|
+ if isinstance(value, int) and (value < bigint_min or value > bigint_max):
|
|
|
|
|
+ self.log(f" 错误: 字段 '{col_name}' 值超出bigint范围 ({value})")
|
|
|
|
|
+ transformed.append(None)
|
|
|
|
|
+ else:
|
|
|
|
|
+ transformed.append(value)
|
|
|
else:
|
|
else:
|
|
|
transformed.append(value)
|
|
transformed.append(value)
|
|
|
return transformed
|
|
return transformed
|
|
@@ -250,33 +292,67 @@ class SQLiteToPostgresMigrator:
|
|
|
if not dependencies:
|
|
if not dependencies:
|
|
|
return True
|
|
return True
|
|
|
|
|
|
|
|
- print(f" 检查 {table} 的依赖表迁移状态...")
|
|
|
|
|
|
|
+ self.log(f" 检查 {table} 的依赖表迁移状态...")
|
|
|
all_migrated = True
|
|
all_migrated = True
|
|
|
|
|
|
|
|
for dep_table in dependencies:
|
|
for dep_table in dependencies:
|
|
|
if dep_table in self.migrated_tables:
|
|
if dep_table in self.migrated_tables:
|
|
|
- print(f" ✓ {dep_table} 已迁移")
|
|
|
|
|
|
|
+ self.log(f" ✓ {dep_table} 已迁移")
|
|
|
elif dep_table in self.failed_tables:
|
|
elif dep_table in self.failed_tables:
|
|
|
- print(f" ✗ {dep_table} 迁移失败")
|
|
|
|
|
|
|
+ self.log(f" ✗ {dep_table} 迁移失败")
|
|
|
all_migrated = False
|
|
all_migrated = False
|
|
|
else:
|
|
else:
|
|
|
- print(f" ? {dep_table} 尚未迁移")
|
|
|
|
|
|
|
+ self.log(f" ? {dep_table} 尚未迁移")
|
|
|
all_migrated = False
|
|
all_migrated = False
|
|
|
|
|
|
|
|
return all_migrated
|
|
return all_migrated
|
|
|
|
|
|
|
|
|
|
+ def handle_integer_overflow(self, table, columns, pg_structure, row):
|
|
|
|
|
+ """处理整数超出范围错误"""
|
|
|
|
|
+ # 找出导致问题的字段
|
|
|
|
|
+ for i, value in enumerate(row):
|
|
|
|
|
+ col_name = columns[i]
|
|
|
|
|
+ col_info = pg_structure.get(col_name.lower(), {})
|
|
|
|
|
+ pg_type = col_info.get('data_type')
|
|
|
|
|
+
|
|
|
|
|
+ if pg_type in ('integer', 'int', 'int4') and isinstance(value, int):
|
|
|
|
|
+ if value < -2147483648 or value > 2147483647:
|
|
|
|
|
+ self.log(f" 检测到字段 '{col_name}' 值超出范围 ({value})")
|
|
|
|
|
+
|
|
|
|
|
+ # 尝试将字段类型改为bigint
|
|
|
|
|
+ try:
|
|
|
|
|
+ self.log(f" 尝试将字段 '{col_name}' 类型改为 bigint")
|
|
|
|
|
+ alter_query = sql.SQL("""
|
|
|
|
|
+ ALTER TABLE {} ALTER COLUMN {} TYPE BIGINT;
|
|
|
|
|
+ """).format(
|
|
|
|
|
+ sql.Identifier(table.lower()),
|
|
|
|
|
+ sql.Identifier(col_name)
|
|
|
|
|
+ )
|
|
|
|
|
+ self.pg_cursor.execute(alter_query)
|
|
|
|
|
+ self.pg_conn.commit()
|
|
|
|
|
+ self.log(f" 成功将字段 '{col_name}' 类型改为 bigint")
|
|
|
|
|
+
|
|
|
|
|
+ # 更新表结构信息
|
|
|
|
|
+ pg_structure = self.get_pg_table_structure(table)
|
|
|
|
|
+ return True
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ self.log(f" 修改字段类型失败: {str(e)}")
|
|
|
|
|
+ self.pg_conn.rollback()
|
|
|
|
|
+ return False
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
def migrate_table(self, table):
|
|
def migrate_table(self, table):
|
|
|
"""迁移单个表"""
|
|
"""迁移单个表"""
|
|
|
- print(f"\n准备迁移表: {table}")
|
|
|
|
|
|
|
+ self.log(f"\n准备迁移表: {table}")
|
|
|
|
|
|
|
|
# 检查是否需要跳过
|
|
# 检查是否需要跳过
|
|
|
if table in self.skip_tables:
|
|
if table in self.skip_tables:
|
|
|
- print(f" 跳过系统表: {table}")
|
|
|
|
|
|
|
+ self.log(f" 跳过系统表: {table}")
|
|
|
return True
|
|
return True
|
|
|
|
|
|
|
|
# 检查依赖表是否已迁移
|
|
# 检查依赖表是否已迁移
|
|
|
if not self.check_dependencies_migrated(table):
|
|
if not self.check_dependencies_migrated(table):
|
|
|
- print(f" 依赖表未完全迁移,暂时跳过 {table}")
|
|
|
|
|
|
|
+ self.log(f" 依赖表未完全迁移,暂时跳过 {table}")
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
# 获取 SQLite 表结构
|
|
# 获取 SQLite 表结构
|
|
@@ -292,7 +368,7 @@ class SQLiteToPostgresMigrator:
|
|
|
rows = self.sqlite_cursor.fetchall()
|
|
rows = self.sqlite_cursor.fetchall()
|
|
|
|
|
|
|
|
if not rows:
|
|
if not rows:
|
|
|
- print(f" 表 {table} 为空,跳过")
|
|
|
|
|
|
|
+ self.log(f" 表 {table} 为空,跳过")
|
|
|
self.migrated_tables.add(table)
|
|
self.migrated_tables.add(table)
|
|
|
return True
|
|
return True
|
|
|
|
|
|
|
@@ -332,22 +408,22 @@ class SQLiteToPostgresMigrator:
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
# 用户确认
|
|
# 用户确认
|
|
|
- print(f" 表 {table} 有 {len(rows)} 行数据需要迁移")
|
|
|
|
|
|
|
+ self.log(f" 表 {table} 有 {len(rows)} 行数据需要迁移")
|
|
|
if primary_keys:
|
|
if primary_keys:
|
|
|
- print(f" 使用主键 ({', '.join(primary_keys)}) 处理冲突")
|
|
|
|
|
|
|
+ self.log(f" 使用主键 ({', '.join(primary_keys)}) 处理冲突")
|
|
|
else:
|
|
else:
|
|
|
- print(" 警告: 表没有主键,可能产生重复数据")
|
|
|
|
|
|
|
+ self.log(" 警告: 表没有主键,可能产生重复数据")
|
|
|
|
|
|
|
|
# 显示字段长度限制
|
|
# 显示字段长度限制
|
|
|
if column_lengths:
|
|
if column_lengths:
|
|
|
- print(" 字段长度限制:")
|
|
|
|
|
|
|
+ self.log(" 字段长度限制:")
|
|
|
for col, max_len in column_lengths.items():
|
|
for col, max_len in column_lengths.items():
|
|
|
- print(f" {col}: {max_len} 字符")
|
|
|
|
|
|
|
+ self.log(f" {col}: {max_len} 字符")
|
|
|
|
|
|
|
|
confirm = input(" 按 Enter 键开始迁移,或输入 's' 跳过此表: ")
|
|
confirm = input(" 按 Enter 键开始迁移,或输入 's' 跳过此表: ")
|
|
|
|
|
|
|
|
if confirm.lower() == 's':
|
|
if confirm.lower() == 's':
|
|
|
- print(f" 已跳过表 {table}")
|
|
|
|
|
|
|
+ self.log(f" 已跳过表 {table}")
|
|
|
return True
|
|
return True
|
|
|
|
|
|
|
|
# 迁移数据
|
|
# 迁移数据
|
|
@@ -363,28 +439,33 @@ class SQLiteToPostgresMigrator:
|
|
|
except psycopg2.IntegrityError as e:
|
|
except psycopg2.IntegrityError as e:
|
|
|
error_count += 1
|
|
error_count += 1
|
|
|
if 'foreign key constraint' in str(e):
|
|
if 'foreign key constraint' in str(e):
|
|
|
- print(f" 外键约束错误: {str(e)}")
|
|
|
|
|
|
|
+ self.log(f" 外键约束错误: {str(e)}")
|
|
|
|
|
+ elif 'integer out of range' in str(e):
|
|
|
|
|
+ # 处理整数超出范围错误
|
|
|
|
|
+ self.log(f" 整数超出范围错误: {str(e)}")
|
|
|
|
|
+ # 尝试将字段类型改为bigint
|
|
|
|
|
+ self.handle_integer_overflow(table, columns, pg_structure, row)
|
|
|
else:
|
|
else:
|
|
|
- print(f" 完整性错误: {str(e)}")
|
|
|
|
|
|
|
+ self.log(f" 完整性错误: {str(e)}")
|
|
|
self.pg_conn.rollback()
|
|
self.pg_conn.rollback()
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
error_count += 1
|
|
error_count += 1
|
|
|
- print(f" 行插入失败: {str(e)}")
|
|
|
|
|
|
|
+ self.log(f" 行插入失败: {str(e)}")
|
|
|
self.pg_conn.rollback()
|
|
self.pg_conn.rollback()
|
|
|
|
|
|
|
|
self.pg_conn.commit()
|
|
self.pg_conn.commit()
|
|
|
|
|
|
|
|
if error_count == 0:
|
|
if error_count == 0:
|
|
|
- print(f" 成功迁移 {success_count} 行数据")
|
|
|
|
|
|
|
+ self.log(f" 成功迁移 {success_count} 行数据")
|
|
|
self.migrated_tables.add(table)
|
|
self.migrated_tables.add(table)
|
|
|
return True
|
|
return True
|
|
|
else:
|
|
else:
|
|
|
- print(f" 部分迁移: {success_count} 行成功, {error_count} 行失败")
|
|
|
|
|
|
|
+ self.log(f" 部分迁移: {success_count} 行成功, {error_count} 行失败")
|
|
|
self.failed_tables.add(table)
|
|
self.failed_tables.add(table)
|
|
|
return False
|
|
return False
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
self.pg_conn.rollback()
|
|
self.pg_conn.rollback()
|
|
|
- print(f" 迁移表 {table} 时出错: {str(e)}")
|
|
|
|
|
|
|
+ self.log(f" 迁移表 {table} 时出错: {str(e)}")
|
|
|
self.failed_tables.add(table)
|
|
self.failed_tables.add(table)
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
@@ -395,24 +476,24 @@ class SQLiteToPostgresMigrator:
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
|
# 询问是否清空 PostgreSQL 数据
|
|
# 询问是否清空 PostgreSQL 数据
|
|
|
- print("\n是否在迁移前清空 PostgreSQL 数据库?")
|
|
|
|
|
- print("1. 清空所有数据 (确保无重复)")
|
|
|
|
|
- print("2. 不清空 (可能产生重复数据)")
|
|
|
|
|
- print("3. 退出")
|
|
|
|
|
|
|
+ self.log("\n是否在迁移前清空 PostgreSQL 数据库?")
|
|
|
|
|
+ self.log("1. 清空所有数据 (确保无重复)")
|
|
|
|
|
+ self.log("2. 不清空 (可能产生重复数据)")
|
|
|
|
|
+ self.log("3. 退出")
|
|
|
|
|
|
|
|
choice = input("请选择 (1/2/3): ")
|
|
choice = input("请选择 (1/2/3): ")
|
|
|
|
|
|
|
|
if choice == '3':
|
|
if choice == '3':
|
|
|
- print("退出迁移")
|
|
|
|
|
|
|
+ self.log("退出迁移")
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
if choice == '1':
|
|
if choice == '1':
|
|
|
if not self.clear_postgres_data():
|
|
if not self.clear_postgres_data():
|
|
|
- print("清空操作失败,退出迁移")
|
|
|
|
|
|
|
+ self.log("清空操作失败,退出迁移")
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
self.analyze_dependencies()
|
|
self.analyze_dependencies()
|
|
|
- print(f"\n开始迁移 {len(self.migration_order)} 个表")
|
|
|
|
|
|
|
+ self.log(f"\n开始迁移 {len(self.migration_order)} 个表")
|
|
|
|
|
|
|
|
# 按依赖顺序迁移表
|
|
# 按依赖顺序迁移表
|
|
|
remaining_tables = self.migration_order.copy()
|
|
remaining_tables = self.migration_order.copy()
|
|
@@ -421,7 +502,7 @@ class SQLiteToPostgresMigrator:
|
|
|
|
|
|
|
|
while remaining_tables and attempt < max_attempts:
|
|
while remaining_tables and attempt < max_attempts:
|
|
|
attempt += 1
|
|
attempt += 1
|
|
|
- print(f"\n第 {attempt} 轮迁移尝试")
|
|
|
|
|
|
|
+ self.log(f"\n第 {attempt} 轮迁移尝试")
|
|
|
|
|
|
|
|
# 复制列表以便在迭代时修改
|
|
# 复制列表以便在迭代时修改
|
|
|
tables_to_migrate = remaining_tables.copy()
|
|
tables_to_migrate = remaining_tables.copy()
|
|
@@ -433,23 +514,23 @@ class SQLiteToPostgresMigrator:
|
|
|
remaining_tables.append(table)
|
|
remaining_tables.append(table)
|
|
|
|
|
|
|
|
if remaining_tables:
|
|
if remaining_tables:
|
|
|
- print(f"\n本轮迁移后仍有 {len(remaining_tables)} 个表未成功迁移")
|
|
|
|
|
|
|
+ self.log(f"\n本轮迁移后仍有 {len(remaining_tables)} 个表未成功迁移")
|
|
|
|
|
|
|
|
# 检查迁移结果
|
|
# 检查迁移结果
|
|
|
if remaining_tables:
|
|
if remaining_tables:
|
|
|
- print(f"\n迁移完成,但以下表迁移失败:")
|
|
|
|
|
|
|
+ self.log(f"\n迁移完成,但以下表迁移失败:")
|
|
|
for table in remaining_tables:
|
|
for table in remaining_tables:
|
|
|
- print(f" - {table}")
|
|
|
|
|
|
|
+ self.log(f" - {table}")
|
|
|
else:
|
|
else:
|
|
|
- print("\n所有表迁移成功!")
|
|
|
|
|
|
|
+ self.log("\n所有表迁移成功!")
|
|
|
|
|
|
|
|
return True
|
|
return True
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
- print(f"迁移过程中发生错误: {str(e)}")
|
|
|
|
|
|
|
+ self.log(f"迁移过程中发生错误: {str(e)}")
|
|
|
return False
|
|
return False
|
|
|
finally:
|
|
finally:
|
|
|
self.close()
|
|
self.close()
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if __name__ == "__main__":
|
|
|
migrator = SQLiteToPostgresMigrator()
|
|
migrator = SQLiteToPostgresMigrator()
|
|
|
- migrator.migrate()
|
|
|
|
|
|
|
+ migrator.migrate()
|