in go/rows.go [271:362]
func (r *Rows) athenaTypeToGoType(columnInfo *athena.ColumnInfo, rawValue *string, driverConfig *Config) (interface{}, error) {
if maskedValue, masked := driverConfig.CheckColumnMasked(*columnInfo.Name); masked { // "comma ok" idiom
return maskedValue, nil
}
if rawValue == nil {
r.tracer.Scope().Counter(DriverName + ".missingvalue").Inc(1)
r.tracer.Log(ErrorLevel, "missing data",
zap.String("columnInfo.Name", *columnInfo.Name),
zap.String("queryID", r.queryID),
zap.String("workgroup", driverConfig.GetWorkgroup().Name))
if driverConfig.IsMissingAsNil() {
return nil, nil
} else if driverConfig.IsMissingAsEmptyString() {
return "", nil
} else if driverConfig.IsMissingAsDefault() {
return r.getDefaultValueForColumnType(*columnInfo.Type), nil
}
r.tracer.Scope().Counter(DriverName + ".failure.convertvalue.config").Inc(1)
r.tracer.Log(ErrorLevel, "missing data", zap.String("columnInfo.Name", *columnInfo.Name))
return nil, fmt.Errorf("Missing data at column " + *columnInfo.Name)
}
val := *rawValue
// https://stackoverflow.com/questions/30299649/parse-string-to-specific-type-of-int-int8-int16-int32-int64
// https://prestodb.io/docs/current/language/types.html#integer
var err error
var i int64
var f float64
switch *columnInfo.Type {
case "tinyint":
// strconv.ParseInt() behavior is to return (int64(0), err)
// which is not as good as just return (nil, err)
if i, err = strconv.ParseInt(val, 10, 8); err != nil {
return nil, err
}
return int8(i), nil
case "smallint":
if i, err = strconv.ParseInt(val, 10, 16); err != nil {
return nil, err
}
return int16(i), nil
case "integer":
if i, err = strconv.ParseInt(val, 10, 32); err != nil {
return nil, err
}
return int32(i), nil
case "bigint":
if i, err = strconv.ParseInt(val, 10, 64); err != nil {
return nil, err
}
return i, nil
case "float", "real":
if f, err = strconv.ParseFloat(val, 32); err != nil {
return nil, err
}
return float32(f), nil
case "double":
if f, err = strconv.ParseFloat(val, 64); err != nil {
return nil, err
}
return f, nil
// for binary, we assume all chars are 0 or 1; for json,
// we assume the json syntax is correct. Leave to caller to verify it.
case "json", "char", "varchar", "varbinary", "row", "string", "binary",
"struct", "interval year to month", "interval day to second", "decimal",
"ipaddress", "array", "map", "unknown":
return val, nil
case "boolean":
if val == "true" {
return true, nil
} else if val == "false" {
return false, nil
}
r.tracer.Scope().Counter(DriverName + ".failure.convertvalue.boolean").Inc(1)
r.tracer.Log(ErrorLevel, "boolean data error", zap.String("val", val))
return nil, fmt.Errorf("unknown value `%s` for boolean", val)
case "date", "time", "time with time zone", "timestamp", "timestamp with time zone":
vv, err := scanTime(val)
if !vv.Valid {
r.tracer.Scope().Counter(DriverName + ".failure.convertvalue." +
"time").Inc(1)
r.tracer.Log(ErrorLevel, "time data error",
zap.String("val", val),
zap.String("type", *columnInfo.Type))
return nil, err
}
return vv.Time, err
default:
r.tracer.Scope().Counter(DriverName + ".failure.convertvalue.type").Inc(1)
r.tracer.Log(ErrorLevel, "column data type error", zap.String("columnInfo.Type", *columnInfo.Type))
return nil, fmt.Errorf("unknown type `%s` with value %s", *columnInfo.Type, val)
}
}