You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

371 lines
11 KiB

  1. // Copyright 2016 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package bigquery
  15. import (
  16. "encoding/base64"
  17. "errors"
  18. "fmt"
  19. "math/big"
  20. "reflect"
  21. "regexp"
  22. "time"
  23. "cloud.google.com/go/civil"
  24. "cloud.google.com/go/internal/fields"
  25. bq "google.golang.org/api/bigquery/v2"
  26. )
  27. var (
  28. // See https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#timestamp-type.
  29. timestampFormat = "2006-01-02 15:04:05.999999-07:00"
  30. // See https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#schema.fields.name
  31. validFieldName = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]{0,127}$")
  32. )
  33. const nullableTagOption = "nullable"
  34. func bqTagParser(t reflect.StructTag) (name string, keep bool, other interface{}, err error) {
  35. name, keep, opts, err := fields.ParseStandardTag("bigquery", t)
  36. if err != nil {
  37. return "", false, nil, err
  38. }
  39. if name != "" && !validFieldName.MatchString(name) {
  40. return "", false, nil, invalidFieldNameError(name)
  41. }
  42. for _, opt := range opts {
  43. if opt != nullableTagOption {
  44. return "", false, nil, fmt.Errorf(
  45. "bigquery: invalid tag option %q. The only valid option is %q",
  46. opt, nullableTagOption)
  47. }
  48. }
  49. return name, keep, opts, nil
  50. }
  51. type invalidFieldNameError string
  52. func (e invalidFieldNameError) Error() string {
  53. return fmt.Sprintf("bigquery: invalid name %q of field in struct", string(e))
  54. }
  55. var fieldCache = fields.NewCache(bqTagParser, nil, nil)
  56. var (
  57. int64ParamType = &bq.QueryParameterType{Type: "INT64"}
  58. float64ParamType = &bq.QueryParameterType{Type: "FLOAT64"}
  59. boolParamType = &bq.QueryParameterType{Type: "BOOL"}
  60. stringParamType = &bq.QueryParameterType{Type: "STRING"}
  61. bytesParamType = &bq.QueryParameterType{Type: "BYTES"}
  62. dateParamType = &bq.QueryParameterType{Type: "DATE"}
  63. timeParamType = &bq.QueryParameterType{Type: "TIME"}
  64. dateTimeParamType = &bq.QueryParameterType{Type: "DATETIME"}
  65. timestampParamType = &bq.QueryParameterType{Type: "TIMESTAMP"}
  66. numericParamType = &bq.QueryParameterType{Type: "NUMERIC"}
  67. )
  68. var (
  69. typeOfDate = reflect.TypeOf(civil.Date{})
  70. typeOfTime = reflect.TypeOf(civil.Time{})
  71. typeOfDateTime = reflect.TypeOf(civil.DateTime{})
  72. typeOfGoTime = reflect.TypeOf(time.Time{})
  73. typeOfRat = reflect.TypeOf(&big.Rat{})
  74. )
  75. // A QueryParameter is a parameter to a query.
  76. type QueryParameter struct {
  77. // Name is used for named parameter mode.
  78. // It must match the name in the query case-insensitively.
  79. Name string
  80. // Value is the value of the parameter.
  81. //
  82. // When you create a QueryParameter to send to BigQuery, the following Go types
  83. // are supported, with their corresponding Bigquery types:
  84. // int, int8, int16, int32, int64, uint8, uint16, uint32: INT64
  85. // Note that uint, uint64 and uintptr are not supported, because
  86. // they may contain values that cannot fit into a 64-bit signed integer.
  87. // float32, float64: FLOAT64
  88. // bool: BOOL
  89. // string: STRING
  90. // []byte: BYTES
  91. // time.Time: TIMESTAMP
  92. // *big.Rat: NUMERIC
  93. // Arrays and slices of the above.
  94. // Structs of the above. Only the exported fields are used.
  95. //
  96. // BigQuery does not support params of type GEOGRAPHY. For users wishing
  97. // to parameterize Geography values, use string parameters and cast in the
  98. // SQL query, e.g. `SELECT ST_GeogFromText(@string_param) as geo`
  99. //
  100. // When a QueryParameter is returned inside a QueryConfig from a call to
  101. // Job.Config:
  102. // Integers are of type int64.
  103. // Floating-point values are of type float64.
  104. // Arrays are of type []interface{}, regardless of the array element type.
  105. // Structs are of type map[string]interface{}.
  106. Value interface{}
  107. }
  108. func (p QueryParameter) toBQ() (*bq.QueryParameter, error) {
  109. pv, err := paramValue(reflect.ValueOf(p.Value))
  110. if err != nil {
  111. return nil, err
  112. }
  113. pt, err := paramType(reflect.TypeOf(p.Value))
  114. if err != nil {
  115. return nil, err
  116. }
  117. return &bq.QueryParameter{
  118. Name: p.Name,
  119. ParameterValue: &pv,
  120. ParameterType: pt,
  121. }, nil
  122. }
  123. func paramType(t reflect.Type) (*bq.QueryParameterType, error) {
  124. if t == nil {
  125. return nil, errors.New("bigquery: nil parameter")
  126. }
  127. switch t {
  128. case typeOfDate:
  129. return dateParamType, nil
  130. case typeOfTime:
  131. return timeParamType, nil
  132. case typeOfDateTime:
  133. return dateTimeParamType, nil
  134. case typeOfGoTime:
  135. return timestampParamType, nil
  136. case typeOfRat:
  137. return numericParamType, nil
  138. }
  139. switch t.Kind() {
  140. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32:
  141. return int64ParamType, nil
  142. case reflect.Float32, reflect.Float64:
  143. return float64ParamType, nil
  144. case reflect.Bool:
  145. return boolParamType, nil
  146. case reflect.String:
  147. return stringParamType, nil
  148. case reflect.Slice:
  149. if t.Elem().Kind() == reflect.Uint8 {
  150. return bytesParamType, nil
  151. }
  152. fallthrough
  153. case reflect.Array:
  154. et, err := paramType(t.Elem())
  155. if err != nil {
  156. return nil, err
  157. }
  158. return &bq.QueryParameterType{Type: "ARRAY", ArrayType: et}, nil
  159. case reflect.Ptr:
  160. if t.Elem().Kind() != reflect.Struct {
  161. break
  162. }
  163. t = t.Elem()
  164. fallthrough
  165. case reflect.Struct:
  166. var fts []*bq.QueryParameterTypeStructTypes
  167. fields, err := fieldCache.Fields(t)
  168. if err != nil {
  169. return nil, err
  170. }
  171. for _, f := range fields {
  172. pt, err := paramType(f.Type)
  173. if err != nil {
  174. return nil, err
  175. }
  176. fts = append(fts, &bq.QueryParameterTypeStructTypes{
  177. Name: f.Name,
  178. Type: pt,
  179. })
  180. }
  181. return &bq.QueryParameterType{Type: "STRUCT", StructTypes: fts}, nil
  182. }
  183. return nil, fmt.Errorf("bigquery: Go type %s cannot be represented as a parameter type", t)
  184. }
  185. func paramValue(v reflect.Value) (bq.QueryParameterValue, error) {
  186. var res bq.QueryParameterValue
  187. if !v.IsValid() {
  188. return res, errors.New("bigquery: nil parameter")
  189. }
  190. t := v.Type()
  191. switch t {
  192. case typeOfDate:
  193. res.Value = v.Interface().(civil.Date).String()
  194. return res, nil
  195. case typeOfTime:
  196. // civil.Time has nanosecond resolution, but BigQuery TIME only microsecond.
  197. // (If we send nanoseconds, then when we try to read the result we get "query job
  198. // missing destination table").
  199. res.Value = CivilTimeString(v.Interface().(civil.Time))
  200. return res, nil
  201. case typeOfDateTime:
  202. res.Value = CivilDateTimeString(v.Interface().(civil.DateTime))
  203. return res, nil
  204. case typeOfGoTime:
  205. res.Value = v.Interface().(time.Time).Format(timestampFormat)
  206. return res, nil
  207. case typeOfRat:
  208. res.Value = NumericString(v.Interface().(*big.Rat))
  209. return res, nil
  210. }
  211. switch t.Kind() {
  212. case reflect.Slice:
  213. if t.Elem().Kind() == reflect.Uint8 {
  214. res.Value = base64.StdEncoding.EncodeToString(v.Interface().([]byte))
  215. return res, nil
  216. }
  217. fallthrough
  218. case reflect.Array:
  219. var vals []*bq.QueryParameterValue
  220. for i := 0; i < v.Len(); i++ {
  221. val, err := paramValue(v.Index(i))
  222. if err != nil {
  223. return bq.QueryParameterValue{}, err
  224. }
  225. vals = append(vals, &val)
  226. }
  227. return bq.QueryParameterValue{ArrayValues: vals}, nil
  228. case reflect.Ptr:
  229. if t.Elem().Kind() != reflect.Struct {
  230. return res, fmt.Errorf("bigquery: Go type %s cannot be represented as a parameter value", t)
  231. }
  232. t = t.Elem()
  233. v = v.Elem()
  234. if !v.IsValid() {
  235. // nil pointer becomes empty value
  236. return res, nil
  237. }
  238. fallthrough
  239. case reflect.Struct:
  240. fields, err := fieldCache.Fields(t)
  241. if err != nil {
  242. return bq.QueryParameterValue{}, err
  243. }
  244. res.StructValues = map[string]bq.QueryParameterValue{}
  245. for _, f := range fields {
  246. fv := v.FieldByIndex(f.Index)
  247. fp, err := paramValue(fv)
  248. if err != nil {
  249. return bq.QueryParameterValue{}, err
  250. }
  251. res.StructValues[f.Name] = fp
  252. }
  253. return res, nil
  254. }
  255. // None of the above: assume a scalar type. (If it's not a valid type,
  256. // paramType will catch the error.)
  257. res.Value = fmt.Sprint(v.Interface())
  258. // Ensure empty string values are sent.
  259. if res.Value == "" {
  260. res.ForceSendFields = append(res.ForceSendFields, "Value")
  261. }
  262. return res, nil
  263. }
  264. func bqToQueryParameter(q *bq.QueryParameter) (QueryParameter, error) {
  265. p := QueryParameter{Name: q.Name}
  266. val, err := convertParamValue(q.ParameterValue, q.ParameterType)
  267. if err != nil {
  268. return QueryParameter{}, err
  269. }
  270. p.Value = val
  271. return p, nil
  272. }
  273. var paramTypeToFieldType = map[string]FieldType{
  274. int64ParamType.Type: IntegerFieldType,
  275. float64ParamType.Type: FloatFieldType,
  276. boolParamType.Type: BooleanFieldType,
  277. stringParamType.Type: StringFieldType,
  278. bytesParamType.Type: BytesFieldType,
  279. dateParamType.Type: DateFieldType,
  280. timeParamType.Type: TimeFieldType,
  281. numericParamType.Type: NumericFieldType,
  282. }
  283. // Convert a parameter value from the service to a Go value. This is similar to, but
  284. // not quite the same as, converting data values.
  285. func convertParamValue(qval *bq.QueryParameterValue, qtype *bq.QueryParameterType) (interface{}, error) {
  286. switch qtype.Type {
  287. case "ARRAY":
  288. if qval == nil {
  289. return []interface{}(nil), nil
  290. }
  291. return convertParamArray(qval.ArrayValues, qtype.ArrayType)
  292. case "STRUCT":
  293. if qval == nil {
  294. return map[string]interface{}(nil), nil
  295. }
  296. return convertParamStruct(qval.StructValues, qtype.StructTypes)
  297. case "TIMESTAMP":
  298. return time.Parse(timestampFormat, qval.Value)
  299. case "DATETIME":
  300. return parseCivilDateTime(qval.Value)
  301. default:
  302. return convertBasicType(qval.Value, paramTypeToFieldType[qtype.Type])
  303. }
  304. }
  305. // convertParamArray converts a query parameter array value to a Go value. It
  306. // always returns a []interface{}.
  307. func convertParamArray(elVals []*bq.QueryParameterValue, elType *bq.QueryParameterType) ([]interface{}, error) {
  308. var vals []interface{}
  309. for _, el := range elVals {
  310. val, err := convertParamValue(el, elType)
  311. if err != nil {
  312. return nil, err
  313. }
  314. vals = append(vals, val)
  315. }
  316. return vals, nil
  317. }
  318. // convertParamStruct converts a query parameter struct value into a Go value. It
  319. // always returns a map[string]interface{}.
  320. func convertParamStruct(sVals map[string]bq.QueryParameterValue, sTypes []*bq.QueryParameterTypeStructTypes) (map[string]interface{}, error) {
  321. vals := map[string]interface{}{}
  322. for _, st := range sTypes {
  323. if sv, ok := sVals[st.Name]; ok {
  324. val, err := convertParamValue(&sv, st.Type)
  325. if err != nil {
  326. return nil, err
  327. }
  328. vals[st.Name] = val
  329. } else {
  330. vals[st.Name] = nil
  331. }
  332. }
  333. return vals, nil
  334. }