github.com/andrewrech/ih-abstract@v0.0.0-20210322142951-2fec1c8d0f38/read.go (about) 1 package main 2 3 import ( 4 "database/sql" 5 "encoding/csv" 6 "errors" 7 "io" 8 "log" 9 "os" 10 11 _ "github.com/denisenkom/go-mssqldb" // sql driver 12 ) 13 14 // read reads raw input data. 15 func read(f flags, in *os.File) (r rawRecords) { 16 log.Println("initializing records map") 17 18 if *f.sql { 19 log.Println("reading Sql") 20 21 db, err := connect(*f.config) 22 if err != nil { 23 log.Fatalln(err) 24 } 25 defer db.Close() 26 27 r = DB(*f.config, db) 28 } 29 30 if !(*f.sql) { 31 log.Println("reading Stdin") 32 33 r = readCSV(in) 34 } 35 36 return r 37 } 38 39 // readSQLRows reads rows of strings from an SQL database. 40 func readSQLRows(rows *sql.Rows) (r rawRecords) { 41 var buf int64 = 2e7 42 43 // initialize channels 44 r.out = make(chan []string, buf) 45 r.done = make(chan struct{}) 46 47 var err error 48 r.header, err = rows.Columns() 49 if err != nil { 50 log.Fatalln(err) 51 } 52 53 // slice of byte slices of correct length 54 rawResult := make([]sql.NullString, len(r.header)) 55 // string slice of correct length 56 result := make([]string, len(r.header)) 57 // destination interface 58 dest := make([]interface{}, len(r.header)) 59 60 // add pointers to destination 61 for i := range result { 62 dest[i] = &rawResult[i] 63 } 64 65 var counter int64 66 stopCounter := make(chan struct{}) 67 count(&counter, "read (sql)", stopCounter) 68 69 go func() { 70 for rows.Next() { 71 // fill destination 72 err = rows.Scan(dest...) 73 if err != nil { 74 log.Fatalln(err) 75 } 76 77 counter++ 78 79 for i, raw := range rawResult { 80 // handle nil type with conversion to "" 81 if raw.Valid { 82 result[i] = raw.String 83 } 84 } 85 86 r.out <- result 87 } 88 89 err = rows.Err() 90 if err != nil { 91 log.Fatalln("error encountered during iteration:", rows.Err()) 92 } 93 94 close(r.out) 95 stopCounter <- struct{}{} 96 r.done <- struct{}{} 97 }() 98 99 return r 100 } 101 102 // readCSV reads records from a CSV file. 103 func readCSV(in io.Reader) (r rawRecords) { 104 var buf int64 = 2e7 105 106 // initialize channels 107 r.out = make(chan []string, buf) 108 r.done = make(chan struct{}) 109 110 reader := csv.NewReader(in) 111 reader.LazyQuotes = true 112 113 var err error 114 r.header, err = reader.Read() 115 if err != nil { 116 log.Fatal(err) 117 } 118 119 var counter int64 120 stopCounter := make(chan struct{}) 121 count(&counter, "read (csv)", stopCounter) 122 123 // process records 124 go func() { 125 for { 126 l, err := reader.Read() 127 128 counter++ 129 130 switch { 131 case errors.Is(err, io.EOF): 132 r.done <- struct{}{} 133 134 close(r.out) 135 136 case err != nil: 137 log.Fatal(err) 138 139 default: 140 r.out <- l 141 } 142 } 143 }() 144 145 stopCounter <- struct{}{} 146 147 return r 148 } 149 150 // headerParse parses input data column names. 151 func headerParse(h []string) (colNames map[string]int) { 152 colNames = make(map[string]int) 153 154 for i, s := range h { 155 colNames[s] = i 156 } 157 158 return 159 }