migration_sqlite3.anubis 12 KB
/*
 * Created by PyramIDE.
 * User: David René
 * Date: 31/08/2011
 * Time: 2:09
 * 
 */

read tools/basis.anubis
read system/files.anubis
read system/logger.anubis
read system/string.anubis
read data_base/sqlite.anubis
read calexium_lib/database/alter_table.anubis
read calexium_lib/database/db_utils.anubis
read calexium_lib/net_services_protocols/logger_service.anubis
read calexium_lib/types/version.anubis

read migration_common_sqlite3.anubis

public type Migration:
  migration(DB_Version version,
            (SQLite3DataBase, Logger) -> Maybe(One)   migrate_to,
            (SQLite3DataBase, Logger) -> Maybe(One)   create_tables,
            (SQLite3DataBase, Logger) -> Maybe(One)   create_indexes,
           ).


define String dir_save_database = "db_backup/".


public define Bool
  migration_less
  (
    Migration a,
    Migration b
  )=
  db_version_less(a.version, b.version)
.

public define Maybe(One)
  backup_database
  (
    Logger        logger,
//    String        current_version,
    String        db_path,
    String        main_db_name,
    List(String)  other_db_names,
  )
  =
//  with should_copy = if file_exists(db_path + main_db_name) then
//                      if sqlite3_open(db_path + main_db_name) is
//                      {
//                        error(sql_error)        then false, // maybe not created yet
//                        ok(Database db)  then 
//                          with ver = get_version_settings(db),
//                          ver != current_version
//                      }
//                     else false,
                            
  //if should_copy then
    with  dir_save = db_path + dir_save_database, 
    if (Maybe(String))make_directory(dir_save) is
    {
      failure     then logError(logger, "Can't create directory '"+db_path + dir_save_database+"'"); failure,
      success(_)  then 
        with copy_with_log =  (String src, String dst) |-> 
                              if file_exists(src) then
                                if copy_file(src, dst) is
                                {
                                  cant_read_file          then logError(logger, "Can't read source file '" + src + "'."); failure,
                                  cant_create_file        then logError(logger, "Can't create destination file '" + dst + "'."); failure,
                                  copy_error              then logError(logger, "Can't copy file '" + src + "' to file '" + dst + "'."); failure,
                                  copy_file_mode_error    then logError(logger, "Can't get file mode for source file '" + src + "'."); failure,
                                  copy_file_times_error   then logError(logger, "Can't set file times into file '" + dst + "'."); failure,
                                  copy_ok(_)              then logInfo(logger, "File '" + dst + "' saved"); success(unique)
                                }
                              else success(unique),
        with copy_db = (List(String) names) |-copy_db->
                          if names is
                          {
                            [] then success(unique),
                            [h . t] then 
                              if copy_with_log(db_path + h, dir_save + h) is
                              {
                                failure then failure,
                                success(_) then copy_db(t)
                              }
                          },
        copy_db([main_db_name . other_db_names])
    }
  //else success(unique)
  .
  
public define Maybe(One)
  backup_database
  (
    Logger          logger,
    SQLite3DataBase db,
    String          db_path,
    String          main_db_name
  )=
  //we use SQLite 3.27.0 feature to backup the database. So we check the running SQLite engine version before 
  //to proceed
  with v3_27 = version(3,27,0,0),
  with sqlite_version = if sql_query_timeout(db, "SELECT sqlite_version();", "backup_database get version") is
                        {
                          error(_)         then logError(logger, "Can't get sqlite version for backup");failure,
                          ok(_, cursor, _) then 
                            with sqlite_version_str = db_get_String(cursor),
                            to_Version(sqlite_version_str)
                        },
                        
        if sqlite_version is
        {
                              failure           then logError(logger, "Can't get sqlite version for backup ");failure,
                              success(sqlite_v) then 
          //if the SQLite engine version is < 3.27 we use old method for backup
          if sqlite_v.numeric +< v3_27.numeric then
            logWarning(logger, "The current SQLite engine v"+sqlite_v+" doesn't support vaccum into. Fallback to previous method");
            backup_database(logger, db_path, main_db_name, [])
          else
            //construct the target folder for backup
            with  dir_save = db_path + dir_save_database,
            if (Maybe(String))make_directory(dir_save) is
            {
              failure     then logError(logger, "Can't create directory '"+db_path + dir_save_database+"'"); failure,
              success(_)  then 
                with filename = db_path + dir_save_database+get_current_datetime_condensed+"-"+main_db_name,
                if sql_query(db, "VACUUM INTO '"+filename+"';") is
                {
                  error(err)  then logError(logger,db_error(err, "Can't backup database into "+filename));failure,
                  ok(_,)      then logInfo(logger, "database backuped into "+filename);success(unique)
                }
            }
      }
.

//
//define Maybe(One)  
//  do_migration_loop
//  (
//    SQLite3DataBase   db,
//    Logger            logger,
//    String            current_version,    //current version of app
//    String            db_ver,             //version found in db settings
//    List(Migration)   migrations,
//    Maybe(Migration)  need_to_create_table,          // success in case of new DB or when no migration have been done
//    (SQLite3DataBase, String) -> One  update_db_version,
//  ) =
//  if migrations is
//  {
//    [] then 
//      if need_to_create_table is
//      {
//        failure then
//          update_db_version(db, current_version);
//          success(unique),
//        success(last) then
//          if last is migration(_, _, create_tables, create_indexes) then
//          if create_tables(db, logger) is success(_) then
//            if create_indexes(db, logger) is success(_) then
//              update_db_version(db, current_version);
//              success(unique)
//            else
//              failure
//          else
//            failure
//      },
//    [h . t] then
//      since h is migration(version, migrate_to, _, _),
//      if compare_db_version(db_ver, version) < 0 then
//        logInfo(logger, "Migrating Database from version " + db_ver + " to version " + version);
//        if migrate_to(db, logger) is failure then 
//          failure
//        else
//          do_migration_loop(db, logger, current_version, version, t, failure, update_db_version)
//      else 
//        do_migration_loop(db, logger, current_version, db_ver, t, success(h), update_db_version),
//  }.
//
///* SQLite 3 version */
//public define Maybe(One)  
//  do_migration
//  (
//    SQLite3DataBase                       db,
//    Logger                                logger,
//    String                                current_version,
//    (SQLite3DataBase) -> String       get_db_version,
//    (SQLite3DataBase, String) -> One  update_db_version,
//    List(Migration)                       all_migrations,
//  )=
//  if is_empty_database(db) then  // new empty database
//    logInfo(logger, "No database found. Creating a new one...");
//    do_migration_loop(db, logger, current_version, "", [], last(all_migrations), update_db_version)
//  else
//  (
//    with db_version = get_db_version(db),
//    //if app required db version is same as db_version, there is nothing to migrate
//    if compare_db_version(db_version, current_version) = 0 then
//      logInfo(logger, "Database version "+db_version+". No database migration found.");
//      success(unique)
//    else
//      do_migration_loop(db, logger, current_version, db_version, all_migrations, failure, update_db_version)
//  ).

/* SQLite 3 version hk_settings with DB_Version*/

define Maybe(One)
  do_table_indexes
  (
    SQLite3DataBase       db,
    Logger                logger,
    Migration             _migration,
    (DB_Version) -> One   update_db_version    
  )=
  since _migration is migration(mig_version, _, create_tables, create_indexes),
  
  if create_tables(db, logger) is success(_) then
    if create_indexes(db, logger) is success(_) then
      update_db_version(mig_version);
      success(unique)
    else
      failure
  else
    failure
.
  
define Maybe(One)  
  do_migration_loop
  (
    SQLite3DataBase   db,
    Logger            logger,
//    String            current_version,    //current version of app
    DB_Version        db_ver,             //version found in db settings
    List(Migration)   migrations,
    (DB_Version) -> One   update_db_version,
  ) =
  if migrations is
  {
    []      then success(unique),
    [h . t] then
      since h is migration(migration_version, migrate_to, create_tables, create_indexes),
      
      if compare_db_version(db_ver, migration_version) is
      {
        less then 
          logInfo(logger, "[less] Migrating Database from version " + db_ver + " to version " + migration_version);
          if migrate_to(db, logger) is failure then 
            failure
          else
            update_db_version(migration_version);
            do_migration_loop(db, logger, migration_version, t, update_db_version)
            
        more then
          logInfo(logger, "[more] No Database migration found from version " + db_ver + " to version " + migration_version);
          do_migration_loop(db, logger, db_ver, t, update_db_version)
        
        equal then
          logInfo(logger, "[equal] No Database migration found from version " + db_ver + " to version " + migration_version);
          do_migration_loop(db, logger, db_ver, t, update_db_version)
          
        equal_but_sign then
          logInfo(logger, "Database migration found from version " + db_ver + " to version " + migration_version+ " by hash difference ");
          if migrate_to(db, logger) is failure then
            failure
          else
            update_db_version(migration_version);
            do_migration_loop(db, logger, migration_version, t, update_db_version)
      }
  }.
  
public define Maybe(One)  
  do_migration
  (
    SQLite3DataBase       db,
    Logger                logger,
    DB_Version            current_db_version,
    (DB_Version) -> One   update_db_version,
    List(Migration)       all_migrations,
  )=
  if is_empty_database(db) then  // new empty database
    logInfo(logger, "No database found. Creating a new one...");
    if last(all_migrations) is success(migration) then
      do_table_indexes(db, logger, migration, update_db_version)
    else
      logError(logger, "No database migration found. Can't create Database");
      failure
  else
  (
//    with db_version = get_db_version,
//    //if app version is same as db_version, there is nothing to migrate
//    if compare_db_version(db_version, current_version) = 0 then
//      logInfo(logger, "Database version "+db_version+". No database migration found.");
//      success(unique)
//    else
      do_migration_loop(db, logger, current_db_version, all_migrations, update_db_version)
  ).
  
public define Maybe(One)  
  do_create_indexes
  (
    SQLite3DataBase         db,
    Logger                  logger,
    List(Migration)         all_migrations,
  )=
  if last(all_migrations) is 
  {
    failure then logCriticalError(logger, "Index creation: migration list is empty!"); failure,
    success(last_migration) then
      if last_migration is migration(_, _, _, create_indexes) then
      if create_indexes(db, logger) is success(_) then
        success(unique)
      else
        failure
  }.