/*
 * db_sync.go
 *
 * Synchronize data between SQL capable databases, client code.
 *
 * Author:
 *   Mark Taylor (mtaylor@taylor-hq.com)
 *
 * Version:
 *   July 2014
 *
 * See presentation:
 *   http://www.taylor-hq.com/~mtaylor/presentations/dbsync/DBSync.html
 *
 * Color-code this script:
 *   pygmentize -f html -o ~/public_html/presentations/dbsync/db_sync-main.go.html -O full db_sync.go
 *
 * NOTES:
 *   Using goroutines (gophers) to parallelize loading data into Postgres:
 *     http://www.acloudtree.com/how-to-shove-data-into-postgres-using-goroutinesgophers-and-golang/
 *
 * TODO:
 *   - Add command-line parameter for sync table definitions (from a file, and from source-db table)
 *   - Generate DAG, and parallel process in topological order.
 *
 */

package main

import (
    "log"
    "flag"
    "fmt"
    "strings"
    "time"
    "database/sql"
    _ "github.com/lib/pq"               // http://godoc.org/github.com/lib/pq
    //_ "github.com/go-sql-driver/mysql"  // http://godoc.org/github.com/go-sql-driver/mysql
    "database/sql/sync"
)


func main() {
    var err                error
    var db_src, db_dest    *sql.DB
    var results            sync.TableSyncResults
    var start              time.Time
    var verbosity          int
    var source_db          = "postgres://postgres:@localhost/tva?sslmode=disable"
    var dest_db            = "postgres://postgres:@localhost/tva2?sslmode=disable"
    //var dest_db          = "mysql://root:xxxx@localhost/test"
    var tables             = []sync.TableSyncParams{
                                { Table_Name:"gauges",       Insert_Only:false, Unique_ID:"id"          },
                                { Table_Name:"gauges_daily", Insert_Only:true,  Timestamp_Column:"date" },
                             }

    flag.IntVar(   &verbosity,    "v", verbosity, "Set verbosity level"     )
    flag.StringVar(&source_db,  "src", source_db, "Set source database"     )
    flag.StringVar(  &dest_db, "dest",   dest_db, "Set destination database")
    flag.Parse()

    db_src, err = sql.Open(strings.Split(source_db, ":")[0], source_db)
    if err != nil {
        log.Fatal(fmt.Sprintf("ERROR: Open() SQL source(%v): %s", source_db, err))
    }
    defer db_src.Close()
    db_dest, err = sql.Open(strings.Split(dest_db, ":")[0], dest_db)
    if err != nil {
        log.Fatal(fmt.Sprintf("ERROR: Open() SQL dest(%v): %s", dest_db, err))
    }
    defer db_dest.Close()

    for _, tparams := range tables {
        start = time.Now()
        results, err = tparams.Sync(db_src, db_dest, verbosity)
        if ! results.Failed {
            fmt.Printf("%s: SUCCESS: INSERTS: %d; UPDATES: %d\n",
                       tparams.Table_Name, results.Inserts, results.Updates)
        } else {
            fmt.Printf("%s: FAILURE\n", tparams.Table_Name)
        }
        fmt.Printf("Duration: %.2fs\n", time.Since(start).Seconds())
    }

}