github.com/runner-mei/ql@v1.1.0/ql/main.go (about) 1 // Copyright 2014 The ql Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Command ql is a utility to explore a database, prototype a schema or test 6 // drive a query, etc. 7 // 8 // Installation: 9 // 10 // $ go get github.com/cznic/ql/ql 11 // 12 // Usage: 13 // 14 // ql [-db name] [-schema regexp] [-tables regexp] [-fld] statement_list 15 // 16 // Options: 17 // 18 // -db name Name of the database to use. Defaults to "ql.db". 19 // If the DB file does not exists it is created automatically. 20 // 21 // -schema re If re != "" show the CREATE statements of matching tables and exit. 22 // 23 // -tables re If re != "" show the matching table names and exit. 24 // 25 // -fld First row of a query result set will show field names. 26 // 27 // statement_list QL statements to execute. 28 // If no non flag arguments are present, ql reads from stdin. 29 // The list is wrapped into an automatic transaction. 30 // 31 // -t Report and measure time to execute, including creating/opening and closing the DB. 32 // 33 // Example: 34 // 35 // $ ql 'create table t (i int, s string)' 36 // $ ql << EOF 37 // > insert into t values 38 // > (1, "a"), 39 // > (2, "b"), 40 // > (3, "c"), 41 // > EOF 42 // $ ql 'select * from t' 43 // 3, "c" 44 // 2, "b" 45 // 1, "a" 46 // $ ql -fld 'select * from t where i != 2 order by s' 47 // "i", "s" 48 // 1, "a" 49 // 3, "c" 50 // $ 51 package main 52 53 import ( 54 "bufio" 55 "flag" 56 "fmt" 57 "io/ioutil" 58 "log" 59 "os" 60 "regexp" 61 "sort" 62 "strings" 63 "time" 64 65 "github.com/cznic/ql" 66 ) 67 68 func str(data []interface{}) string { 69 a := make([]string, len(data)) 70 for i, v := range data { 71 switch x := v.(type) { 72 case string: 73 a[i] = fmt.Sprintf("%q", x) 74 default: 75 a[i] = fmt.Sprint(x) 76 } 77 } 78 return strings.Join(a, ", ") 79 } 80 81 func main() { 82 if err := do(); err != nil { 83 log.Fatal(err) 84 } 85 } 86 87 func do() (err error) { 88 oDB := flag.String("db", "ql.db", "The DB file to open. It'll be created if missing.") 89 oFlds := flag.Bool("fld", false, "Show recordset's field names.") 90 oSchema := flag.String("schema", "", "If non empty, show the CREATE statements of matching tables and exit.") 91 oTables := flag.String("tables", "", "If non empty, list matching table names and exit.") 92 oTime := flag.Bool("t", false, "Measure and report time to execute the statement(s) including DB create/open/close.") 93 flag.Parse() 94 95 t0 := time.Now() 96 if *oTime { 97 defer func() { 98 fmt.Fprintf(os.Stderr, "%s\n", time.Since(t0)) 99 }() 100 } 101 102 db, err := ql.OpenFile(*oDB, &ql.Options{CanCreate: true}) 103 if err != nil { 104 return err 105 } 106 107 defer func() { 108 ec := db.Close() 109 switch { 110 case ec != nil && err != nil: 111 log.Println(ec) 112 case ec != nil: 113 err = ec 114 } 115 }() 116 117 if pat := *oSchema; pat != "" { 118 re, err := regexp.Compile(pat) 119 if err != nil { 120 return err 121 } 122 123 nfo, err := db.Info() 124 if err != nil { 125 return err 126 } 127 128 r := []string{} 129 for _, ti := range nfo.Tables { 130 if !re.MatchString(ti.Name) { 131 continue 132 } 133 134 a := []string{} 135 for _, ci := range ti.Columns { 136 a = append(a, fmt.Sprintf("%s %s", ci.Name, ci.Type)) 137 } 138 r = append(r, fmt.Sprintf("CREATE TABLE %s (%s);", ti.Name, strings.Join(a, ", "))) 139 } 140 sort.Strings(r) 141 if len(r) != 0 { 142 fmt.Println(strings.Join(r, "\n")) 143 } 144 return nil 145 } 146 147 if pat := *oTables; pat != "" { 148 re, err := regexp.Compile(pat) 149 if err != nil { 150 return err 151 } 152 153 nfo, err := db.Info() 154 if err != nil { 155 return err 156 } 157 158 r := []string{} 159 for _, ti := range nfo.Tables { 160 if !re.MatchString(ti.Name) { 161 continue 162 } 163 164 r = append(r, ti.Name) 165 } 166 sort.Strings(r) 167 if len(r) != 0 { 168 fmt.Println(strings.Join(r, "\n")) 169 } 170 return nil 171 } 172 173 var src string 174 switch n := flag.NArg(); n { 175 case 0: 176 b, err := ioutil.ReadAll(bufio.NewReader(os.Stdin)) 177 if err != nil { 178 return err 179 } 180 181 src = string(b) 182 default: 183 a := make([]string, n) 184 for i := range a { 185 a[i] = flag.Arg(i) 186 } 187 src = strings.Join(a, " ") 188 } 189 190 src = "BEGIN TRANSACTION; " + src + "; COMMIT;" 191 l, err := ql.Compile(src) 192 if err != nil { 193 log.Println(src) 194 return err 195 } 196 197 rs, i, err := db.Execute(ql.NewRWCtx(), l) 198 if err != nil { 199 a := strings.Split(strings.TrimSpace(fmt.Sprint(l)), "\n") 200 return fmt.Errorf("%v: %s", err, a[i]) 201 } 202 203 if len(rs) == 0 { 204 return 205 } 206 207 switch { 208 case l.IsExplainStmt(): 209 return rs[len(rs)-1].Do(*oFlds, func(data []interface{}) (bool, error) { 210 fmt.Println(data[0]) 211 return true, nil 212 }) 213 default: 214 return rs[len(rs)-1].Do(*oFlds, func(data []interface{}) (bool, error) { 215 fmt.Println(str(data)) 216 return true, nil 217 }) 218 } 219 }