func fillDatasetsByErrors(datasets *dataset.CollectionDataset, dbs []db.Database, ctx *action.Context) error { for _, d := range dbs { localCtx := ctx.Get("context" + d.Code).(*action.Context) if !localCtx.Get("success").(bool) { ds := datasets.GetOrCreateDataset(d.Code) ds.Error = true ds.TextError = localCtx.Snap.Error().Error() } } return nil }
//SelectAfter trigger after for select action func SelectAfter(dbs []db.Database, ctx *action.Context) error { done := ctx.Get("chandone") logger.Trace.Println("SelectAfter") if done != nil { done.(chan bool) <- true } switch ctx.Get("format") { case "table": return doOutputTable(dbs, ctx) case "json": return doOutputJSON(dbs, ctx) case "xml": return doOutputXML(dbs, ctx) } return nil }
func doOutputXML(dbs []db.Database, ctx *action.Context) error { datasets := ctx.Get("datasets").(*dataset.CollectionDataset) if err := fillDatasetsByErrors(datasets, dbs, ctx); err != nil { return err } subformat := ctx.GetDef("subformat", "").(string) if subformat == "" { datasets.WriteXML(os.Stdout) return nil } f, err := os.Create(subformat) if err != nil { return err } defer f.Close() _, err = datasets.WriteXML(f) return err }
func parseOthersFlagsForRunContext(ctx *cli.Context, ctxRun *action.Context) error { if ctx.IsSet("format") { format := ctx.String("format") subformat := "" //TODO: refactor it! if strings.Contains(format, "raw:") { subformat = format[len("raw:"):] format = "raw" } if strings.Contains(format, "table:") { subformat = format[len("table:"):] format = "table" } if strings.Contains(format, "json:") { subformat = format[len("json:"):] format = "json" } if strings.Contains(format, "xml:") { subformat = format[len("xml:"):] format = "xml" } switch format { case "table", "raw", "json", "xml": ctxRun.Set("format", format) ctxRun.Set("subformat", subformat) break default: return fmt.Errorf("Unknown format:%s", format) } } else { ctxRun.Set("format", "raw") } if ctx.IsSet("timeout") { ctxRun.Set("timeout", ctx.Int("timeout")) } if ctx.IsSet("commit") { ctxRun.Set("commit", ctx.Bool("commit")) } return nil }
//PrintStatistic - print common statistic after execute action func PrintStatistic(dbs []db.Database, ctx *action.Context) error { var buf bytes.Buffer pget := ctx.Get("params").(parametergetter.ParameterGetter) if pget.GetDef(parametergetter.Statistic, false).(bool) { mSec := ctx.Snap.Finished().Sub(ctx.Snap.Started()).Nanoseconds() / 1000 / 1000 exec := ctx.GetDef("exec", 0).(int) success := ctx.GetDef("success", 0).(int) failed := ctx.GetDef("failed", 0).(int) buf.WriteString(fmt.Sprintf("Executed: %-4d Success:%-3d (%3.2f%%) Failed:%-3d \n", exec, success, float64(success)/float64(exec)*100, failed)) buf.WriteString(fmt.Sprintf("Completed: %v msec", mSec)) fmt.Println(buf.String()) } return nil }
//SelectBefore trigger before for select action func SelectBefore(dbs []db.Database, ctx *action.Context) error { // Prepare data in ctx.datasets logger.Trace.Println("SelectBefore") format := ctx.Get("format") subformat := ctx.GetDef("subformat", "").(string) datasets := dataset.NewColllection() chanHdr := make(chanHeader) chandata := make(chanData) chanDone := make(chan bool) ctx.Set("chanheader", chanHdr) ctx.Set("chandata", chandata) ctx.Set("chandone", chanDone) ctx.Set("datasets", datasets) line := 0 go func() { for { select { case hdr := <-chanHdr: ds := datasets.GetOrCreateDataset(hdr.Code) ds.AddColumns(hdr.Columns...) case cudata := <-chandata: line++ ds := datasets.GetOrCreateDataset(cudata.Code) ds.Append(cudata.Data) if format == "raw" { fmt.Println(formatRaw(subformat, ds.RowsCount(), line, ds.GetColumnsNames(), cudata.Data)) } case <-chanDone: logger.Trace.Println("SelectBefore do done") return } } }() return nil }
//PrintStatisticQuery - print statistic for each database after execute action func PrintStatisticQuery(dbs []db.Database, ctx *action.Context) error { pget := ctx.Get("params").(parametergetter.ParameterGetter) if pget.GetDef(parametergetter.QueryStatistic, false).(bool) { for _, d := range dbs { localCtx := ctx.Get("context" + d.Code).(*action.Context) mSec := localCtx.Snap.Finished().Sub(localCtx.Snap.Started()).Nanoseconds() / 1000 / 1000 if localCtx.Get("success").(bool) { rowcount := localCtx.GetDef("rowcount", 0).(int) rowsaffected := localCtx.GetDef("rowsaffected", int64(0)).(int64) fmt.Printf("%s: Success. Elapsed time:%d msec. Rows count:%d Rows affected: %d\n", d.Code, mSec, rowcount, rowsaffected) } else { var errmsg string if errs := localCtx.Snap.Error(); errs != nil { errmsg = errs.Error() } fmt.Printf("%s: Failed! Elapsed time:%d msec. Error message: %s \n", d.Code, mSec, strings.Replace(errmsg, "\n", " ", -1)) } } } return nil }
//SelectError trigger error for select action func SelectError(dbs []db.Database, ctx *action.Context) error { logger.Trace.Println("Failed execute") if !ctx.GetDef("silent", false).(bool) { fmt.Println("All requests will fail.") } switch ctx.Get("format") { case "json", "xml": return SelectAfter(dbs, ctx) } done := ctx.Get("chandone") if done != nil { done.(chan bool) <- true } return nil }
//Exec - execute sql for the databases func Exec(dbContext db.Database, dbHandle *sql.DB, cmd *sqlcommand.SQLCommand, ctx *action.Context) error { logger.Trace.Println("run exec", dbContext.Code) defer logger.Trace.Println("done exec", dbContext.Code) localCtx := ctx.Get("context" + dbContext.Code).(*action.Context) commit := ctx.GetDef("commit", false).(bool) var pint []interface{} for _, p := range cmd.Params { pint = append(pint, p) } tx, err := dbHandle.Begin() if err != nil { return err } sqmt, err := tx.Prepare(cmd.Script) if err != nil { return err } defer sqmt.Close() res, err := sqmt.Exec(pint...) if err != nil { return err } defer sqmt.Close() if commit { logger.Debug.Printf("%s Transaction commited", dbContext.Code) if err := tx.Commit(); err != nil { return err } } else { if err := tx.Rollback(); err != nil { return err } logger.Warn.Printf("%s: Transaction rollback.Use a special flag for commit the transaction\n", dbContext.Code) } ra, err := res.RowsAffected() if err != nil { return err } localCtx.IncInt64("rowsaffected", ra) ctx.IncInt64("rowsaffected", ra) return nil }
//Run concrete action for all databases func Run(dbs []db.Database, sql *sqlcommand.SQLCommand, act action.Actioner, ctx *action.Context, pget parametergetter.ParameterGetter) (*action.Context, error) { logger.Trace.Println("rdb run") defer logger.Trace.Println("rdb run done") ctx.Set("params", pget) ctx.Set("silent", pget.GetDef(parametergetter.Silent, false).(bool)) ctx.Snap.Start() colParams, err := parameters.GetInstance().All() if err != nil { return nil, err } ctx.Set("Params", colParams.Get()) for _, d := range dbs { err := runItem(d, sql, act, ctx, pget) if err != nil { logger.Error.Println(err) //todo: в общий список ошибок } } wg.Wait() ctx.Snap.Done(nil) return ctx, nil }
func runItem(d db.Database, s *sqlcommand.SQLCommand, doaction action.Actioner, ctx *action.Context, pget parametergetter.ParameterGetter) error { logger.Trace.Println("runItem") defer logger.Trace.Println(d.Code, "runItem done") if s != nil { logger.Trace.Println(d.Code, s.Script) } wg.Add(1) ctx.IncInt("exec", 1) params := ctx.Get("Params").([]parameters.Parameter) go func() { timeout := ctx.GetDef("timeout", 0).(int) defer wg.Done() var ( ctxExec context.Context cancel context.CancelFunc ) ch := make(chan bool) if timeout > 0 { ctxExec, cancel = context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second) } else { ctxExec, cancel = context.WithCancel(context.Background()) } defer cancel() localCtx := action.NewContext() go func() { defer func() { ch <- true close(ch) }() ctx.Set("context"+d.Code, localCtx) ctx.Set("iscancel", ch) localCtx.Snap.Start() localCtx.Set("success", false) connectionString, e := paramsreplace.Replace(d.ConnectionString, params) if e != nil { ctx.IncInt("failed", 1) logger.Error.Println(e) return } logger.Debug.Println(d.Code, "Connection string:", connectionString) connection, err := rdb.Open(d.Engine, connectionString) if err != nil { ctx.IncInt("failed", 1) logger.Error.Println(d.Code, err) return } defer func() { if err := connection.Close(); err != nil { panic(err) } else { logger.Trace.Printf("%s disconnected", d.Code) } }() err = doaction(d, connection, s, ctx) if err != nil { if err.Error() != "cancel" { ctx.IncInt("failed", 1) localCtx.Snap.Done(err) logger.Error.Println(d.Code, err) if !ctx.GetDef("silent", false).(bool) { fmt.Fprintf(os.Stdout, "%s: %s\n", d.Code, strings.Replace(err.Error(), "\n", " ", -1)) } } return } localCtx.Set("success", true) ctx.IncInt("success", 1) localCtx.Snap.Done(nil) runtime.Gosched() }() select { case <-ch: logger.Trace.Println("operation done w/o timeout") return case <-ctxExec.Done(): err := ctxExec.Err() logger.Trace.Printf("operation done: %s\n", err) ctx.IncInt("failed", 1) localCtx.Snap.Done(err) logger.Error.Println(d.Code, err) // ch <- true return } }() return nil }
//Select - it action for select command func Select(dbs1 db.Database, dsrc *sql.DB, cmd *sqlcommand.SQLCommand, ctx *action.Context) error { logger.Trace.Println("run select", dbs1.Code) timeout := ctx.GetDef("timeout", 0).(int) logger.Debug.Printf("run select timeout %d sec", timeout) var pint []interface{} chanCancel := ctx.Get("iscancel").(chan bool) for _, p := range cmd.Params { pint = append(pint, p) } /*tx,err:=dsrc.Begin() if err!=nil { return err } defer tx.Rollback() rw, err := tx.Query(cmd.Script, pint...)*/ tx, err := dsrc.Begin() if err != nil { return err } defer tx.Rollback() stmt, err := tx.Prepare(cmd.Script) if err != nil { return err } defer stmt.Close() rw, err := tx.Query(cmd.Script, pint...) if err != nil { return err } defer func() { if err := rw.Close(); err != nil { panic(err) } }() cols, _ := rw.Columns() localCtx := ctx.Get("context" + dbs1.Code).(*action.Context) chanHdr := ctx.Get("chanheader") chandata := ctx.Get("chandata") if chanHdr != nil { chanHdr.(chanHeader) <- &headerTable{ Code: dbs1.Code, Columns: cols, } } rg := rowgetter.MustRowGetter(rw) for { select { case <-chanCancel: logger.Info.Println("run select canceled", dbs1.Code) return fmt.Errorf("cancel") default: } row, ok := rg.Next() if !ok { break } localCtx.IncInt("rowcount", 1) if /*format=="table" &&*/ chandata != nil { data := make(map[string]interface{}) for i, r := range row { data[cols[i]] = r } data["_CODE_"] = dbs1.Code chandata.(chanData) <- &dataTable{ Code: dbs1.Code, Data: data, } } } return nil }
func doOutputTable(dbs []db.Database, ctx *action.Context) error { datasets := ctx.Get("datasets").(*dataset.CollectionDataset) table := fmttab.New("", fmttab.BorderThin, nil) table.AddColumn("_CODE_", 10, fmttab.AlignLeft) ctx.Set("table", table) for _, col := range datasets.GetUniqueColumnsNames() { table.AddColumn(col, 15, fmttab.AlignLeft) } for _, ds := range datasets.GetDatasets() { for _, row := range ds.Rows { table.AppendData(row.GetDataMap()) } } pget := ctx.Get("params").(parametergetter.ParameterGetter) if pget.GetDef(parametergetter.AutoFitWidthColumns, true).(bool) { //todo: move into fmttab cols := table.Columns.ColumnsVisible() for c, col := range cols { max := utf8.RuneCountInString(col.Name) for i := 0; i < len(table.Data); i++ { val, ok := table.Data[i][col.Name] if ok && val != nil { fval := fmt.Sprintf("%v", val) l := utf8.RuneCountInString(fval) if l > max { max = l } } } if max != 0 { cols[c].Width = max } } } switch pget.GetDef(parametergetter.BorderTable, "").(string) { case "Thin": table.SetBorder(fmttab.BorderThin) case "Double": table.SetBorder(fmttab.BorderDouble) case "None": table.SetBorder(fmttab.BorderNone) case "Simple": table.SetBorder(fmttab.BorderSimple) } tabex.SetTableSubFormat(table, ctx.GetDef("subformat", "").(string)) if pget.GetDef(parametergetter.Fit, true).(bool) { if e := termbox.Init(); e != nil { return e } tw, _ := termbox.Size() table.AutoSize(true, tw) termbox.Close() } if _, err := table.WriteTo(os.Stdout); err != nil { return err } return nil }