| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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. |
| */ |
| |
| // Copyright 2011 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Type conversions for Scan. |
| |
| package util |
| |
| import ( |
| "database/sql" |
| "database/sql/driver" |
| "errors" |
| "fmt" |
| "math" |
| "reflect" |
| "strconv" |
| "strings" |
| "time" |
| ) |
| |
| var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error |
| |
| // convertAssignRows copies to dest the value in src, converting it if possible. |
| // An error is returned if the copy would result in loss of information. |
| // dest should be a pointer type. If rows is passed in, the rows will |
| // be used as the parent for any cursor values converted from a |
| // driver.Rows to a *ScanRows. |
| func convertAssignRows(dest, src interface{}, rows *ScanRows) error { |
| // Common cases, without reflect. |
| switch s := src.(type) { |
| case string: |
| switch d := dest.(type) { |
| case *string: |
| if d == nil { |
| return errNilPtr |
| } |
| *d = s |
| return nil |
| case *[]byte: |
| if d == nil { |
| return errNilPtr |
| } |
| *d = []byte(s) |
| return nil |
| case *sql.RawBytes: |
| if d == nil { |
| return errNilPtr |
| } |
| *d = append((*d)[:0], s...) |
| return nil |
| } |
| case []byte: |
| switch d := dest.(type) { |
| case *string: |
| if d == nil { |
| return errNilPtr |
| } |
| *d = string(s) |
| return nil |
| case *interface{}: |
| if d == nil { |
| return errNilPtr |
| } |
| *d = cloneBytes(s) |
| return nil |
| case *[]byte: |
| if d == nil { |
| return errNilPtr |
| } |
| *d = cloneBytes(s) |
| return nil |
| case *sql.RawBytes: |
| if d == nil { |
| return errNilPtr |
| } |
| *d = s |
| return nil |
| } |
| case time.Time: |
| switch d := dest.(type) { |
| case *time.Time: |
| *d = s |
| return nil |
| case *string: |
| *d = s.Format(time.RFC3339Nano) |
| return nil |
| case *[]byte: |
| if d == nil { |
| return errNilPtr |
| } |
| *d = []byte(s.Format(time.RFC3339Nano)) |
| return nil |
| case *sql.RawBytes: |
| if d == nil { |
| return errNilPtr |
| } |
| *d = s.AppendFormat((*d)[:0], time.RFC3339Nano) |
| return nil |
| } |
| case decimalDecompose: |
| switch d := dest.(type) { |
| case decimalCompose: |
| return d.Compose(s.Decompose(nil)) |
| } |
| case nil: |
| switch d := dest.(type) { |
| case *interface{}: |
| if d == nil { |
| return errNilPtr |
| } |
| *d = nil |
| return nil |
| case *[]byte: |
| if d == nil { |
| return errNilPtr |
| } |
| *d = nil |
| return nil |
| case *sql.RawBytes: |
| if d == nil { |
| return errNilPtr |
| } |
| *d = nil |
| return nil |
| } |
| // The driver is returning a cursor the client may iterate over. |
| case driver.Rows: |
| switch d := dest.(type) { |
| case *ScanRows: |
| if d == nil { |
| return errNilPtr |
| } |
| if rows == nil { |
| return errors.New("invalid context to convert cursor rows, missing parent *ScanRows") |
| } |
| rows.closemu.Lock() |
| *d = ScanRows{ |
| dc: rows.dc, |
| releaseConn: func(error) {}, |
| rowsi: s, |
| } |
| // Chain the cancel function. |
| parentCancel := rows.cancel |
| rows.cancel = func() { |
| // When ScanRows.cancel is called, the closemu will be locked as well. |
| // So we can access rs.lasterr. |
| d.close(rows.lasterr) |
| if parentCancel != nil { |
| parentCancel() |
| } |
| } |
| rows.closemu.Unlock() |
| return nil |
| } |
| } |
| |
| var sv reflect.Value |
| |
| switch d := dest.(type) { |
| case *string: |
| sv = reflect.ValueOf(src) |
| switch sv.Kind() { |
| case reflect.Bool, |
| reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, |
| reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, |
| reflect.Float32, reflect.Float64: |
| *d = asString(src) |
| return nil |
| } |
| case *[]byte: |
| sv = reflect.ValueOf(src) |
| if b, ok := asBytes(nil, sv); ok { |
| *d = b |
| return nil |
| } |
| case *sql.RawBytes: |
| sv = reflect.ValueOf(src) |
| if b, ok := asBytes([]byte(*d)[:0], sv); ok { |
| *d = sql.RawBytes(b) |
| return nil |
| } |
| case *bool: |
| bv, err := driver.Bool.ConvertValue(src) |
| if err == nil { |
| *d = bv.(bool) |
| } |
| return err |
| case *interface{}: |
| *d = src |
| return nil |
| } |
| |
| if scanner, ok := dest.(sql.Scanner); ok { |
| return scanner.Scan(src) |
| } |
| |
| dpv := reflect.ValueOf(dest) |
| if dpv.Kind() != reflect.Ptr { |
| return errors.New("destination not a pointer") |
| } |
| if dpv.IsNil() { |
| return errNilPtr |
| } |
| |
| if !sv.IsValid() { |
| sv = reflect.ValueOf(src) |
| } |
| |
| dv := reflect.Indirect(dpv) |
| if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) { |
| switch b := src.(type) { |
| case []byte: |
| dv.Set(reflect.ValueOf(cloneBytes(b))) |
| default: |
| dv.Set(sv) |
| } |
| return nil |
| } |
| |
| if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) { |
| dv.Set(sv.Convert(dv.Type())) |
| return nil |
| } |
| |
| // The following conversions use a string value as an intermediate representation |
| // to convert between various numeric types. |
| // |
| // This also allows scanning into user defined types such as "type Int int64". |
| // For symmetry, also check for string destination types. |
| switch dv.Kind() { |
| case reflect.Ptr: |
| if src == nil { |
| dv.Set(reflect.Zero(dv.Type())) |
| return nil |
| } |
| dv.Set(reflect.New(dv.Type().Elem())) |
| return convertAssignRows(dv.Interface(), src, rows) |
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
| if src == nil { |
| return fmt.Errorf("converting NULL to %s is unsupported", dv.Kind()) |
| } |
| s := asString(src) |
| i64, err := strconv.ParseInt(s, 10, dv.Type().Bits()) |
| if err != nil { |
| err = strconvErr(err) |
| return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) |
| } |
| dv.SetInt(i64) |
| return nil |
| case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
| if src == nil { |
| return fmt.Errorf("converting NULL to %s is unsupported", dv.Kind()) |
| } |
| s := asString(src) |
| u64, err := strconv.ParseUint(s, 10, dv.Type().Bits()) |
| if err != nil { |
| err = strconvErr(err) |
| return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) |
| } |
| dv.SetUint(u64) |
| return nil |
| case reflect.Float32, reflect.Float64: |
| if src == nil { |
| return fmt.Errorf("converting NULL to %s is unsupported", dv.Kind()) |
| } |
| s := asString(src) |
| f64, err := strconv.ParseFloat(s, dv.Type().Bits()) |
| if err != nil { |
| err = strconvErr(err) |
| return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) |
| } |
| dv.SetFloat(f64) |
| return nil |
| case reflect.String: |
| if src == nil { |
| return fmt.Errorf("converting NULL to %s is unsupported", dv.Kind()) |
| } |
| switch v := src.(type) { |
| case string: |
| dv.SetString(v) |
| return nil |
| case []byte: |
| dv.SetString(string(v)) |
| return nil |
| } |
| } |
| |
| return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest) |
| } |
| |
| func strconvErr(err error) error { |
| if ne, ok := err.(*strconv.NumError); ok { |
| return ne.Err |
| } |
| return err |
| } |
| |
| func cloneBytes(b []byte) []byte { |
| if b == nil { |
| return nil |
| } |
| c := make([]byte, len(b)) |
| copy(c, b) |
| return c |
| } |
| |
| func asString(src interface{}) string { |
| switch v := src.(type) { |
| case string: |
| return v |
| case []byte: |
| return string(v) |
| } |
| rv := reflect.ValueOf(src) |
| switch rv.Kind() { |
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
| return strconv.FormatInt(rv.Int(), 10) |
| case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
| return strconv.FormatUint(rv.Uint(), 10) |
| case reflect.Float64: |
| return strconv.FormatFloat(rv.Float(), 'g', -1, 64) |
| case reflect.Float32: |
| return strconv.FormatFloat(rv.Float(), 'g', -1, 32) |
| case reflect.Bool: |
| return strconv.FormatBool(rv.Bool()) |
| } |
| return fmt.Sprintf("%v", src) |
| } |
| |
| func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) { |
| switch rv.Kind() { |
| case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
| return strconv.AppendInt(buf, rv.Int(), 10), true |
| case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: |
| return strconv.AppendUint(buf, rv.Uint(), 10), true |
| case reflect.Float32: |
| return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true |
| case reflect.Float64: |
| return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true |
| case reflect.Bool: |
| return strconv.AppendBool(buf, rv.Bool()), true |
| case reflect.String: |
| s := rv.String() |
| return append(buf, s...), true |
| } |
| return |
| } |
| |
| var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem() |
| |
| type decimalDecompose interface { |
| // Decompose returns the internal decimal state in parts. |
| // If the provided buf has sufficient capacity, buf may be returned as the coefficient with |
| // the value set and length set as appropriate. |
| Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) |
| } |
| |
| type decimalCompose interface { |
| // Compose sets the internal decimal value from parts. If the value cannot be |
| // represented then an error should be returned. |
| Compose(form byte, negative bool, coefficient []byte, exponent int32) error |
| } |
| |
| // ConvertDbVersion convert a string db version to a number. |
| func ConvertDbVersion(version string) (int, error) { |
| parts := strings.Split(version, ".") |
| size := len(parts) |
| maxVersionDot := 3 |
| if size > maxVersionDot+1 { |
| return 0, fmt.Errorf("incompatible version format: %s", version) |
| } |
| |
| var res int |
| for idx, part := range parts { |
| if partInt, err := strconv.Atoi(part); err == nil { |
| res += calculatePartValue(partInt, size, idx) |
| } else { |
| subParts := strings.Split(part, "-") |
| if subPartInt, err := strconv.Atoi(subParts[0]); err == nil { |
| res += calculatePartValue(subPartInt, size, idx) |
| } |
| } |
| } |
| return res, nil |
| } |
| |
| func calculatePartValue(partNumeric, size, index int) int { |
| return partNumeric * int(math.Pow(100, float64(size-index))) |
| } |