// Checks whether or not a patch has been applied on database func hasPatchApplied(dbConfig *patchsql.DatabaseConfig, patchConfig *PatchConfig) (result bool, err error) { result = false err = dbConfig.Execute( func(db *dbsql.DB) (err error) { var successRow = -1 if err = db.QueryRow( ` SELECT COUNT(dcl_id) FROM sysdb_change_log WHERE dcl_named_id = ? AND dcl_result = 2 ORDER BY dcl_time_update DESC LIMIT 1 `, patchConfig.Id, ).Scan(&successRow); err != nil { return } result = successRow == 1 return }, ) return }
// Checks whether or not a patch has been applied on database func hasPatchApplied(dbConfig *patchsql.DatabaseConfig, patchConfig *PatchConfig) (result bool, err error) { result = false err = dbConfig.Execute( func(db *dbsql.DB) (err error) { var successRow = -1 /** * Since MySQL would gives you total number of COUNT(*) * even if you use limit express, * we just check the counting of success patching >= 1 */ if err = db.QueryRow( ` SELECT COUNT(*) FROM sysdb_change_log WHERE dcl_named_id = ? AND dcl_result = 2 ORDER BY dcl_time_update DESC `, patchConfig.Id, ).Scan(&successRow); err != nil { return } result = successRow >= 1 return }, ) return }
// 1. Add log to database // 2. Applies patch to database // 3. Update result patching on log in database func applyPatch( dbConfig *patchsql.DatabaseConfig, patchConfig *PatchConfig, scripts []string, ) (err error) { var patchContent = patchResult{ patchConfig: patchConfig, } /** * Add log to dtaabase */ if err = dbConfig.Execute(newChangeLogFunc(&patchContent)); err != nil { return } // :~) /** * Applies scripts to database */ for _, script := range scripts { log.Printf("Applying script:\n%v\n", script) if err = dbConfig.Execute( func(db *dbsql.DB) (err error) { _, err = db.Exec(script) return }, ); err != nil { /** * Logs the failed result to database */ patchContent.result = failed patchContent.message = fmt.Sprintf("Error: [%v]\nScript:\n%v\n", err, script) if logErr := dbConfig.Execute(updateChangeLogFunc(&patchContent)); logErr != nil { panic(fmt.Errorf("Cannot log failed patching: [%v]. Error: %f.\nScript:\n%v\n", patchConfig.Id, err, script)) } // :~) return } } // :~) /** * Update result patching on log in database */ patchContent.result = success if err = dbConfig.Execute(updateChangeLogFunc(&patchContent)); err != nil { return } // :~) return }
// Executes the patches in change log func ExecutePatches(changeLogConfig *ChangeLogConfig) (err error) { /** * Loads configuration of patches */ var loadedPatches []PatchConfig if loadedPatches, err = LoadChangeLogFromFile(changeLogConfig.ChangeLog); err != nil { return } // :~) /** * Connect to database */ var dbConfig *patchsql.DatabaseConfig if dbConfig, err = patchsql.NewDatabaseConfig( changeLogConfig.DriverName, changeLogConfig.Dsn, ); err != nil { return } defer dbConfig.Close() // :~) /** * Checking the schema for change log */ if err = dbConfig.Execute(checkChangeLogSchema); err != nil { return } // :~) /** * Iterates each patch and applies it to database */ var numberOfApplied = 0 for _, p := range loadedPatches { /** * Checks if the patch has been applied */ if patchApplied, _ := hasPatchApplied(dbConfig, &p); patchApplied { continue } // :~) log.Printf("Applying patch: [%v](%v)...", p.Id, p.Filename) var scripts []string /** * Loads scripts from file */ if scripts, err = p.loadScripts(changeLogConfig.PatchFileBase, changeLogConfig.Delimiter); err != nil { return fmt.Errorf("Load script file[%v/%v] error: %v", changeLogConfig.PatchFileBase, p.Filename, err) } // :~) /** * Applies patch to database */ if err = applyPatch(dbConfig, &p, scripts); err != nil { var patchErr = fmt.Errorf("Patch [%v](%v) has error: %v", p.Id, p.Filename, err) log.Println(patchErr) return patchErr } // :~) numberOfApplied++ log.Printf("Applying patch success. [%v](%v).", p.Id, p.Filename) } // :~) log.Printf("Number of applied patches: %v", numberOfApplied) return }