google-cloud-bigquery 1.14.0 → 1.42.0

Sign up to get free protection for your applications and to get access to all the features.
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