| /* |
| Copyright 2016 The Transicator Authors |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| package common |
| |
| import ( |
| "fmt" |
| "strconv" |
| "time" |
| ) |
| |
| const ( |
| /* |
| postgresEpoch represents January 1, 2000, UTC, in nanoseconds |
| from the Unix epoch (which is January 1, 1970). |
| */ |
| postgresEpochNanos = 946684800000000000 |
| nanosPerMicro = 1000 |
| ) |
| |
| /* |
| Get places the value of the column into the specified interface if possible. |
| "d" must be set to one of: |
| * *string |
| * *[]byte |
| * *int16, *int32, *int64, *uint16, *uint32, *uint64 |
| * *float64, *float32 |
| * *bool |
| * |
| * If the conversion is not possible, then an error will be returned. |
| */ |
| func (v ColumnVal) Get(d interface{}) error { |
| switch v.Value.(type) { |
| case nil: |
| return getNil(d) |
| case string: |
| return getString(d, v.Value.(string)) |
| case int64: |
| return getInt(d, v.Value.(int64)) |
| case uint64: |
| return getUint(d, v.Value.(uint64)) |
| case float64: |
| return getFloat(d, v.Value.(float64)) |
| case bool: |
| return getBool(d, v.Value.(bool)) |
| case []byte: |
| return getBytes(d, v.Value.([]byte)) |
| case time.Time: |
| return getTime(d, v.Value.(time.Time)) |
| default: |
| return fmt.Errorf("Value %T not of expected type", v.Value) |
| } |
| } |
| |
| /* |
| Get retrieves the value of the specified type just like "get", but it sets the |
| target to the empty value if the column is not present. |
| */ |
| func (r Row) Get(name string, d interface{}) error { |
| col := r[name] |
| if col != nil { |
| return col.Get(d) |
| } |
| return getNil(d) |
| } |
| |
| func getNil(d interface{}) error { |
| switch d.(type) { |
| case *string: |
| *(d.(*string)) = "" |
| case *[]byte: |
| *(d.(*[]byte)) = nil |
| case *interface{}: |
| *(d.(*interface{})) = nil |
| default: |
| return getInt(d, 0) |
| } |
| return nil |
| } |
| |
| /* |
| String converts the value of the column into a string. Binary data will |
| be encoded using base64. |
| */ |
| func (v ColumnVal) String() string { |
| var s string |
| v.Get(&s) |
| return s |
| } |
| |
| func getString(d interface{}, s string) error { |
| switch d.(type) { |
| case *string: |
| *(d.(*string)) = s |
| case *int64: |
| iv, err := strconv.ParseInt(s, 10, 64) |
| if err != nil { |
| return err |
| } |
| *(d.(*int64)) = iv |
| case *int32: |
| iv, err := strconv.ParseInt(s, 10, 32) |
| if err != nil { |
| return err |
| } |
| *(d.(*int32)) = int32(iv) |
| case *int: |
| iv, err := strconv.ParseInt(s, 10, 32) |
| if err != nil { |
| return err |
| } |
| *(d.(*int)) = int(iv) |
| case *int16: |
| iv, err := strconv.ParseInt(s, 10, 16) |
| if err != nil { |
| return err |
| } |
| *(d.(*int16)) = int16(iv) |
| case *uint64: |
| iv, err := strconv.ParseUint(s, 10, 64) |
| if err != nil { |
| return err |
| } |
| *(d.(*uint64)) = iv |
| case *uint32: |
| iv, err := strconv.ParseUint(s, 10, 32) |
| if err != nil { |
| return err |
| } |
| *(d.(*uint32)) = uint32(iv) |
| case *uint: |
| iv, err := strconv.ParseUint(s, 10, 32) |
| if err != nil { |
| return err |
| } |
| *(d.(*uint)) = uint(iv) |
| case *uint16: |
| iv, err := strconv.ParseUint(s, 10, 16) |
| if err != nil { |
| return err |
| } |
| *(d.(*uint16)) = uint16(iv) |
| case *float64: |
| v, err := strconv.ParseFloat(s, 64) |
| if err != nil { |
| return err |
| } |
| *(d.(*float64)) = v |
| case *float32: |
| v, err := strconv.ParseFloat(s, 32) |
| if err != nil { |
| return err |
| } |
| *(d.(*float32)) = float32(v) |
| case *bool: |
| v, err := strconv.ParseBool(s) |
| if err != nil { |
| return err |
| } |
| *(d.(*bool)) = v |
| case *[]byte: |
| *(d.(*[]byte)) = []byte(s) |
| default: |
| return fmt.Errorf("Invalid conversion: Can't convert string to %T", d) |
| } |
| return nil |
| } |
| |
| func getInt(d interface{}, i int64) error { |
| switch d.(type) { |
| case *string: |
| *(d.(*string)) = strconv.FormatInt(i, 10) |
| case *int64: |
| *(d.(*int64)) = i |
| case *int32: |
| *(d.(*int32)) = int32(i) |
| case *int: |
| *(d.(*int)) = int(i) |
| case *int16: |
| *(d.(*int16)) = int16(i) |
| case *uint64: |
| *(d.(*uint64)) = uint64(i) |
| case *uint32: |
| *(d.(*uint32)) = uint32(i) |
| case *uint: |
| *(d.(*uint)) = uint(i) |
| case *uint16: |
| *(d.(*uint16)) = uint16(i) |
| case *float64: |
| *(d.(*float64)) = float64(i) |
| case *float32: |
| *(d.(*float32)) = float32(i) |
| case *bool: |
| if i == 0 { |
| *(d.(*bool)) = false |
| } else { |
| *(d.(*bool)) = true |
| } |
| default: |
| return fmt.Errorf("Invalid conversion: Can't convert int64 to %T", d) |
| } |
| return nil |
| } |
| |
| func getUint(d interface{}, i uint64) error { |
| switch d.(type) { |
| case *string: |
| *(d.(*string)) = strconv.FormatUint(i, 10) |
| case *int64: |
| *(d.(*int64)) = int64(i) |
| case *int32: |
| *(d.(*int32)) = int32(i) |
| case *int: |
| *(d.(*int)) = int(i) |
| case *int16: |
| *(d.(*int16)) = int16(i) |
| case *uint64: |
| *(d.(*uint64)) = i |
| case *uint32: |
| *(d.(*uint32)) = uint32(i) |
| case *uint: |
| *(d.(*uint)) = uint(i) |
| case *uint16: |
| *(d.(*uint16)) = uint16(i) |
| case *float64: |
| *(d.(*float64)) = float64(i) |
| case *float32: |
| *(d.(*float32)) = float32(i) |
| case *bool: |
| if i == 0 { |
| *(d.(*bool)) = false |
| } else { |
| *(d.(*bool)) = true |
| } |
| default: |
| return fmt.Errorf("Invalid conversion: Can't convert uint64 to %T", d) |
| } |
| return nil |
| } |
| |
| func getFloat(d interface{}, i float64) error { |
| switch d.(type) { |
| case *string: |
| *(d.(*string)) = strconv.FormatFloat(i, 'G', -1, 64) |
| case *int64: |
| *(d.(*int64)) = int64(i) |
| case *int32: |
| *(d.(*int32)) = int32(i) |
| case *int: |
| *(d.(*int)) = int(i) |
| case *int16: |
| *(d.(*int16)) = int16(i) |
| case *uint64: |
| *(d.(*uint64)) = uint64(i) |
| case *uint32: |
| *(d.(*uint32)) = uint32(i) |
| case *uint: |
| *(d.(*uint)) = uint(i) |
| case *uint16: |
| *(d.(*uint16)) = uint16(i) |
| case *float64: |
| *(d.(*float64)) = float64(i) |
| case *float32: |
| *(d.(*float32)) = float32(i) |
| case *bool: |
| if i == 0 { |
| *(d.(*bool)) = false |
| } else { |
| *(d.(*bool)) = true |
| } |
| default: |
| return fmt.Errorf("Invalid conversion: Can't convert float64 to %T", d) |
| } |
| return nil |
| } |
| |
| func isTrue(b bool) int { |
| if b { |
| return 1 |
| } |
| return 0 |
| } |
| |
| func getBool(d interface{}, b bool) error { |
| switch d.(type) { |
| case *string: |
| *(d.(*string)) = strconv.FormatBool(b) |
| case *int64: |
| *(d.(*int64)) = int64(isTrue(b)) |
| case *int32: |
| *(d.(*int32)) = int32(isTrue(b)) |
| case *int: |
| *(d.(*int)) = int(isTrue(b)) |
| case *int16: |
| *(d.(*int16)) = int16(isTrue(b)) |
| case *uint64: |
| *(d.(*uint64)) = uint64(isTrue(b)) |
| case *uint32: |
| *(d.(*uint32)) = uint32(isTrue(b)) |
| case *uint: |
| *(d.(*uint)) = uint(isTrue(b)) |
| case *uint16: |
| *(d.(*uint16)) = uint16(isTrue(b)) |
| case *float64: |
| *(d.(*float64)) = float64(isTrue(b)) |
| case *float32: |
| *(d.(*float32)) = float32(isTrue(b)) |
| case *bool: |
| *(d.(*bool)) = b |
| default: |
| return fmt.Errorf("Invalid conversion: Can't convert bool to %T", d) |
| } |
| return nil |
| } |
| |
| func getBytes(d interface{}, b []byte) error { |
| switch d.(type) { |
| case *string: |
| *(d.(*string)) = string(b) |
| case *[]byte: |
| *(d.(*[]byte)) = b |
| case *interface{}: |
| *(d.(*interface{})) = b |
| default: |
| return getString(d, string(b)) |
| } |
| return nil |
| } |
| |
| func getTime(d interface{}, t time.Time) error { |
| switch d.(type) { |
| case *string: |
| *(d.(*string)) = t.String() |
| case *time.Time: |
| *(d.(*time.Time)) = t |
| case *interface{}: |
| *(d.(*interface{})) = t |
| default: |
| return fmt.Errorf("Invalid conversion: Can't convert Time to %T", d) |
| } |
| return nil |
| } |
| |
| /* |
| convertParameter takes any type of primitive field and converts it to |
| something that we can place inside a protobuf. Since we control the |
| encoding, it panics if an unknown data type comes up. |
| */ |
| func convertParameter(v interface{}) isValuePb_Value { |
| if v == nil { |
| return nil |
| } |
| switch v.(type) { |
| case *interface{}: |
| // This makes it easier to scan rows from an existing SQL driver |
| return convertParameter(*(v.(*interface{}))) |
| case string: |
| return &ValuePb_String_{ |
| String_: v.(string), |
| } |
| case []byte: |
| return &ValuePb_Bytes{ |
| Bytes: v.([]byte), |
| } |
| case bool: |
| return &ValuePb_Bool{ |
| Bool: v.(bool), |
| } |
| case int16: |
| return &ValuePb_Int{ |
| Int: int64(v.(int16)), |
| } |
| case int32: |
| return &ValuePb_Int{ |
| Int: int64(v.(int32)), |
| } |
| case int: |
| return &ValuePb_Int{ |
| Int: int64(v.(int)), |
| } |
| case int64: |
| return &ValuePb_Int{ |
| Int: v.(int64), |
| } |
| case uint16: |
| return &ValuePb_Uint{ |
| Uint: uint64(v.(uint16)), |
| } |
| case uint32: |
| return &ValuePb_Uint{ |
| Uint: uint64(v.(uint32)), |
| } |
| case uint: |
| return &ValuePb_Uint{ |
| Uint: uint64(v.(uint)), |
| } |
| case uint64: |
| return &ValuePb_Uint{ |
| Uint: v.(uint64), |
| } |
| case float32: |
| return &ValuePb_Double{ |
| Double: float64(v.(float32)), |
| } |
| case float64: |
| return &ValuePb_Double{ |
| Double: v.(float64), |
| } |
| case time.Time: |
| return &ValuePb_Timestamp{ |
| Timestamp: TimeToPgTimestamp(v.(time.Time)), |
| } |
| default: |
| panic(fmt.Sprintf("Can't convert value %v type %T for protobuf", v, v)) |
| } |
| } |
| |
| /* |
| PgTimestampToTime converts a Postgres timestamp, expressed in microseconds |
| since midnight, January 1, 2000, into a Go Time. |
| */ |
| func PgTimestampToTime(pts int64) time.Time { |
| utx := (pts * nanosPerMicro) + postgresEpochNanos |
| return time.Unix(0, utx) |
| } |
| |
| /* |
| TimeToPgTimestamp converts a Go Time into a Postgres timestamp. |
| */ |
| func TimeToPgTimestamp(t time.Time) int64 { |
| return (t.UnixNano() - postgresEpochNanos) / nanosPerMicro |
| } |