google-cloud-bigquery 1.14.0 → 1.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/AUTHENTICATION.md +17 -54
  3. data/CHANGELOG.md +377 -0
  4. data/CONTRIBUTING.md +328 -116
  5. data/LOGGING.md +1 -1
  6. data/OVERVIEW.md +21 -20
  7. data/TROUBLESHOOTING.md +2 -8
  8. data/lib/google/cloud/bigquery/argument.rb +197 -0
  9. data/lib/google/cloud/bigquery/convert.rb +155 -173
  10. data/lib/google/cloud/bigquery/copy_job.rb +74 -26
  11. data/lib/google/cloud/bigquery/credentials.rb +5 -12
  12. data/lib/google/cloud/bigquery/data.rb +109 -18
  13. data/lib/google/cloud/bigquery/dataset/access.rb +474 -52
  14. data/lib/google/cloud/bigquery/dataset/list.rb +7 -13
  15. data/lib/google/cloud/bigquery/dataset/tag.rb +67 -0
  16. data/lib/google/cloud/bigquery/dataset.rb +1044 -287
  17. data/lib/google/cloud/bigquery/external/avro_source.rb +107 -0
  18. data/lib/google/cloud/bigquery/external/bigtable_source/column.rb +404 -0
  19. data/lib/google/cloud/bigquery/external/bigtable_source/column_family.rb +945 -0
  20. data/lib/google/cloud/bigquery/external/bigtable_source.rb +230 -0
  21. data/lib/google/cloud/bigquery/external/csv_source.rb +481 -0
  22. data/lib/google/cloud/bigquery/external/data_source.rb +771 -0
  23. data/lib/google/cloud/bigquery/external/json_source.rb +170 -0
  24. data/lib/google/cloud/bigquery/external/parquet_source.rb +148 -0
  25. data/lib/google/cloud/bigquery/external/sheets_source.rb +166 -0
  26. data/lib/google/cloud/bigquery/external.rb +50 -2256
  27. data/lib/google/cloud/bigquery/extract_job.rb +226 -61
  28. data/lib/google/cloud/bigquery/insert_response.rb +1 -3
  29. data/lib/google/cloud/bigquery/job/list.rb +10 -14
  30. data/lib/google/cloud/bigquery/job.rb +289 -14
  31. data/lib/google/cloud/bigquery/load_job.rb +810 -136
  32. data/lib/google/cloud/bigquery/model/list.rb +5 -9
  33. data/lib/google/cloud/bigquery/model.rb +247 -16
  34. data/lib/google/cloud/bigquery/policy.rb +432 -0
  35. data/lib/google/cloud/bigquery/project/list.rb +6 -11
  36. data/lib/google/cloud/bigquery/project.rb +509 -250
  37. data/lib/google/cloud/bigquery/query_job.rb +594 -128
  38. data/lib/google/cloud/bigquery/routine/list.rb +165 -0
  39. data/lib/google/cloud/bigquery/routine.rb +1227 -0
  40. data/lib/google/cloud/bigquery/schema/field.rb +413 -63
  41. data/lib/google/cloud/bigquery/schema.rb +221 -48
  42. data/lib/google/cloud/bigquery/service.rb +204 -112
  43. data/lib/google/cloud/bigquery/standard_sql.rb +269 -53
  44. data/lib/google/cloud/bigquery/table/async_inserter.rb +86 -43
  45. data/lib/google/cloud/bigquery/table/list.rb +6 -11
  46. data/lib/google/cloud/bigquery/table.rb +1470 -377
  47. data/lib/google/cloud/bigquery/time.rb +6 -0
  48. data/lib/google/cloud/bigquery/version.rb +1 -1
  49. data/lib/google/cloud/bigquery.rb +4 -6
  50. data/lib/google-cloud-bigquery.rb +14 -13
  51. metadata +66 -38
@@ -0,0 +1,197 @@
1
+ # Copyright 2020 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
+ # https://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
+
15
+
16
+ require "google/cloud/bigquery/standard_sql"
17
+
18
+ module Google
19
+ module Cloud
20
+ module Bigquery
21
+ ##
22
+ # # Argument
23
+ #
24
+ # Input/output argument of a function or a stored procedure. See {Routine}.
25
+ #
26
+ # @example
27
+ # require "google/cloud/bigquery"
28
+ #
29
+ # bigquery = Google::Cloud::Bigquery.new
30
+ # dataset = bigquery.dataset "my_dataset"
31
+ # routine = dataset.create_routine "my_routine" do |r|
32
+ # r.routine_type = "SCALAR_FUNCTION"
33
+ # r.language = :SQL
34
+ # r.body = "(SELECT SUM(IF(elem.name = \"foo\",elem.val,null)) FROM UNNEST(arr) AS elem)"
35
+ # r.arguments = [
36
+ # Google::Cloud::Bigquery::Argument.new(
37
+ # name: "arr",
38
+ # argument_kind: "FIXED_TYPE",
39
+ # data_type: Google::Cloud::Bigquery::StandardSql::DataType.new(
40
+ # type_kind: "ARRAY",
41
+ # array_element_type: Google::Cloud::Bigquery::StandardSql::DataType.new(
42
+ # type_kind: "STRUCT",
43
+ # struct_type: Google::Cloud::Bigquery::StandardSql::StructType.new(
44
+ # fields: [
45
+ # Google::Cloud::Bigquery::StandardSql::Field.new(
46
+ # name: "name",
47
+ # type: Google::Cloud::Bigquery::StandardSql::DataType.new(type_kind: "STRING")
48
+ # ),
49
+ # Google::Cloud::Bigquery::StandardSql::Field.new(
50
+ # name: "val",
51
+ # type: Google::Cloud::Bigquery::StandardSql::DataType.new(type_kind: "INT64")
52
+ # )
53
+ # ]
54
+ # )
55
+ # )
56
+ # )
57
+ # )
58
+ # ]
59
+ # end
60
+ #
61
+ class Argument
62
+ ##
63
+ # Creates a new, immutable Argument object.
64
+ #
65
+ # @overload initialize(data_type, kind, mode, name)
66
+ # @param [StandardSql::DataType, String] data_type The data type of the argument. Required unless
67
+ # {#argument_kind} is `ANY_TYPE`.
68
+ # @param [String] argument_kind The kind of argument. Optional. Defaults to `FIXED_TYPE`.
69
+ #
70
+ # * `FIXED_TYPE` - The argument is a variable with fully specified type, which can be a struct or an array,
71
+ # but not a table.
72
+ # * `ANY_TYPE` - The argument is any type, including struct or array, but not a table.
73
+ #
74
+ # To be added: `FIXED_TABLE`, `ANY_TABLE`.
75
+ # @param [String] mode Specifies whether the argument is input or output. Optional. Can be set for procedures
76
+ # only.
77
+ #
78
+ # * IN - The argument is input-only.
79
+ # * OUT - The argument is output-only.
80
+ # * INOUT - The argument is both an input and an output.
81
+ # @param [String] name The name of the argument. Optional. Can be absent for a function return argument.
82
+ #
83
+ def initialize **kwargs
84
+ kwargs[:data_type] = StandardSql::DataType.gapi_from_string_or_data_type kwargs[:data_type]
85
+ @gapi = Google::Apis::BigqueryV2::Argument.new(**kwargs)
86
+ end
87
+
88
+ ##
89
+ # The data type of the argument. Required unless {#argument_kind} is `ANY_TYPE`.
90
+ #
91
+ # @return [StandardSql::DataType] The data type.
92
+ #
93
+ def data_type
94
+ StandardSql::DataType.from_gapi @gapi.data_type
95
+ end
96
+
97
+ ##
98
+ # The kind of argument. Optional. Defaults to `FIXED_TYPE`.
99
+ #
100
+ # * `FIXED_TYPE` - The argument is a variable with fully specified type, which can be a struct or an array, but
101
+ # not a table.
102
+ # * `ANY_TYPE` - The argument is any type, including struct or array, but not a table.
103
+ #
104
+ # To be added: `FIXED_TABLE`, `ANY_TABLE`.
105
+ #
106
+ # @return [String] The upper case kind of argument.
107
+ #
108
+ def argument_kind
109
+ @gapi.argument_kind
110
+ end
111
+
112
+ ##
113
+ # Checks if the value of {#argument_kind} is `FIXED_TYPE`. The default is `true`.
114
+ #
115
+ # @return [Boolean] `true` when `FIXED_TYPE`, `false` otherwise.
116
+ #
117
+ def fixed_type?
118
+ return true if @gapi.argument_kind.nil?
119
+ @gapi.argument_kind == "FIXED_TYPE"
120
+ end
121
+
122
+ ##
123
+ # Checks if the value of {#argument_kind} is `ANY_TYPE`. The default is `false`.
124
+ #
125
+ # @return [Boolean] `true` when `ANY_TYPE`, `false` otherwise.
126
+ #
127
+ def any_type?
128
+ @gapi.argument_kind == "ANY_TYPE"
129
+ end
130
+
131
+ ##
132
+ # Specifies whether the argument is input or output. Optional. Can be set for procedures only.
133
+ #
134
+ # * IN - The argument is input-only.
135
+ # * OUT - The argument is output-only.
136
+ # * INOUT - The argument is both an input and an output.
137
+ #
138
+ # @return [String] The upper case input/output mode of the argument.
139
+ #
140
+ def mode
141
+ @gapi.mode
142
+ end
143
+
144
+ ##
145
+ # Checks if the value of {#mode} is `IN`. Can be set for procedures only. The default is `false`.
146
+ #
147
+ # @return [Boolean] `true` when `IN`, `false` otherwise.
148
+ #
149
+ def in?
150
+ @gapi.mode == "IN"
151
+ end
152
+
153
+ ##
154
+ # Checks if the value of {#mode} is `OUT`. Can be set for procedures only. The default is `false`.
155
+ #
156
+ # @return [Boolean] `true` when `OUT`, `false` otherwise.
157
+ #
158
+ def out?
159
+ @gapi.mode == "OUT"
160
+ end
161
+
162
+ ##
163
+ # Checks if the value of {#mode} is `INOUT`. Can be set for procedures only. The default is `false`.
164
+ #
165
+ # @return [Boolean] `true` when `INOUT`, `false` otherwise.
166
+ #
167
+ def inout?
168
+ @gapi.mode == "INOUT"
169
+ end
170
+
171
+ ##
172
+ #
173
+ # The name of the argument. Optional. Can be absent for a function return argument.
174
+ #
175
+ # @return [String] The name of the argument.
176
+ #
177
+ def name
178
+ @gapi.name
179
+ end
180
+
181
+ ##
182
+ # @private
183
+ def to_gapi
184
+ @gapi
185
+ end
186
+
187
+ ##
188
+ # @private New Argument from a Google API Client object.
189
+ def self.from_gapi gapi
190
+ new.tap do |a|
191
+ a.instance_variable_set :@gapi, gapi
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
@@ -23,30 +23,29 @@ require "date"
23
23
  module Google
24
24
  module Cloud
25
25
  module Bigquery
26
- # rubocop:disable Metrics/ModuleLength
27
-
28
26
  ##
29
27
  # @private
30
28
  #
31
- # Internal conversion of raw data values to/from Bigquery values
29
+ # Internal conversion of raw data values to/from BigQuery values
30
+ #
31
+ # | BigQuery | Ruby | Notes |
32
+ # |--------------|--------------------------------------|----------------------------------------------------|
33
+ # | `BOOL` | `true`/`false` | |
34
+ # | `INT64` | `Integer` | |
35
+ # | `FLOAT64` | `Float` | |
36
+ # | `NUMERIC` | `BigDecimal` | `BigDecimal` values will be rounded to scale 9. |
37
+ # | `BIGNUMERIC` | converted to `BigDecimal` | Pass data as `String`; map query params in `types`.|
38
+ # | `STRING` | `String` | |
39
+ # | `DATETIME` | `DateTime` | `DATETIME` does not support time zone. |
40
+ # | `DATE` | `Date` | |
41
+ # | `GEOGRAPHY` | `String` | |
42
+ # | `TIMESTAMP` | `Time` | |
43
+ # | `TIME` | `Google::Cloud::BigQuery::Time` | |
44
+ # | `BYTES` | `File`, `IO`, `StringIO`, or similar | |
45
+ # | `ARRAY` | `Array` | Nested arrays, `nil` values are not supported. |
46
+ # | `STRUCT` | `Hash` | Hash keys may be strings or symbols. |
32
47
  #
33
- # | BigQuery | Ruby | Notes |
34
- # |-------------|----------------|---|
35
- # | `BOOL` | `true`/`false` | |
36
- # | `INT64` | `Integer` | |
37
- # | `FLOAT64` | `Float` | |
38
- # | `NUMERIC` | `BigDecimal` | Will be rounded to 9 decimal places |
39
- # | `STRING` | `String` | |
40
- # | `DATETIME` | `DateTime` | `DATETIME` does not support time zone. |
41
- # | `DATE` | `Date` | |
42
- # | `TIMESTAMP` | `Time` | |
43
- # | `TIME` | `Google::Cloud::BigQuery::Time` | |
44
- # | `BYTES` | `File`, `IO`, `StringIO`, or similar | |
45
- # | `ARRAY` | `Array` | Nested arrays, `nil` values are not supported. |
46
- # | `STRUCT` | `Hash` | Hash keys may be strings or symbols. |
47
48
  module Convert
48
- ##
49
- # @private
50
49
  def self.format_rows rows, fields
51
50
  Array(rows).map do |row|
52
51
  # convert TableRow to hash to handle nested TableCell values
@@ -54,13 +53,10 @@ module Google
54
53
  end
55
54
  end
56
55
 
57
- ##
58
- # @private
59
56
  def self.format_row row, fields
60
- row_pairs = fields.zip(row[:f]).map do |f, v|
57
+ fields.zip(row[:f]).to_h do |f, v|
61
58
  [f.name.to_sym, format_value(v, f)]
62
59
  end
63
- Hash[row_pairs]
64
60
  end
65
61
 
66
62
  # rubocop:disable all
@@ -75,11 +71,7 @@ module Google
75
71
  elsif Array === value[:v]
76
72
  value[:v].map { |v| format_value v, field }
77
73
  elsif Hash === value[:v]
78
- if value[:v].empty?
79
- nil
80
- else
81
- format_row value[:v], field.fields
82
- end
74
+ format_row value[:v], field.fields
83
75
  elsif field.type == "STRING"
84
76
  String value[:v]
85
77
  elsif field.type == "INTEGER"
@@ -96,6 +88,8 @@ module Google
96
88
  end
97
89
  elsif field.type == "NUMERIC"
98
90
  BigDecimal value[:v]
91
+ elsif field.type == "BIGNUMERIC"
92
+ BigDecimal value[:v]
99
93
  elsif field.type == "BOOLEAN"
100
94
  (value[:v] == "true" ? true : (value[:v] == "false" ? false : nil))
101
95
  elsif field.type == "BYTES"
@@ -108,135 +102,118 @@ module Google
108
102
  ::Time.parse("#{value[:v]} UTC").to_datetime
109
103
  elsif field.type == "DATE"
110
104
  Date.parse value[:v]
105
+ elsif field.type == "GEOGRAPHY"
106
+ String value[:v]
111
107
  else
112
108
  value[:v]
113
109
  end
114
110
  end
115
111
 
116
- ##
117
- # @private
118
- def self.to_query_param value
119
- if TrueClass === value
120
- return Google::Apis::BigqueryV2::QueryParameter.new(
121
- parameter_type: Google::Apis::BigqueryV2::QueryParameterType.new(
122
- type: "BOOL"),
123
- parameter_value: Google::Apis::BigqueryV2::QueryParameterValue.new(
124
- value: true)
125
- )
126
- elsif FalseClass === value
127
- return Google::Apis::BigqueryV2::QueryParameter.new(
128
- parameter_type: Google::Apis::BigqueryV2::QueryParameterType.new(
129
- type: "BOOL"),
130
- parameter_value: Google::Apis::BigqueryV2::QueryParameterValue.new(
131
- value: false)
132
- )
133
- elsif Integer === value
134
- return Google::Apis::BigqueryV2::QueryParameter.new(
135
- parameter_type: Google::Apis::BigqueryV2::QueryParameterType.new(
136
- type: "INT64"),
137
- parameter_value: Google::Apis::BigqueryV2::QueryParameterValue.new(
138
- value: value)
139
- )
140
- elsif Float === value
141
- return Google::Apis::BigqueryV2::QueryParameter.new(
142
- parameter_type: Google::Apis::BigqueryV2::QueryParameterType.new(
143
- type: "FLOAT64"),
144
- parameter_value: Google::Apis::BigqueryV2::QueryParameterValue.new(
145
- value: value)
146
- )
147
- elsif BigDecimal === value
148
- # Round to precision of 9
149
- value_str = value.finite? ? value.round(9).to_s("F") : value.to_s
150
- return Google::Apis::BigqueryV2::QueryParameter.new(
151
- parameter_type: Google::Apis::BigqueryV2::QueryParameterType.new(
152
- type: "NUMERIC"),
153
- parameter_value: Google::Apis::BigqueryV2::QueryParameterValue.new(
154
- value: value_str)
155
- )
156
- elsif String === value
157
- return Google::Apis::BigqueryV2::QueryParameter.new(
158
- parameter_type: Google::Apis::BigqueryV2::QueryParameterType.new(
159
- type: "STRING"),
160
- parameter_value: Google::Apis::BigqueryV2::QueryParameterValue.new(
161
- value: value)
162
- )
163
- elsif DateTime === value
164
- return Google::Apis::BigqueryV2::QueryParameter.new(
165
- parameter_type: Google::Apis::BigqueryV2::QueryParameterType.new(
166
- type: "DATETIME"),
167
- parameter_value: Google::Apis::BigqueryV2::QueryParameterValue.new(
168
- value: value.strftime("%Y-%m-%d %H:%M:%S.%6N"))
169
- )
170
- elsif Date === value
171
- return Google::Apis::BigqueryV2::QueryParameter.new(
172
- parameter_type: Google::Apis::BigqueryV2::QueryParameterType.new(
173
- type: "DATE"),
174
- parameter_value: Google::Apis::BigqueryV2::QueryParameterValue.new(
175
- value: value.to_s)
176
- )
177
- elsif ::Time === value
178
- return Google::Apis::BigqueryV2::QueryParameter.new(
179
- parameter_type: Google::Apis::BigqueryV2::QueryParameterType.new(
180
- type: "TIMESTAMP"),
181
- parameter_value: Google::Apis::BigqueryV2::QueryParameterValue.new(
182
- value: value.strftime("%Y-%m-%d %H:%M:%S.%6N%:z"))
183
- )
184
- elsif Bigquery::Time === value
185
- return Google::Apis::BigqueryV2::QueryParameter.new(
186
- parameter_type: Google::Apis::BigqueryV2::QueryParameterType.new(
187
- type: "TIME"),
188
- parameter_value: Google::Apis::BigqueryV2::QueryParameterValue.new(
189
- value: value.value)
190
- )
191
- elsif value.respond_to?(:read) && value.respond_to?(:rewind)
192
- value.rewind
193
- return Google::Apis::BigqueryV2::QueryParameter.new(
194
- parameter_type: Google::Apis::BigqueryV2::QueryParameterType.new(
195
- type: "BYTES"),
196
- parameter_value: Google::Apis::BigqueryV2::QueryParameterValue.new(
197
- value: Base64.strict_encode64(
198
- value.read.force_encoding("ASCII-8BIT")))
199
- )
200
- elsif Array === value
201
- array_params = value.map { |param| Convert.to_query_param param }
202
- return Google::Apis::BigqueryV2::QueryParameter.new(
203
- parameter_type: Google::Apis::BigqueryV2::QueryParameterType.new(
204
- type: "ARRAY",
205
- array_type: array_params.first.parameter_type
206
- ),
207
- parameter_value: Google::Apis::BigqueryV2::QueryParameterValue.new(
208
- array_values: array_params.map(&:parameter_value)
209
- )
210
- )
211
- elsif Hash === value
212
- struct_pairs = value.map do |name, param|
213
- struct_param = Convert.to_query_param param
214
- [Google::Apis::BigqueryV2::QueryParameterType::StructType.new(
215
- name: String(name),
216
- type: struct_param.parameter_type
217
- ), struct_param.parameter_value]
112
+ # rubocop:enable all
113
+
114
+ def self.to_query_param_value value, type = nil
115
+ return Google::Apis::BigqueryV2::QueryParameterValue.new value: nil if value.nil?
116
+
117
+ json_value = to_json_value value, type
118
+
119
+ case json_value
120
+ when Array
121
+ type = extract_array_type type
122
+ array_values = json_value.map { |v| to_query_param_value v, type }
123
+ Google::Apis::BigqueryV2::QueryParameterValue.new array_values: array_values
124
+ when Hash
125
+ struct_values = json_value.to_h do |k, v|
126
+ [String(k), to_query_param_value(v, type)]
218
127
  end
219
- struct_values = Hash[struct_pairs.map do |type, value|
220
- [type.name.to_sym, value]
221
- end]
128
+ Google::Apis::BigqueryV2::QueryParameterValue.new struct_values: struct_values
129
+ else
130
+ # Everything else is converted to a string, per the API expectations.
131
+ Google::Apis::BigqueryV2::QueryParameterValue.new value: json_value.to_s
132
+ end
133
+ end
222
134
 
223
- return Google::Apis::BigqueryV2::QueryParameter.new(
224
- parameter_type: Google::Apis::BigqueryV2::QueryParameterType.new(
225
- type: "STRUCT",
226
- struct_types: struct_pairs.map(&:first)
227
- ),
228
- parameter_value: Google::Apis::BigqueryV2::QueryParameterValue.new(
229
- struct_values: struct_values
230
- )
135
+ def self.to_query_param_type type
136
+ case type
137
+ when Array
138
+ Google::Apis::BigqueryV2::QueryParameterType.new(
139
+ type: "ARRAY".freeze,
140
+ array_type: to_query_param_type(type.first)
141
+ )
142
+ when Hash
143
+ Google::Apis::BigqueryV2::QueryParameterType.new(
144
+ type: "STRUCT".freeze,
145
+ struct_types: type.map do |key, val|
146
+ Google::Apis::BigqueryV2::QueryParameterType::StructType.new(
147
+ name: String(key),
148
+ type: to_query_param_type(val)
149
+ )
150
+ end
231
151
  )
232
152
  else
233
- raise "A query parameter of type #{value.class} is not supported."
153
+ Google::Apis::BigqueryV2::QueryParameterType.new type: type.to_s.freeze
234
154
  end
235
155
  end
236
156
 
237
- ##
238
- # @private
239
- def self.to_json_value value
157
+ # rubocop:disable Lint/DuplicateBranch
158
+ # rubocop:disable Metrics/CyclomaticComplexity
159
+ # rubocop:disable Style/GuardClause
160
+
161
+ def self.default_query_param_type_for param
162
+ raise ArgumentError, "nil params are not supported, must assign optional type" if param.nil?
163
+
164
+ case param
165
+ when String
166
+ :STRING
167
+ when Symbol
168
+ :STRING
169
+ when TrueClass
170
+ :BOOL
171
+ when FalseClass
172
+ :BOOL
173
+ when Integer
174
+ :INT64
175
+ when BigDecimal
176
+ :NUMERIC
177
+ when Numeric
178
+ :FLOAT64
179
+ when ::Time
180
+ :TIMESTAMP
181
+ when Bigquery::Time
182
+ :TIME
183
+ when DateTime
184
+ :DATETIME
185
+ when Date
186
+ :DATE
187
+ when Array
188
+ if param.empty?
189
+ raise ArgumentError, "Cannot determine type for empty array values"
190
+ end
191
+ non_nil_values = param.compact.map { |p| default_query_param_type_for p }.compact
192
+ if non_nil_values.empty?
193
+ raise ArgumentError, "Cannot determine type for array of nil values"
194
+ end
195
+ if non_nil_values.uniq.count > 1
196
+ raise ArgumentError, "Cannot determine type for array of different types of values"
197
+ end
198
+ [non_nil_values.first]
199
+ when Hash
200
+ param.transform_values do |value|
201
+ default_query_param_type_for value
202
+ end
203
+ else
204
+ if param.respond_to?(:read) && param.respond_to?(:rewind)
205
+ :BYTES
206
+ else
207
+ raise "A query parameter of type #{param.class} is not supported"
208
+ end
209
+ end
210
+ end
211
+
212
+ # rubocop:enable Lint/DuplicateBranch
213
+ # rubocop:enable Metrics/CyclomaticComplexity
214
+ # rubocop:enable Style/GuardClause
215
+
216
+ def self.to_json_value value, type = nil
240
217
  if DateTime === value
241
218
  value.strftime "%Y-%m-%d %H:%M:%S.%6N"
242
219
  elsif Date === value
@@ -245,30 +222,49 @@ module Google
245
222
  value.strftime "%Y-%m-%d %H:%M:%S.%6N%:z"
246
223
  elsif Bigquery::Time === value
247
224
  value.value
225
+ elsif BigDecimal === value
226
+ if value.finite?
227
+ # Round to precision of 9 unless explicit `BIGNUMERIC`
228
+ bigdecimal = type == :BIGNUMERIC ? value : value.round(9)
229
+ bigdecimal.to_s "F"
230
+ else
231
+ value.to_s
232
+ end
248
233
  elsif value.respond_to?(:read) && value.respond_to?(:rewind)
249
234
  value.rewind
250
- Base64.strict_encode64(value.read.force_encoding("ASCII-8BIT"))
235
+ Base64.strict_encode64 value.read.force_encoding("ASCII-8BIT")
251
236
  elsif Array === value
252
- value.map { |v| to_json_value v }
237
+ type = extract_array_type type
238
+ value.map { |x| to_json_value x, type }
253
239
  elsif Hash === value
254
- Hash[value.map { |k, v| [k.to_s, to_json_value(v)] }]
240
+ value.to_h { |k, v| [k.to_s, to_json_value(v, type)] }
255
241
  else
256
242
  value
257
243
  end
258
244
  end
259
245
 
260
- # rubocop:enable all
246
+ def self.to_query_param param, type = nil
247
+ type ||= default_query_param_type_for param
261
248
 
262
- ##
263
- # @private
264
- def self.to_json_rows rows
265
- rows.map { |row| to_json_row row }
249
+ Google::Apis::BigqueryV2::QueryParameter.new(
250
+ parameter_type: to_query_param_type(type),
251
+ parameter_value: to_query_param_value(param, type)
252
+ )
266
253
  end
267
254
 
268
255
  ##
269
- # @private
256
+ # Lists are specified by providing the type code in an array. For example, an array of integers are specified as
257
+ # `[:INT64]`. Extracts the symbol/hash.
258
+ def self.extract_array_type type
259
+ return nil if type.nil?
260
+ unless type.is_a?(Array) && type.count == 1 && (type.first.is_a?(Symbol) || type.first.is_a?(Hash))
261
+ raise ArgumentError, "types Array #{type.inspect} should include only a single symbol or hash element."
262
+ end
263
+ type.first
264
+ end
265
+
270
266
  def self.to_json_row row
271
- Hash[row.map { |k, v| [k.to_s, to_json_value(v)] }]
267
+ row.to_h { |k, v| [k.to_s, to_json_value(v)] }
272
268
  end
273
269
 
274
270
  def self.resolve_legacy_sql standard_sql, legacy_sql
@@ -278,8 +274,6 @@ module Google
278
274
  end
279
275
 
280
276
  ##
281
- # @private
282
- #
283
277
  # Converts create disposition strings to API values.
284
278
  #
285
279
  # @return [String] API representation of create disposition.
@@ -298,8 +292,6 @@ module Google
298
292
  end
299
293
 
300
294
  ##
301
- # @private
302
- #
303
295
  # Converts write disposition strings to API values.
304
296
  #
305
297
  # @return [String] API representation of write disposition.
@@ -320,8 +312,6 @@ module Google
320
312
  end
321
313
 
322
314
  ##
323
- # @private
324
- #
325
315
  # Converts source format strings to API values.
326
316
  #
327
317
  # @return [String] API representation of source format.
@@ -335,15 +325,15 @@ module Google
335
325
  "parquet" => "PARQUET",
336
326
  "datastore" => "DATASTORE_BACKUP",
337
327
  "backup" => "DATASTORE_BACKUP",
338
- "datastore_backup" => "DATASTORE_BACKUP"
328
+ "datastore_backup" => "DATASTORE_BACKUP",
329
+ "ml_tf_saved_model" => "ML_TF_SAVED_MODEL",
330
+ "ml_xgboost_booster" => "ML_XGBOOST_BOOSTER"
339
331
  }[format.to_s.downcase]
340
332
  return val unless val.nil?
341
333
  format
342
334
  end
343
335
 
344
336
  ##
345
- # @private
346
- #
347
337
  # Converts file paths into source format by extension.
348
338
  #
349
339
  # @return [String] API representation of source format.
@@ -354,8 +344,6 @@ module Google
354
344
  end
355
345
 
356
346
  ##
357
- # @private
358
- #
359
347
  # Converts file path into source format by extension.
360
348
  #
361
349
  # @return [String] API representation of source format.
@@ -370,8 +358,6 @@ module Google
370
358
  end
371
359
 
372
360
  ##
373
- # @private
374
- #
375
361
  # Converts a primitive time value in milliseconds to a Ruby Time object.
376
362
  #
377
363
  # @return [Time, nil] The Ruby Time object, or nil if the given argument
@@ -382,19 +368,15 @@ module Google
382
368
  end
383
369
 
384
370
  ##
385
- # @private
386
- #
387
371
  # Converts a Ruby Time object to a primitive time value in milliseconds.
388
372
  #
389
373
  # @return [Integer, nil] The primitive time value in milliseconds, or
390
374
  # nil if the given argument is nil.
391
375
  def self.time_to_millis time_obj
392
376
  return nil unless time_obj
393
- (time_obj.to_i * 1000) + (time_obj.nsec / 1000000)
377
+ (time_obj.to_i * 1000) + (time_obj.nsec / 1_000_000)
394
378
  end
395
379
  end
396
-
397
- # rubocop:enable Metrics/ModuleLength
398
380
  end
399
381
  end
400
382
  end