github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/interpreter/main.go (about)

     1  // Copyright 2015 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package main
    15  
    16  import (
    17  	"database/sql"
    18  	"flag"
    19  	"fmt"
    20  	"io"
    21  	"os"
    22  	"runtime"
    23  	"strings"
    24  	"time"
    25  
    26  	"github.com/insionng/yougam/libraries/juju/errors"
    27  	"github.com/insionng/yougam/libraries/ngaut/log"
    28  	"github.com/insionng/yougam/libraries/peterh/liner"
    29  	"github.com/insionng/yougam/libraries/pingcap/tidb"
    30  	"github.com/insionng/yougam/libraries/pingcap/tidb/terror"
    31  	"github.com/insionng/yougam/libraries/pingcap/tidb/util/printer"
    32  )
    33  
    34  var (
    35  	logLevel = flag.String("L", "error", "log level")
    36  	store    = flag.String("store", "goleveldb", "the name for the registered storage, e.g. hbase, memory, goleveldb, boltdb")
    37  	dbPath   = flag.String("dbpath", "test", "db path")
    38  	dbName   = flag.String("dbname", "test", "default db name")
    39  	lease    = flag.Int("lease", 1, "schema lease seconds, very dangerous to change only if you know what you do")
    40  
    41  	line        *liner.State
    42  	historyPath = "/tmp/tidb_interpreter"
    43  )
    44  
    45  func openHistory() {
    46  	if f, err := os.Open(historyPath); err == nil {
    47  		line.ReadHistory(f)
    48  		f.Close()
    49  	}
    50  }
    51  
    52  func saveHistory() {
    53  	if f, err := os.Create(historyPath); err == nil {
    54  		line.WriteHistory(f)
    55  		f.Close()
    56  	}
    57  }
    58  
    59  func executeLine(tx *sql.Tx, txnLine string) error {
    60  	start := time.Now()
    61  	if tidb.IsQuery(txnLine) {
    62  		rows, err := tx.Query(txnLine)
    63  		elapsed := time.Since(start).Seconds()
    64  		if err != nil {
    65  			return errors.Trace(err)
    66  		}
    67  		defer rows.Close()
    68  		cols, err := rows.Columns()
    69  		if err != nil {
    70  			return errors.Trace(err)
    71  		}
    72  
    73  		values := make([][]byte, len(cols))
    74  		scanArgs := make([]interface{}, len(values))
    75  		for i := range values {
    76  			scanArgs[i] = &values[i]
    77  		}
    78  
    79  		var datas [][]string
    80  		for rows.Next() {
    81  			err := rows.Scan(scanArgs...)
    82  			if err != nil {
    83  				return errors.Trace(err)
    84  			}
    85  
    86  			data := make([]string, len(cols))
    87  			for i, value := range values {
    88  				if value == nil {
    89  					data[i] = "NULL"
    90  				} else {
    91  					data[i] = string(value)
    92  				}
    93  			}
    94  
    95  			datas = append(datas, data)
    96  		}
    97  
    98  		// For `cols` and `datas[i]` always has the same length,
    99  		// no need to check return validity.
   100  		result, _ := printer.GetPrintResult(cols, datas)
   101  		fmt.Printf("%s", result)
   102  
   103  		switch len(datas) {
   104  		case 0:
   105  			fmt.Printf("Empty set")
   106  		case 1:
   107  			fmt.Printf("1 row in set")
   108  		default:
   109  			fmt.Printf("%v rows in set", len(datas))
   110  		}
   111  		fmt.Printf(" (%.2f sec)\n", elapsed)
   112  		if err := rows.Err(); err != nil {
   113  			return errors.Trace(err)
   114  		}
   115  	} else {
   116  		// TODO: last insert id
   117  		res, err := tx.Exec(txnLine)
   118  		elapsed := time.Since(start).Seconds()
   119  		if err != nil {
   120  			return errors.Trace(err)
   121  		}
   122  		cnt, err := res.RowsAffected()
   123  		if err != nil {
   124  			return errors.Trace(err)
   125  		}
   126  		switch cnt {
   127  		case 0, 1:
   128  			fmt.Printf("Query OK, %d row affected", cnt)
   129  		default:
   130  			fmt.Printf("Query OK, %d rows affected", cnt)
   131  		}
   132  		fmt.Printf(" (%.2f sec)\n", elapsed)
   133  	}
   134  	return nil
   135  }
   136  
   137  func mayExit(err error, l string) bool {
   138  	if terror.ErrorEqual(err, liner.ErrPromptAborted) || terror.ErrorEqual(err, io.EOF) {
   139  		fmt.Println("\nBye")
   140  		saveHistory()
   141  		return true
   142  	}
   143  	if err != nil {
   144  		log.Fatal(errors.ErrorStack(err))
   145  	}
   146  	return false
   147  }
   148  
   149  func readStatement(prompt string) (string, error) {
   150  	var ret string
   151  	for {
   152  		l, err := line.Prompt(prompt)
   153  		if err != nil {
   154  			return "", err
   155  		}
   156  		if strings.HasSuffix(l, ";") == false {
   157  			ret += l + "\n"
   158  			prompt = "   -> "
   159  			continue
   160  		}
   161  		return ret + l, nil
   162  	}
   163  }
   164  
   165  func main() {
   166  	printer.PrintTiDBInfo()
   167  
   168  	flag.Parse()
   169  	log.SetLevelByString(*logLevel)
   170  	// support for signal notify
   171  	runtime.GOMAXPROCS(runtime.NumCPU())
   172  
   173  	line = liner.NewLiner()
   174  	defer line.Close()
   175  
   176  	line.SetCtrlCAborts(true)
   177  	openHistory()
   178  
   179  	tidb.SetSchemaLease(time.Duration(*lease) * time.Second)
   180  
   181  	// use test as default DB.
   182  	mdb, err := sql.Open(tidb.DriverName, *store+"://"+*dbPath+"/"+*dbName)
   183  	if err != nil {
   184  		log.Fatal(errors.ErrorStack(err))
   185  	}
   186  
   187  	for {
   188  		l, err := readStatement("tidb> ")
   189  		if mayExit(err, l) {
   190  			return
   191  		}
   192  		line.AppendHistory(l)
   193  
   194  		// if we're in transaction
   195  		if strings.HasPrefix(l, "BEGIN") || strings.HasPrefix(l, "begin") {
   196  			tx, err := mdb.Begin()
   197  			if err != nil {
   198  				log.Error(errors.ErrorStack(err))
   199  				continue
   200  			}
   201  			for {
   202  				txnLine, err := readStatement(">> ")
   203  				if mayExit(err, txnLine) {
   204  					return
   205  				}
   206  				line.AppendHistory(txnLine)
   207  
   208  				if !strings.HasSuffix(txnLine, ";") {
   209  					txnLine += ";"
   210  				}
   211  
   212  				if strings.HasPrefix(txnLine, "COMMIT") || strings.HasPrefix(txnLine, "commit") {
   213  					err = tx.Commit()
   214  					if err != nil {
   215  						log.Error(errors.ErrorStack(err))
   216  						tx.Rollback()
   217  					}
   218  					break
   219  				}
   220  				// normal sql statement
   221  				err = executeLine(tx, txnLine)
   222  				if err != nil {
   223  					log.Error(errors.ErrorStack(err))
   224  					tx.Rollback()
   225  					break
   226  				}
   227  			}
   228  		} else {
   229  			tx, err := mdb.Begin()
   230  			if err != nil {
   231  				log.Error(errors.ErrorStack(err))
   232  				continue
   233  			}
   234  			err = executeLine(tx, l)
   235  			if err != nil {
   236  				log.Error(errors.ErrorStack(err))
   237  				tx.Rollback()
   238  				continue
   239  			}
   240  			err = tx.Commit()
   241  			if err != nil {
   242  				log.Error(errors.ErrorStack(err))
   243  			}
   244  		}
   245  	}
   246  }