123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- // Copyright 2015 Google Inc. All Rights Reserved.
- //
- // 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 bigquery
- import (
- "errors"
- "fmt"
- "strconv"
- "time"
- bq "google.golang.org/api/bigquery/v2"
- )
- // Value stores the contents of a single cell from a BigQuery result.
- type Value interface{}
- // ValueLoader stores a slice of Values representing a result row from a Read operation.
- // See Iterator.Get for more information.
- type ValueLoader interface {
- Load(v []Value) error
- }
- // ValueList converts a []Value to implement ValueLoader.
- type ValueList []Value
- // Load stores a sequence of values in a ValueList.
- func (vs *ValueList) Load(v []Value) error {
- *vs = append(*vs, v...)
- return nil
- }
- // convertRows converts a series of TableRows into a series of Value slices.
- // schema is used to interpret the data from rows; its length must match the
- // length of each row.
- func convertRows(rows []*bq.TableRow, schema Schema) ([][]Value, error) {
- var rs [][]Value
- for _, r := range rows {
- row, err := convertRow(r, schema)
- if err != nil {
- return nil, err
- }
- rs = append(rs, row)
- }
- return rs, nil
- }
- func convertRow(r *bq.TableRow, schema Schema) ([]Value, error) {
- if len(schema) != len(r.F) {
- return nil, errors.New("schema length does not match row length")
- }
- var values []Value
- for i, cell := range r.F {
- fs := schema[i]
- v, err := convertValue(cell.V, fs.Type, fs.Schema)
- if err != nil {
- return nil, err
- }
- values = append(values, v)
- }
- return values, nil
- }
- func convertValue(val interface{}, typ FieldType, schema Schema) (Value, error) {
- switch val := val.(type) {
- case nil:
- return nil, nil
- case []interface{}:
- return convertRepeatedRecord(val, typ, schema)
- case map[string]interface{}:
- return convertNestedRecord(val, schema)
- case string:
- return convertBasicType(val, typ)
- default:
- return nil, fmt.Errorf("got value %v; expected a value of type %s", val, typ)
- }
- }
- func convertRepeatedRecord(vals []interface{}, typ FieldType, schema Schema) (Value, error) {
- var values []Value
- for _, cell := range vals {
- // each cell contains a single entry, keyed by "v"
- val := cell.(map[string]interface{})["v"]
- v, err := convertValue(val, typ, schema)
- if err != nil {
- return nil, err
- }
- values = append(values, v)
- }
- return values, nil
- }
- func convertNestedRecord(val map[string]interface{}, schema Schema) (Value, error) {
- // convertNestedRecord is similar to convertRow, as a record has the same structure as a row.
- // Nested records are wrapped in a map with a single key, "f".
- record := val["f"].([]interface{})
- if len(record) != len(schema) {
- return nil, errors.New("schema length does not match record length")
- }
- var values []Value
- for i, cell := range record {
- // each cell contains a single entry, keyed by "v"
- val := cell.(map[string]interface{})["v"]
- fs := schema[i]
- v, err := convertValue(val, fs.Type, fs.Schema)
- if err != nil {
- return nil, err
- }
- values = append(values, v)
- }
- return values, nil
- }
- // convertBasicType returns val as an interface with a concrete type specified by typ.
- func convertBasicType(val string, typ FieldType) (Value, error) {
- switch typ {
- case StringFieldType:
- return val, nil
- case IntegerFieldType:
- return strconv.Atoi(val)
- case FloatFieldType:
- return strconv.ParseFloat(val, 64)
- case BooleanFieldType:
- return strconv.ParseBool(val)
- case TimestampFieldType:
- f, err := strconv.ParseFloat(val, 64)
- return Value(time.Unix(0, int64(f*1e9))), err
- default:
- return nil, errors.New("unrecognized type")
- }
- }
|