Wednesday, November 19, 2014

My codez is frawress

Sup boys and girls. Tweety here. Well here's another look into Qt, I've discovered that I need a way to change the database schema. So looking for something that resembles a "rake db:migrate" from ruby on rails I came up with this function:


bool Database::migrate()
{
    bool seccess = true;
    QString failed;
    QFile migrateFile("D:/Qt/Tools/QtCreator/bin/sqliteTest/migrate_file.in");
    QFile doneMigrate("D:/Qt/Tools/QtCreator/bin/sqliteTest/migrate_file.out");
    if(!migrateFile.open(QIODevice::ReadWrite) || !doneMigrate.open(QIODevice::WriteOnly|QIODevice::Append))
        seccess = false;
    QTextStream in(&migrateFile), out(&doneMigrate);
    while(!in.atEnd())
    {
        bool new_table = true;
        QString table_name;
        QString new_columns;
        QString old_columns;
        QString line = in.readLine();

        QStringList list = line.split(";");
        table_name = list[0];
        new_columns = list[1];
        qDebug()<<table_name<<"\t"<<new_columns;
        QSqlRecord rec = db.record(table_name);
        if(!rec.isEmpty())
        {
            new_table = false;
            for(int i = 0; i < rec.count();i++)
            {
                if(i==0)
                    old_columns += rec.fieldName(i);
                else
                {
                    old_columns += ", ";
                    old_columns += rec.fieldName(i);
                }
            }
        }
        qDebug()<<old_columns;
        QSqlQuery qry(db);
        if(!new_table)
        {
            if(!qry.exec(QString("ALTER TABLE %1 RENAME TO TempOldTable").arg(table_name)))
                seccess = false;
            if(!qry.exec(QString("CREATE TABLE %1 (%2) ").arg(table_name, new_columns)))
                seccess = false;
            if(!qry.exec(QString("INSERT INTO user (%1) SELECT %1 FROM TempOldTable").arg(old_columns)))
                seccess = false;
            if(!qry.exec("DROP TABLE TempOldTable"))
                seccess = false;
        }
        else
        {
            if(!qry.exec(QString("CREATE TABLE %1 (%2) ").arg(table_name, new_columns)))
                seccess = false;
        }
        if(seccess)
            out << line << endl;
        else
            failed += line + "\n";
        rec = db.record(table_name);
        if(!rec.isEmpty())
        {
            for(int i = 0; i < rec.count();i++)
            {
                if(i==0)
                    old_columns = rec.fieldName(i);
                else
                {
                    old_columns += ", ";
                    old_columns += rec.fieldName(i);
                }
            }
        }
        qDebug()<<old_columns;
    }
    migrateFile.resize(0);
    in << failed;
    doneMigrate.close();
    migrateFile.close();

    return seccess;
}


Now this is for Qt, but the logic is sound should work for any SQLite3 database. So let's go over the code a bit. First this uses a semi colon separated variables file. I used the ";" because there is commas that I want to keep in the string. How this file looks is each line would be a new or alter to a database tables. The format is [table_name];[columns] with all the different columns separated with commas. So first we have a bool for success or not, it's set to true and if anything fails it's set to false. Next we load a couple files, and open them. Open the out file with truncate. Then make a pair of streams for each file. Then we get each line from the "in" file, the one with the table name and columns. Take that line and split it on the ";". Assign the variables table_name and new_columns with the strings in the line we split. Next we get the columns from the table in the database. If rec is empty we know that there is no table with that name so we create a new one with the sqlite3 commands "CREATE TABLE". Else we change the name of the old table, create a new table with the original name and the new columns, insert everything from the old database table into the new one, then drop the old table. 
    If all that was a success we output the original line to the out file as a record of what has been done. If it failed then we copy that line to a new string then copy it back to the original file, so we can do it over. Then close the files. Not to bad right? Let me know what you think in the comments and any improvements that could help me or anyone else that might view this blog. Thanks for reading. We'll see you next time, but until then... I'm outta here.