A little while ago I was tasked with migrating about hundred trac instances from SQLite to MySQL. Unfortunately SQLites schema definitions is not compatible with MySQL. So I had to wrote some scripts to handle it. After a little bit of experimenting everything worked perfectly.
The Strategy I employed was like this:
- Make a dump of a default MySQL trac db schema.
- Make a dump of the trac SQLite database
- Remove db schema definitions from the SQLite dump
- Concatenate the MySQL and SQLite dumps
- Load data into MySQL
- Edit Trac's database settings to use MySQL
- Do a trac-admin upgrade
MYSQL_USER=username
MYSQL_PSWD=password
TRACBASE=/path/to/tracreps
# loop through all trac instances in tracreps
for d in $( ls $TRACBASE )
do
if [ -d "$TRACBASE/$d" ]; then
echo $d
TRACNAME=$d
echo "creating database for $TRACNAME..."
mysqladmin --user $MYSQL_USER -p$MYSQL_PSWD create $TRACNAME# dump sqlite db structure + data
echo "dumping data from sqlite..."
$SQLITECMD $TRACBASE/$TRACNAME/db/trac.db .dump > trac.sqlite.sql# remove database definitions from dump using a custom python script
echo "cleaning database definition from dump..."
`./cleansql.py < trac.sqlite.sql > trac.sqlite.sql.dataonly`# concatenate mysql database definitions and sqlite data
cat trac.mysql.sql trac.sqlite.sql.dataonly > trac.sqlecho "loading data into mysql..."
mysql --user $MYSQL_USER -p$MYSQL_PSWD --default_character_set utf8 $TRACNAME < trac.sql# edit database connection string in trac.ini
sed -i "s?sqlite:db/trac.db?mysql://$MYSQL_USER:$MYSQL_PSWD@localhost:3306/$TRACNAME?" $TRACBASE/$TRACNAME/conf/trac.iniecho "upgrading mysql database..."
trac-admin $TRACBASE/$TRACNAME upgrade --no-backup
To remove schema definitions from the sqlite dump and fix some incompatibilities I made this python script referenced above as cleansql.py:
#!/usr/bin/env python
import sysimport refile = sys.stdin.read()
file = re.sub(r'(CREATE (TABLE|INDEX)[^;]*|COMMIT|BEGIN TRANSACTION);', '', file)
file = re.sub(r'INSERT INTO "([^"]+)"', lambda m: 'INSERT INTO `%s`' % m.groups(1), file)
# fix sql for reports
file = re.sub(r'CAST\((.+) AS int\)', lambda m: 'CAST(%s AS signed)' % m.groups(1), file)sys.stdout.write(file)
And that's it. I hope this will benefit someone tasked with the same job.
