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  }