github.com/tobgu/qframe@v0.4.0/internal/io/sql/column.go (about) 1 package sql 2 3 import ( 4 "math" 5 "reflect" 6 7 "github.com/tobgu/qframe/internal/math/float" 8 9 "github.com/tobgu/qframe/qerrors" 10 ) 11 12 // Column implements the sql.Scanner interface 13 // and allows arbitrary data types to be loaded from 14 // any database/sql/driver into a QFrame. 15 type Column struct { 16 kind reflect.Kind 17 nulls int 18 // pointer to the data slice which 19 // contains the inferred data type 20 ptr interface{} 21 data struct { 22 Ints []int 23 Floats []float64 24 Bools []bool 25 Strings []*string 26 } 27 coerce func(t interface{}) error 28 precision int 29 } 30 31 // Null appends a new Null value to 32 // the underlying column data. 33 func (c *Column) Null() error { 34 // If we haven't inferred the type of 35 // data we are scanning simply count 36 // the number of NULL values we receive. 37 // The only scenario this will happen is 38 // when the first returned values are NULL. 39 if c.kind == reflect.Invalid { 40 c.nulls++ 41 return nil 42 } 43 switch c.kind { 44 case reflect.Float64: 45 c.data.Floats = append(c.data.Floats, math.NaN()) 46 case reflect.String: 47 c.data.Strings = append(c.data.Strings, nil) 48 default: 49 return qerrors.New("Column Null", "non-nullable type: %s", c.kind) 50 } 51 return nil 52 } 53 54 // Int adds a new int to the underlying data slice 55 func (c *Column) Int(i int) { 56 if c.ptr == nil { 57 c.kind = reflect.Int 58 c.ptr = &c.data.Ints 59 } 60 c.data.Ints = append(c.data.Ints, i) 61 } 62 63 // Float adds a new float to the underlying data slice 64 func (c *Column) Float(f float64) { 65 if c.ptr == nil { 66 c.kind = reflect.Float64 67 c.ptr = &c.data.Floats 68 // add any NULL floats previously scanned 69 if c.nulls > 0 { 70 for i := 0; i < c.nulls; i++ { 71 c.data.Floats = append(c.data.Floats, math.NaN()) 72 } 73 c.nulls = 0 74 } 75 } 76 if c.precision > 0 { 77 f = float.Fixed(f, c.precision) 78 } 79 c.data.Floats = append(c.data.Floats, f) 80 } 81 82 // String adds a new string to the underlying data slice 83 func (c *Column) String(s string) { 84 if c.ptr == nil { 85 c.kind = reflect.String 86 c.ptr = &c.data.Strings 87 // add any NULL strings previously scanned 88 if c.nulls > 0 { 89 for i := 0; i < c.nulls; i++ { 90 c.data.Strings = append(c.data.Strings, nil) 91 } 92 c.nulls = 0 93 } 94 } 95 c.data.Strings = append(c.data.Strings, &s) 96 } 97 98 // Bool adds a new bool to the underlying data slice 99 func (c *Column) Bool(b bool) { 100 if c.ptr == nil { 101 c.kind = reflect.Bool 102 c.ptr = &c.data.Bools 103 } 104 c.data.Bools = append(c.data.Bools, b) 105 } 106 107 // Scan implements the sql.Scanner interface 108 func (c *Column) Scan(t interface{}) error { 109 if c.coerce != nil { 110 return c.coerce(t) 111 } 112 switch v := t.(type) { 113 case bool: 114 c.Bool(v) 115 case string: 116 c.String(v) 117 case int64: 118 c.Int(int(v)) 119 case []uint8: 120 c.String(string(v)) 121 case float64: 122 c.Float(v) 123 case nil: 124 err := c.Null() 125 if err != nil { 126 return err 127 } 128 default: 129 return qerrors.New( 130 "Column Scan", "unsupported scan type: %s", reflect.ValueOf(t).Kind()) 131 } 132 return nil 133 } 134 135 // Data returns the underlying data slice 136 func (c *Column) Data() interface{} { 137 if c.ptr == nil { 138 return nil 139 } 140 // *[]<T> -> []<T> 141 return reflect.ValueOf(c.ptr).Elem().Interface() 142 }