#!/bin/bash # Import an SQLite dump of a Bacula catalog into Postgres # Designed for v1.63.3 (as found on Debian sarge) # # v0.5 # # Copyright (c) 2006 Russell Howe # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. FILE=bacula.sql # Tables, in order of size TABLES=(File Filename Path Job Media Pool CDImages Counters Version Client FileSet JobMedia NextId UnsavedFiles BaseFiles) # Tables, in insert order TABLESINSERT=(Pool CDImages Client Counters FileSet Filename Job Media JobMedia NextId Path File UnsavedFiles Version BaseFiles) DBNAME=bacula LOGFILE="/var/tmp/sqlite2pgsql.$$.log" importdata() { if [ "x" == "x$1" ]; then echo "Error: importdata() called without an argument. Aborting." exit 1 fi SQLFILE="$1" if [ ! -r "$SQLFILE" ]; then echo "Error: Cannot read from $SQLFILE. Aborting." exit 1 fi echo -n "Loading $SQLFILE into database $DBNAME..." psql -d "$DBNAME" -f "$SQLFILE" || ( echo "Failed to load $SQLFILE into database $DBNAME. psql exited with return code $?. Aborting." exit 1 ) } # Go through each of the table names, splitting the INSERT statements off # into seperate files for table in ${TABLES[@]}; do SRC="$FILE.other" if [ ! -f "$FILE.other" ]; then SRC="$FILE" fi PATTERN="^INSERT INTO $table " if [ ! -f "$FILE.data.$table" ]; then echo -n "Separating $table table from database dump..." echo "BEGIN;" > "$FILE.data.$table.tmp" grep "$PATTERN" "$SRC" >> "$FILE.data.$table.tmp" echo "COMMIT;" >> "$FILE.data.$table.tmp" mv "$FILE.data.$table.tmp" "$FILE.data.$table" echo "done. ($FILE.data.$table)" echo -n "Stripping matched lines from the source file to speed up the next round..." grep -v "$PATTERN" "$SRC" > "$FILE.other.tmp" mv "$FILE.other.tmp" "$FILE.other" echo "done." else echo "$FILE.data.$table already exists. Assuming this table has already been split" echo "off from the main dump. Not regenerating." fi done echo "Seperating DDL statements from INSERT statements" grep -v "^INSERT" "$FILE.other" > "$FILE.ddl" echo "DDL statements are now in $FILE.ddl" grep "^INSERT" "$FILE.other" > "$FILE.data.other" echo "Any remaining INSERT statements are now in $FILE.data.other" echo "Fixing up datatypes used in the DDL..." sed -e 's/TINYINT/SMALLINT/g' \ -e 's/DATETIME/TIMESTAMP/g' \ -e 's/INTEGER UNSIGNED/INTEGER/g' \ -e 's/BIGINT UNSIGNED/BIGINT/g' \ -e 's/INTEGER AUTOINCREMENT/SERIAL/g' \ -e s/\ DEFAULT\ \"\"/\ DEFAULT\ \'\'/g \ -e s#\ TIMESTAMP\ DEFAULT\ 0#\ TIMESTAMP\ DEFAULT\ \'1/1/1970\'#g "$FILE.ddl" > "$FILE.ddl.postgres" echo "Fixing Pool table..." sed -e 's/,0,0);$/,NULL,NULL);/' "$FILE.data.Pool" > "$FILE.data.Pool.fixed" echo "Fixing removing entries from Job table which no longer have a Pool to link to" # Remove jobs which refer to nonexistent pools, and fix up invalid start and end times to be 1/1/1970 grep -vE '([2589]|1[0-5]),[0-9]+,[0-9]+,[0-9]+\);' "$FILE.data.Job" \ |sed -e s@^\\\(INSERT\ INTO\ Job\ VALUES\(\\\(\[^,\]\\\+,\\\)\\\{8\\\}\\\)0,@\\1NULL,@ \ -e s@^\\\(INSERT\ INTO\ Job\ VALUES\(\\\(\[^,\]\\\+,\\\)\\\{9\\\}\\\)0,@\\1\NULL,@ \ -e s@^\\\(INSERT\ INTO\ Job\ VALUES\(\\\(\[^,\]\\\+,\\\)\\\{17\\\}\\\)0,@\\1\NULL,@ \ -e s@^\\\(INSERT\ INTO\ Job\ VALUES\(\\\(\[^,\]\\\+,\\\)\\\{18\\\}\\\)0,@\\1\NULL,@ \ -e s@^\\\(INSERT\ INTO\ Job\ VALUES\(\\\(\[^,\]\\\+,\\\)\\\{5\\\}\\\)0,@\\1NULL,@ > "$FILE.data.Job.fixed" # Remove JobMedia entries which refer to nonexistent Jobs echo "Cleaning up the dump of the JobMedia table..." grep -vE 'INSERT INTO JobMedia VALUES\([0-9]+,([12589]|1[0-4]),' "$FILE.data.JobMedia" > "$FILE.data.JobMedia.fixed" # Remove File entries which refer to nonexistent Jobs echo "Cleaning up the dump of the File table..." grep -vE 'INSERT INTO File VALUES\([0-9]+,[0-9]+,([12589]|1[0-4]),' "$FILE.data.File" > "$FILE.data.File.fixed" echo "OK, we should be ready to import data into PostgreSQL now. DDL first..." echo "This will probably fail the first time. You will have to edit $FILE.other" echo "and rearrange the CREATE TABLE statements so that the tables are created" echo "in the correct order." echo "After editing $FILE.other, simply rerun this script and it will carry on" echo "where it left off." importdata "$FILE.ddl.postgres" for table in ${TABLESINSERT[@]} other; do IMPORTFILE="$FILE.data.$table" if [ -f "$FILE.data.$table.fixed" ]; then IMPORTFILE="$FILE.data.$table.fixed" fi importdata "$IMPORTFILE" 2>&1 |tee -a "$LOGFILE" done echo "All done! Check $LOGFILE for errors."