google-cloud-bigquery 1.18.1 → 1.19.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.
@@ -0,0 +1,165 @@
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 "delegate"
17
+
18
+ module Google
19
+ module Cloud
20
+ module Bigquery
21
+ class Routine
22
+ ##
23
+ # Routine::List is a special case Array with additional values.
24
+ class List < DelegateClass(::Array)
25
+ ##
26
+ # If not empty, indicates that there are more records that match
27
+ # the request and this value should be passed to continue.
28
+ attr_accessor :token
29
+
30
+ ##
31
+ # @private Create a new Routine::List with an array of routines.
32
+ def initialize arr = []
33
+ super arr
34
+ end
35
+
36
+ ##
37
+ # Whether there is a next page of routines.
38
+ #
39
+ # @return [Boolean]
40
+ #
41
+ # @example
42
+ # require "google/cloud/bigquery"
43
+ #
44
+ # bigquery = Google::Cloud::Bigquery.new
45
+ # dataset = bigquery.dataset "my_dataset"
46
+ #
47
+ # routines = dataset.routines
48
+ # if routines.next?
49
+ # next_routines = routines.next
50
+ # end
51
+ #
52
+ def next?
53
+ !token.nil?
54
+ end
55
+
56
+ ##
57
+ # Retrieve the next page of routines.
58
+ #
59
+ # @return [Routine::List]
60
+ #
61
+ # @example
62
+ # require "google/cloud/bigquery"
63
+ #
64
+ # bigquery = Google::Cloud::Bigquery.new
65
+ # dataset = bigquery.dataset "my_dataset"
66
+ #
67
+ # routines = dataset.routines
68
+ # if routines.next?
69
+ # next_routines = routines.next
70
+ # end
71
+ #
72
+ def next
73
+ return nil unless next?
74
+ ensure_service!
75
+ gapi = @service.list_routines @dataset_id, token: token, max: @max, filter: @filter
76
+ self.class.from_gapi gapi, @service, @dataset_id, @max, filter: @filter
77
+ end
78
+
79
+ ##
80
+ # Retrieves remaining results by repeatedly invoking {#next} until
81
+ # {#next?} returns `false`. Calls the given block once for each
82
+ # result, which is passed as the argument to the block.
83
+ #
84
+ # An Enumerator is returned if no block is given.
85
+ #
86
+ # This method will make repeated API calls until all remaining results
87
+ # are retrieved. (Unlike `#each`, for example, which merely iterates
88
+ # over the results returned by a single API call.) Use with caution.
89
+ #
90
+ # @param [Integer] request_limit The upper limit of API requests to
91
+ # make to load all routines. Default is no limit.
92
+ # @yield [routine] The block for accessing each routine.
93
+ # @yieldparam [Routine] routine The routine object.
94
+ #
95
+ # @return [Enumerator]
96
+ #
97
+ # @example Iterating each result by passing a block:
98
+ # require "google/cloud/bigquery"
99
+ #
100
+ # bigquery = Google::Cloud::Bigquery.new
101
+ # dataset = bigquery.dataset "my_dataset"
102
+ #
103
+ # dataset.routines.all do |routine|
104
+ # puts routine.routine_id
105
+ # end
106
+ #
107
+ # @example Using the enumerator by not passing a block:
108
+ # require "google/cloud/bigquery"
109
+ #
110
+ # bigquery = Google::Cloud::Bigquery.new
111
+ # dataset = bigquery.dataset "my_dataset"
112
+ #
113
+ # all_names = dataset.routines.all.map do |routine|
114
+ # routine.routine_id
115
+ # end
116
+ #
117
+ # @example Limit the number of API requests made:
118
+ # require "google/cloud/bigquery"
119
+ #
120
+ # bigquery = Google::Cloud::Bigquery.new
121
+ # dataset = bigquery.dataset "my_dataset"
122
+ #
123
+ # dataset.routines.all(request_limit: 10) do |routine|
124
+ # puts routine.routine_id
125
+ # end
126
+ #
127
+ def all request_limit: nil
128
+ request_limit = request_limit.to_i if request_limit
129
+ return enum_for :all, request_limit: request_limit unless block_given?
130
+ results = self
131
+ loop do
132
+ results.each { |r| yield r }
133
+ if request_limit
134
+ request_limit -= 1
135
+ break if request_limit.negative?
136
+ end
137
+ break unless results.next?
138
+ results = results.next
139
+ end
140
+ end
141
+
142
+ ##
143
+ # @private New Routine::List from a response object.
144
+ def self.from_gapi gapi_list, service, dataset_id = nil, max = nil, filter: nil
145
+ routines = List.new(Array(gapi_list.routines).map { |gapi| Routine.from_gapi gapi, service })
146
+ routines.instance_variable_set :@token, gapi_list.next_page_token
147
+ routines.instance_variable_set :@service, service
148
+ routines.instance_variable_set :@dataset_id, dataset_id
149
+ routines.instance_variable_set :@max, max
150
+ routines.instance_variable_set :@filter, filter
151
+ routines
152
+ end
153
+
154
+ protected
155
+
156
+ ##
157
+ # Raise an error unless an active service is available.
158
+ def ensure_service!
159
+ raise "Must have active connection" unless @service
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -78,12 +78,10 @@ module Google
78
78
  ##
79
79
  # Lists all datasets in the specified project to which you have
80
80
  # been granted the READER dataset role.
81
- def list_datasets options = {}
81
+ def list_datasets all: nil, filter: nil, max: nil, token: nil
82
82
  # The list operation is considered idempotent
83
83
  execute backoff: true do
84
- service.list_datasets \
85
- @project, all: options[:all], filter: options[:filter],
86
- max_results: options[:max], page_token: options[:token]
84
+ service.list_datasets @project, all: all, filter: filter, max_results: max, page_token: token
87
85
  end
88
86
  end
89
87
 
@@ -133,10 +131,10 @@ module Google
133
131
  ##
134
132
  # Lists all tables in the specified dataset.
135
133
  # Requires the READER dataset role.
136
- def list_tables dataset_id, options = {}
134
+ def list_tables dataset_id, max: nil, token: nil
137
135
  # The list operation is considered idempotent
138
136
  execute backoff: true do
139
- service.list_tables @project, dataset_id, max_results: options[:max], page_token: options[:token]
137
+ service.list_tables @project, dataset_id, max_results: max, page_token: token
140
138
  end
141
139
  end
142
140
 
@@ -190,38 +188,45 @@ module Google
190
188
 
191
189
  ##
192
190
  # Retrieves data from the table.
193
- def list_tabledata dataset_id, table_id, options = {}
191
+ def list_tabledata dataset_id, table_id, max: nil, token: nil, start: nil
194
192
  # The list operation is considered idempotent
195
193
  execute backoff: true do
196
194
  json_txt = service.list_table_data \
197
195
  @project, dataset_id, table_id,
198
- max_results: options.delete(:max),
199
- page_token: options.delete(:token),
200
- start_index: options.delete(:start),
196
+ max_results: max,
197
+ page_token: token,
198
+ start_index: start,
201
199
  options: { skip_deserialization: true }
202
200
  JSON.parse json_txt, symbolize_names: true
203
201
  end
204
202
  end
205
203
 
206
- def insert_tabledata dataset_id, table_id, rows, options = {}
204
+ def insert_tabledata dataset_id, table_id, rows, insert_ids: nil, ignore_unknown: nil, skip_invalid: nil
207
205
  json_rows = Array(rows).map { |row| Convert.to_json_row row }
208
- insert_tabledata_json_rows dataset_id, table_id, json_rows, options
206
+ insert_tabledata_json_rows dataset_id, table_id, json_rows, insert_ids: insert_ids,
207
+ ignore_unknown: ignore_unknown,
208
+ skip_invalid: skip_invalid
209
209
  end
210
210
 
211
- def insert_tabledata_json_rows dataset_id, table_id, json_rows, options = {}
212
- rows_and_ids = Array(json_rows).zip Array(options[:insert_ids])
211
+ def insert_tabledata_json_rows dataset_id, table_id, json_rows, insert_ids: nil, ignore_unknown: nil,
212
+ skip_invalid: nil
213
+ rows_and_ids = Array(json_rows).zip Array(insert_ids)
213
214
  insert_rows = rows_and_ids.map do |json_row, insert_id|
214
- insert_id ||= SecureRandom.uuid
215
- {
216
- insertId: insert_id,
217
- json: json_row
218
- }
215
+ if insert_id == :skip
216
+ { json: json_row }
217
+ else
218
+ insert_id ||= SecureRandom.uuid
219
+ {
220
+ insertId: insert_id,
221
+ json: json_row
222
+ }
223
+ end
219
224
  end
220
225
 
221
226
  insert_req = {
222
227
  rows: insert_rows,
223
- ignoreUnknownValues: options[:ignore_unknown],
224
- skipInvalidRows: options[:skip_invalid]
228
+ ignoreUnknownValues: ignore_unknown,
229
+ skipInvalidRows: skip_invalid
225
230
  }.to_json
226
231
 
227
232
  # The insertAll with insertId operation is considered idempotent
@@ -281,16 +286,66 @@ module Google
281
286
  execute { service.delete_model @project, dataset_id, model_id }
282
287
  end
283
288
 
289
+ ##
290
+ # Creates a new routine in the dataset.
291
+ def insert_routine dataset_id, new_routine_gapi
292
+ execute { service.insert_routine @project, dataset_id, new_routine_gapi }
293
+ end
294
+
295
+ ##
296
+ # Lists all routines in the specified dataset.
297
+ # Requires the READER dataset role.
298
+ # Unless readMask is set in the request, only the following fields are populated:
299
+ # etag, projectId, datasetId, routineId, routineType, creationTime, lastModifiedTime, and language.
300
+ def list_routines dataset_id, max: nil, token: nil, filter: nil
301
+ # The list operation is considered idempotent
302
+ execute backoff: true do
303
+ service.list_routines @project, dataset_id, max_results: max,
304
+ page_token: token,
305
+ filter: filter
306
+ end
307
+ end
308
+
309
+ ##
310
+ # Gets the specified routine resource by routine ID.
311
+ def get_routine dataset_id, routine_id
312
+ # The get operation is considered idempotent
313
+ execute backoff: true do
314
+ service.get_routine @project, dataset_id, routine_id
315
+ end
316
+ end
317
+
318
+ ##
319
+ # Updates information in an existing routine, replacing the entire routine resource.
320
+ def update_routine dataset_id, routine_id, new_routine_gapi
321
+ update_with_backoff = false
322
+ options = {}
323
+ if new_routine_gapi.etag
324
+ options[:header] = { "If-Match" => new_routine_gapi.etag }
325
+ # The update with etag operation is considered idempotent
326
+ update_with_backoff = true
327
+ end
328
+ execute backoff: update_with_backoff do
329
+ service.update_routine @project, dataset_id, routine_id, new_routine_gapi, options: options
330
+ end
331
+ end
332
+
333
+ ##
334
+ # Deletes the routine specified by routine_id from the dataset.
335
+ def delete_routine dataset_id, routine_id
336
+ execute { service.delete_routine @project, dataset_id, routine_id }
337
+ end
338
+
284
339
  ##
285
340
  # Lists all jobs in the specified project to which you have
286
341
  # been granted the READER job role.
287
- def list_jobs options = {}
342
+ def list_jobs all: nil, max: nil, token: nil, filter: nil, min_created_at: nil, max_created_at: nil
288
343
  # The list operation is considered idempotent
289
- min_creation_time = Convert.time_to_millis options[:min_created_at]
290
- max_creation_time = Convert.time_to_millis options[:max_created_at]
344
+ min_creation_time = Convert.time_to_millis min_created_at
345
+ max_creation_time = Convert.time_to_millis max_created_at
291
346
  execute backoff: true do
292
- service.list_jobs @project, all_users: options[:all], max_results: options[:max],
293
- page_token: options[:token], projection: "full", state_filter: options[:filter],
347
+ service.list_jobs @project, all_users: all, max_results: max,
348
+ page_token: token, projection: "full", state_filter: filter,
294
349
  min_creation_time: min_creation_time, max_creation_time: max_creation_time
295
350
  end
296
351
  end
@@ -329,15 +384,15 @@ module Google
329
384
 
330
385
  ##
331
386
  # Returns the query data for the job
332
- def job_query_results job_id, options = {}
387
+ def job_query_results job_id, location: nil, max: nil, token: nil, start: nil, timeout: nil
333
388
  # The get operation is considered idempotent
334
389
  execute backoff: true do
335
390
  service.get_job_query_results @project, job_id,
336
- location: options.delete(:location),
337
- max_results: options.delete(:max),
338
- page_token: options.delete(:token),
339
- start_index: options.delete(:start),
340
- timeout_ms: options.delete(:timeout)
391
+ location: location,
392
+ max_results: max,
393
+ page_token: token,
394
+ start_index: start,
395
+ timeout_ms: timeout
341
396
  end
342
397
  end
343
398
 
@@ -405,9 +460,9 @@ module Google
405
460
 
406
461
  ##
407
462
  # Lists all projects to which you have been granted any project role.
408
- def list_projects options = {}
463
+ def list_projects max: nil, token: nil
409
464
  execute backoff: true do
410
- service.list_projects max_results: options[:max], page_token: options[:token]
465
+ service.list_projects max_results: max, page_token: token
411
466
  end
412
467
  end
413
468
 
@@ -485,10 +540,10 @@ module Google
485
540
  sleep delay
486
541
  end
487
542
 
488
- def initialize options = {}
489
- @retries = (options[:retries] || Backoff.retries).to_i
490
- @reasons = (options[:reasons] || Backoff.reasons).to_a
491
- @backoff = options[:backoff] || Backoff.backoff
543
+ def initialize retries: nil, reasons: nil, backoff: nil
544
+ @retries = (retries || Backoff.retries).to_i
545
+ @reasons = (reasons || Backoff.reasons).to_a
546
+ @backoff = backoff || Backoff.backoff
492
547
  end
493
548
 
494
549
  def execute
@@ -18,89 +18,226 @@ module Google
18
18
  module Bigquery
19
19
  ##
20
20
  # BigQuery standard SQL is compliant with the SQL 2011 standard and has
21
- # extensions that support querying nested and repeated data.
21
+ # extensions that support querying nested and repeated data. See {Routine} and {Argument}.
22
+ #
23
+ # @example
24
+ # require "google/cloud/bigquery"
25
+ #
26
+ # bigquery = Google::Cloud::Bigquery.new
27
+ # dataset = bigquery.dataset "my_dataset"
28
+ # routine = dataset.create_routine "my_routine" do |r|
29
+ # r.routine_type = "SCALAR_FUNCTION"
30
+ # r.language = :SQL
31
+ # r.body = "(SELECT SUM(IF(elem.name = \"foo\",elem.val,null)) FROM UNNEST(arr) AS elem)"
32
+ # r.arguments = [
33
+ # Google::Cloud::Bigquery::Argument.new(
34
+ # name: "arr",
35
+ # argument_kind: "FIXED_TYPE",
36
+ # data_type: Google::Cloud::Bigquery::StandardSql::DataType.new(
37
+ # type_kind: "ARRAY",
38
+ # array_element_type: Google::Cloud::Bigquery::StandardSql::DataType.new(
39
+ # type_kind: "STRUCT",
40
+ # struct_type: Google::Cloud::Bigquery::StandardSql::StructType.new(
41
+ # fields: [
42
+ # Google::Cloud::Bigquery::StandardSql::Field.new(
43
+ # name: "name",
44
+ # type: Google::Cloud::Bigquery::StandardSql::DataType.new(type_kind: "STRING")
45
+ # ),
46
+ # Google::Cloud::Bigquery::StandardSql::Field.new(
47
+ # name: "val",
48
+ # type: Google::Cloud::Bigquery::StandardSql::DataType.new(type_kind: "INT64")
49
+ # )
50
+ # ]
51
+ # )
52
+ # )
53
+ # )
54
+ # )
55
+ # ]
56
+ # end
57
+ #
22
58
  module StandardSql
23
59
  ##
24
- # A field or a column.
60
+ # A field or a column. See {Routine} and {Argument}.
61
+ #
62
+ # @example
63
+ # require "google/cloud/bigquery"
64
+ #
65
+ # bigquery = Google::Cloud::Bigquery.new
66
+ # dataset = bigquery.dataset "my_dataset"
67
+ # routine = dataset.create_routine "my_routine" do |r|
68
+ # r.routine_type = "SCALAR_FUNCTION"
69
+ # r.language = :SQL
70
+ # r.body = "(SELECT SUM(IF(elem.name = \"foo\",elem.val,null)) FROM UNNEST(arr) AS elem)"
71
+ # r.arguments = [
72
+ # Google::Cloud::Bigquery::Argument.new(
73
+ # name: "arr",
74
+ # argument_kind: "FIXED_TYPE",
75
+ # data_type: Google::Cloud::Bigquery::StandardSql::DataType.new(
76
+ # type_kind: "ARRAY",
77
+ # array_element_type: Google::Cloud::Bigquery::StandardSql::DataType.new(
78
+ # type_kind: "STRUCT",
79
+ # struct_type: Google::Cloud::Bigquery::StandardSql::StructType.new(
80
+ # fields: [
81
+ # Google::Cloud::Bigquery::StandardSql::Field.new(
82
+ # name: "name",
83
+ # type: Google::Cloud::Bigquery::StandardSql::DataType.new(type_kind: "STRING")
84
+ # ),
85
+ # Google::Cloud::Bigquery::StandardSql::Field.new(
86
+ # name: "val",
87
+ # type: Google::Cloud::Bigquery::StandardSql::DataType.new(type_kind: "INT64")
88
+ # )
89
+ # ]
90
+ # )
91
+ # )
92
+ # )
93
+ # )
94
+ # ]
95
+ # end
96
+ #
25
97
  class Field
26
98
  ##
27
- # @private Create an empty StandardSql::Field object.
28
- def initialize
29
- @gapi_json = nil
99
+ # Creates a new, immutable StandardSql::Field object.
100
+ #
101
+ # @overload initialize(name, type)
102
+ # @param [String] name The name of the field. Optional. Can be absent for struct fields.
103
+ # @param [StandardSql::DataType, String] type The type of the field. Optional. Absent if not explicitly
104
+ # specified (e.g., `CREATE FUNCTION` statement can omit the return type; in this case the output parameter
105
+ # does not have this "type" field).
106
+ #
107
+ def initialize **kwargs
108
+ # Convert client object kwargs to a gapi object
109
+ kwargs[:type] = DataType.gapi_from_string_or_data_type kwargs[:type] if kwargs[:type]
110
+ @gapi = Google::Apis::BigqueryV2::StandardSqlField.new(**kwargs)
30
111
  end
31
112
 
32
113
  ##
33
- # The name of the field. (Can be absent for struct fields.)
114
+ # The name of the field. Optional. Can be absent for struct fields.
34
115
  #
35
116
  # @return [String, nil]
36
117
  #
37
118
  def name
38
- return nil if @gapi_json[:name] == "".freeze
39
-
40
- @gapi_json[:name]
119
+ return if @gapi.name == "".freeze
120
+ @gapi.name
41
121
  end
42
122
 
43
123
  ##
44
- # The type of the field.
124
+ # The type of the field. Optional. Absent if not explicitly specified (e.g., `CREATE FUNCTION` statement can
125
+ # omit the return type; in this case the output parameter does not have this "type" field).
45
126
  #
46
- # @return [DataType]
127
+ # @return [DataType, nil] The type of the field.
47
128
  #
48
129
  def type
49
- DataType.from_gapi_json @gapi_json[:type]
130
+ DataType.from_gapi @gapi.type if @gapi.type
131
+ end
132
+
133
+ ##
134
+ # @private New Google::Apis::BigqueryV2::StandardSqlField object.
135
+ def to_gapi
136
+ @gapi
50
137
  end
51
138
 
52
139
  ##
53
- # @private New StandardSql::Field from a JSON object.
54
- def self.from_gapi_json gapi_json
140
+ # @private New StandardSql::Field from a Google::Apis::BigqueryV2::StandardSqlField object.
141
+ def self.from_gapi gapi
55
142
  new.tap do |f|
56
- f.instance_variable_set :@gapi_json, gapi_json
143
+ f.instance_variable_set :@gapi, gapi
57
144
  end
58
145
  end
59
146
  end
60
147
 
61
148
  ##
62
- # The type of a field or a column.
149
+ # The type of a variable, e.g., a function argument. See {Routine} and {Argument}.
150
+ #
151
+ # @example
152
+ # require "google/cloud/bigquery"
153
+ #
154
+ # bigquery = Google::Cloud::Bigquery.new
155
+ # dataset = bigquery.dataset "my_dataset"
156
+ # routine = dataset.create_routine "my_routine" do |r|
157
+ # r.routine_type = "SCALAR_FUNCTION"
158
+ # r.language = :SQL
159
+ # r.body = "(SELECT SUM(IF(elem.name = \"foo\",elem.val,null)) FROM UNNEST(arr) AS elem)"
160
+ # r.arguments = [
161
+ # Google::Cloud::Bigquery::Argument.new(
162
+ # name: "arr",
163
+ # argument_kind: "FIXED_TYPE",
164
+ # data_type: Google::Cloud::Bigquery::StandardSql::DataType.new(
165
+ # type_kind: "ARRAY",
166
+ # array_element_type: Google::Cloud::Bigquery::StandardSql::DataType.new(
167
+ # type_kind: "STRUCT",
168
+ # struct_type: Google::Cloud::Bigquery::StandardSql::StructType.new(
169
+ # fields: [
170
+ # Google::Cloud::Bigquery::StandardSql::Field.new(
171
+ # name: "name",
172
+ # type: Google::Cloud::Bigquery::StandardSql::DataType.new(type_kind: "STRING")
173
+ # ),
174
+ # Google::Cloud::Bigquery::StandardSql::Field.new(
175
+ # name: "val",
176
+ # type: Google::Cloud::Bigquery::StandardSql::DataType.new(type_kind: "INT64")
177
+ # )
178
+ # ]
179
+ # )
180
+ # )
181
+ # )
182
+ # )
183
+ # ]
184
+ # end
185
+ #
186
+ # @see https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types Standard SQL Data Types
187
+ #
63
188
  class DataType
64
189
  ##
65
- # @private Create an empty StandardSql::DataType object.
66
- def initialize
67
- @gapi_json = nil
190
+ # Creates a new, immutable StandardSql::DataType object.
191
+ #
192
+ # @overload initialize(type_kind, array_element_type, struct_type)
193
+ # @param [String] type_kind The top level type of this field. Required. Can be [any standard SQL data
194
+ # type](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types) (e.g., `INT64`, `DATE`,
195
+ # `ARRAY`).
196
+ # @param [DataType, String] array_element_type The type of the array's elements, if {#type_kind} is `ARRAY`.
197
+ # See {#array?}. Optional.
198
+ # @param [StructType] struct_type The fields of the struct, in order, if {#type_kind} is `STRUCT`. See
199
+ # {#struct?}. Optional.
200
+ #
201
+ def initialize **kwargs
202
+ # Convert client object kwargs to a gapi object
203
+ if kwargs[:array_element_type]
204
+ kwargs[:array_element_type] = self.class.gapi_from_string_or_data_type kwargs[:array_element_type]
205
+ end
206
+ kwargs[:struct_type] = kwargs[:struct_type].to_gapi if kwargs[:struct_type]
207
+
208
+ @gapi = Google::Apis::BigqueryV2::StandardSqlDataType.new(**kwargs)
68
209
  end
69
210
 
70
211
  ##
71
- # The top level type of this field.
72
- #
73
- # Can be any standard SQL data type (e.g., "INT64", "DATE", "ARRAY").
212
+ # The top level type of this field. Required. Can be any standard SQL data type (e.g., `INT64`, `DATE`,
213
+ # `ARRAY`).
74
214
  #
75
- # @see https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types
76
- # Standard SQL Data Types
215
+ # @see https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types Standard SQL Data Types
77
216
  #
78
- # @return [String]
217
+ # @return [String] The upper case type.
79
218
  #
80
219
  def type_kind
81
- @gapi_json[:typeKind]
220
+ @gapi.type_kind
82
221
  end
83
222
 
84
223
  ##
85
- # The type of a fields when DataType is an Array. (See #array?)
224
+ # The type of the array's elements, if {#type_kind} is `ARRAY`. See {#array?}. Optional.
86
225
  #
87
226
  # @return [DataType, nil]
88
227
  #
89
228
  def array_element_type
90
- return if @gapi_json[:arrayElementType].nil?
91
-
92
- DataType.from_gapi_json @gapi_json[:arrayElementType]
229
+ return if @gapi.array_element_type.nil?
230
+ DataType.from_gapi @gapi.array_element_type
93
231
  end
94
232
 
95
233
  ##
96
- # The fields of the struct. (See #struct?)
234
+ # The fields of the struct, in order, if {#type_kind} is `STRUCT`. See {#struct?}. Optional.
97
235
  #
98
236
  # @return [StructType, nil]
99
237
  #
100
238
  def struct_type
101
- return if @gapi_json[:structType].nil?
102
-
103
- StructType.from_gapi_json @gapi_json[:structType]
239
+ return if @gapi.struct_type.nil?
240
+ StructType.from_gapi @gapi.struct_type
104
241
  end
105
242
 
106
243
  ##
@@ -247,41 +384,108 @@ module Google
247
384
  end
248
385
 
249
386
  ##
250
- # @private New StandardSql::DataType from a JSON object.
251
- def self.from_gapi_json gapi_json
252
- new.tap do |dt|
253
- dt.instance_variable_set :@gapi_json, gapi_json
387
+ # @private New Google::Apis::BigqueryV2::StandardSqlDataType object.
388
+ def to_gapi
389
+ @gapi
390
+ end
391
+
392
+ ##
393
+ # @private New StandardSql::DataType from a Google::Apis::BigqueryV2::StandardSqlDataType object.
394
+ def self.from_gapi gapi
395
+ new.tap do |f|
396
+ f.instance_variable_set :@gapi, gapi
397
+ end
398
+ end
399
+
400
+ ##
401
+ # @private New Google::Apis::BigqueryV2::StandardSqlDataType from a String or StandardSql::DataType object.
402
+ def self.gapi_from_string_or_data_type data_type
403
+ return if data_type.nil?
404
+ if data_type.is_a? StandardSql::DataType
405
+ data_type.to_gapi
406
+ elsif data_type.is_a? Hash
407
+ data_type
408
+ elsif data_type.is_a?(String) || data_type.is_a?(Symbol)
409
+ Google::Apis::BigqueryV2::StandardSqlDataType.new type_kind: data_type.to_s.upcase
410
+ else
411
+ raise ArgumentError, "Unable to convert #{data_type} to Google::Apis::BigqueryV2::StandardSqlDataType"
254
412
  end
255
413
  end
256
414
  end
257
415
 
258
416
  ##
259
- # The type of a `STRUCT` field or a column.
417
+ # The fields of a `STRUCT` type. See {DataType#struct_type}. See {Routine} and {Argument}.
418
+ #
419
+ # @example
420
+ # require "google/cloud/bigquery"
421
+ #
422
+ # bigquery = Google::Cloud::Bigquery.new
423
+ # dataset = bigquery.dataset "my_dataset"
424
+ # routine = dataset.create_routine "my_routine" do |r|
425
+ # r.routine_type = "SCALAR_FUNCTION"
426
+ # r.language = :SQL
427
+ # r.body = "(SELECT SUM(IF(elem.name = \"foo\",elem.val,null)) FROM UNNEST(arr) AS elem)"
428
+ # r.arguments = [
429
+ # Google::Cloud::Bigquery::Argument.new(
430
+ # name: "arr",
431
+ # argument_kind: "FIXED_TYPE",
432
+ # data_type: Google::Cloud::Bigquery::StandardSql::DataType.new(
433
+ # type_kind: "ARRAY",
434
+ # array_element_type: Google::Cloud::Bigquery::StandardSql::DataType.new(
435
+ # type_kind: "STRUCT",
436
+ # struct_type: Google::Cloud::Bigquery::StandardSql::StructType.new(
437
+ # fields: [
438
+ # Google::Cloud::Bigquery::StandardSql::Field.new(
439
+ # name: "name",
440
+ # type: Google::Cloud::Bigquery::StandardSql::DataType.new(type_kind: "STRING")
441
+ # ),
442
+ # Google::Cloud::Bigquery::StandardSql::Field.new(
443
+ # name: "val",
444
+ # type: Google::Cloud::Bigquery::StandardSql::DataType.new(type_kind: "INT64")
445
+ # )
446
+ # ]
447
+ # )
448
+ # )
449
+ # )
450
+ # )
451
+ # ]
452
+ # end
453
+ #
260
454
  class StructType
261
455
  ##
262
- # @private Create an empty StandardSql::DataType object.
263
- def initialize
264
- @gapi_json = nil
456
+ # Creates a new, immutable StandardSql::StructType object.
457
+ #
458
+ # @overload initialize(fields)
459
+ # @param [Array<Field>] fields The fields of the struct. Required.
460
+ #
461
+ def initialize **kwargs
462
+ # Convert each field client object to gapi object, if fields given (self.from_gapi does not pass kwargs)
463
+ kwargs[:fields] = kwargs[:fields]&.map(&:to_gapi) if kwargs[:fields]
464
+ @gapi = Google::Apis::BigqueryV2::StandardSqlStructType.new(**kwargs)
265
465
  end
266
466
 
267
467
  ##
268
- # The top level type of this field.
468
+ # The fields of the struct.
269
469
  #
270
- # Can be any standard SQL data type (e.g., "INT64", "DATE", "ARRAY").
271
- #
272
- # @return [Array<Field>]
470
+ # @return [Array<Field>] A frozen array of fields.
273
471
  #
274
472
  def fields
275
- Array(@gapi_json[:fields]).map do |field_gapi_json|
276
- Field.from_gapi_json field_gapi_json
277
- end
473
+ Array(@gapi.fields).map do |field_gapi|
474
+ Field.from_gapi field_gapi
475
+ end.freeze
476
+ end
477
+
478
+ ##
479
+ # @private New Google::Apis::BigqueryV2::StandardSqlStructType object.
480
+ def to_gapi
481
+ @gapi
278
482
  end
279
483
 
280
484
  ##
281
- # @private New StandardSql::StructType from a JSON object.
282
- def self.from_gapi_json gapi_json
283
- new.tap do |st|
284
- st.instance_variable_set :@gapi_json, gapi_json
485
+ # @private New StandardSql::StructType from a Google::Apis::BigqueryV2::StandardSqlStructType object.
486
+ def self.from_gapi gapi
487
+ new.tap do |f|
488
+ f.instance_variable_set :@gapi, gapi
285
489
  end
286
490
  end
287
491
  end