google-cloud-bigquery 1.21.2
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.
- checksums.yaml +7 -0
- data/.yardopts +16 -0
- data/AUTHENTICATION.md +158 -0
- data/CHANGELOG.md +397 -0
- data/CODE_OF_CONDUCT.md +40 -0
- data/CONTRIBUTING.md +188 -0
- data/LICENSE +201 -0
- data/LOGGING.md +27 -0
- data/OVERVIEW.md +463 -0
- data/TROUBLESHOOTING.md +31 -0
- data/lib/google-cloud-bigquery.rb +139 -0
- data/lib/google/cloud/bigquery.rb +145 -0
- data/lib/google/cloud/bigquery/argument.rb +197 -0
- data/lib/google/cloud/bigquery/convert.rb +383 -0
- data/lib/google/cloud/bigquery/copy_job.rb +316 -0
- data/lib/google/cloud/bigquery/credentials.rb +50 -0
- data/lib/google/cloud/bigquery/data.rb +526 -0
- data/lib/google/cloud/bigquery/dataset.rb +2845 -0
- data/lib/google/cloud/bigquery/dataset/access.rb +1021 -0
- data/lib/google/cloud/bigquery/dataset/list.rb +162 -0
- data/lib/google/cloud/bigquery/encryption_configuration.rb +123 -0
- data/lib/google/cloud/bigquery/external.rb +2432 -0
- data/lib/google/cloud/bigquery/extract_job.rb +368 -0
- data/lib/google/cloud/bigquery/insert_response.rb +180 -0
- data/lib/google/cloud/bigquery/job.rb +657 -0
- data/lib/google/cloud/bigquery/job/list.rb +162 -0
- data/lib/google/cloud/bigquery/load_job.rb +1704 -0
- data/lib/google/cloud/bigquery/model.rb +740 -0
- data/lib/google/cloud/bigquery/model/list.rb +164 -0
- data/lib/google/cloud/bigquery/project.rb +1655 -0
- data/lib/google/cloud/bigquery/project/list.rb +161 -0
- data/lib/google/cloud/bigquery/query_job.rb +1695 -0
- data/lib/google/cloud/bigquery/routine.rb +1108 -0
- data/lib/google/cloud/bigquery/routine/list.rb +165 -0
- data/lib/google/cloud/bigquery/schema.rb +564 -0
- data/lib/google/cloud/bigquery/schema/field.rb +668 -0
- data/lib/google/cloud/bigquery/service.rb +589 -0
- data/lib/google/cloud/bigquery/standard_sql.rb +495 -0
- data/lib/google/cloud/bigquery/table.rb +3340 -0
- data/lib/google/cloud/bigquery/table/async_inserter.rb +520 -0
- data/lib/google/cloud/bigquery/table/list.rb +172 -0
- data/lib/google/cloud/bigquery/time.rb +65 -0
- data/lib/google/cloud/bigquery/version.rb +22 -0
- metadata +297 -0
@@ -0,0 +1,657 @@
|
|
1
|
+
# Copyright 2015 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/errors"
|
17
|
+
require "google/cloud/bigquery/service"
|
18
|
+
require "google/cloud/bigquery/job/list"
|
19
|
+
require "google/cloud/bigquery/convert"
|
20
|
+
require "json"
|
21
|
+
|
22
|
+
module Google
|
23
|
+
module Cloud
|
24
|
+
module Bigquery
|
25
|
+
##
|
26
|
+
# # Job
|
27
|
+
#
|
28
|
+
# Represents a generic Job that may be performed on a {Table}.
|
29
|
+
#
|
30
|
+
# The subclasses of Job represent the specific BigQuery job types:
|
31
|
+
# {CopyJob}, {ExtractJob}, {LoadJob}, and {QueryJob}.
|
32
|
+
#
|
33
|
+
# A job instance is created when you call {Project#query_job},
|
34
|
+
# {Dataset#query_job}, {Table#copy_job}, {Table#extract_job},
|
35
|
+
# {Table#load_job}.
|
36
|
+
#
|
37
|
+
# @see https://cloud.google.com/bigquery/docs/managing-jobs Running and
|
38
|
+
# Managing Jobs
|
39
|
+
# @see https://cloud.google.com/bigquery/docs/reference/v2/jobs Jobs API
|
40
|
+
# reference
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# require "google/cloud/bigquery"
|
44
|
+
#
|
45
|
+
# bigquery = Google::Cloud::Bigquery.new
|
46
|
+
#
|
47
|
+
# job = bigquery.query_job "SELECT COUNT(word) as count FROM " \
|
48
|
+
# "`bigquery-public-data.samples.shakespeare`"
|
49
|
+
#
|
50
|
+
# job.wait_until_done!
|
51
|
+
#
|
52
|
+
# if job.failed?
|
53
|
+
# puts job.error
|
54
|
+
# else
|
55
|
+
# puts job.data.first
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
class Job
|
59
|
+
##
|
60
|
+
# @private The Service object.
|
61
|
+
attr_accessor :service
|
62
|
+
|
63
|
+
##
|
64
|
+
# @private The Google API Client object.
|
65
|
+
attr_accessor :gapi
|
66
|
+
|
67
|
+
##
|
68
|
+
# @private Create an empty Job object.
|
69
|
+
def initialize
|
70
|
+
@service = nil
|
71
|
+
@gapi = {}
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# The ID of the job.
|
76
|
+
#
|
77
|
+
# @return [String] The ID must contain only letters (a-z, A-Z), numbers
|
78
|
+
# (0-9), underscores (_), or dashes (-). The maximum length is 1,024
|
79
|
+
# characters.
|
80
|
+
#
|
81
|
+
def job_id
|
82
|
+
@gapi.job_reference.job_id
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# The ID of the project containing the job.
|
87
|
+
#
|
88
|
+
# @return [String] The project ID.
|
89
|
+
#
|
90
|
+
def project_id
|
91
|
+
@gapi.job_reference.project_id
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# The geographic location where the job runs.
|
96
|
+
#
|
97
|
+
# @return [String] A geographic location, such as "US", "EU" or
|
98
|
+
# "asia-northeast1".
|
99
|
+
#
|
100
|
+
# @!group Attributes
|
101
|
+
def location
|
102
|
+
@gapi.job_reference.location
|
103
|
+
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# The email address of the user who ran the job.
|
107
|
+
#
|
108
|
+
# @return [String] The email address.
|
109
|
+
#
|
110
|
+
def user_email
|
111
|
+
@gapi.user_email
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# The current state of the job. A `DONE` state does not mean that the
|
116
|
+
# job completed successfully. Use {#failed?} to discover if an error
|
117
|
+
# occurred or if the job was successful.
|
118
|
+
#
|
119
|
+
# @return [String] The state code. The possible values are `PENDING`,
|
120
|
+
# `RUNNING`, and `DONE`.
|
121
|
+
#
|
122
|
+
def state
|
123
|
+
return nil if @gapi.status.nil?
|
124
|
+
@gapi.status.state
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# Checks if the job's state is `RUNNING`.
|
129
|
+
#
|
130
|
+
# @return [Boolean] `true` when `RUNNING`, `false` otherwise.
|
131
|
+
#
|
132
|
+
def running?
|
133
|
+
return false if state.nil?
|
134
|
+
"running".casecmp(state).zero?
|
135
|
+
end
|
136
|
+
|
137
|
+
##
|
138
|
+
# Checks if the job's state is `PENDING`.
|
139
|
+
#
|
140
|
+
# @return [Boolean] `true` when `PENDING`, `false` otherwise.
|
141
|
+
#
|
142
|
+
def pending?
|
143
|
+
return false if state.nil?
|
144
|
+
"pending".casecmp(state).zero?
|
145
|
+
end
|
146
|
+
|
147
|
+
##
|
148
|
+
# Checks if the job's state is `DONE`. When `true`, the job has stopped
|
149
|
+
# running. However, a `DONE` state does not mean that the job completed
|
150
|
+
# successfully. Use {#failed?} to detect if an error occurred or if the
|
151
|
+
# job was successful.
|
152
|
+
#
|
153
|
+
# @return [Boolean] `true` when `DONE`, `false` otherwise.
|
154
|
+
#
|
155
|
+
def done?
|
156
|
+
return false if state.nil?
|
157
|
+
"done".casecmp(state).zero?
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Checks if an error is present. Use {#error} to access the error
|
162
|
+
# object.
|
163
|
+
#
|
164
|
+
# @return [Boolean] `true` when there is an error, `false` otherwise.
|
165
|
+
#
|
166
|
+
def failed?
|
167
|
+
!error.nil?
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# The time when the job was created.
|
172
|
+
#
|
173
|
+
# @return [Time, nil] The creation time from the job statistics.
|
174
|
+
#
|
175
|
+
def created_at
|
176
|
+
Convert.millis_to_time @gapi.statistics.creation_time
|
177
|
+
end
|
178
|
+
|
179
|
+
##
|
180
|
+
# The time when the job was started.
|
181
|
+
# This field is present after the job's state changes from `PENDING`
|
182
|
+
# to either `RUNNING` or `DONE`.
|
183
|
+
#
|
184
|
+
# @return [Time, nil] The start time from the job statistics.
|
185
|
+
#
|
186
|
+
def started_at
|
187
|
+
Convert.millis_to_time @gapi.statistics.start_time
|
188
|
+
end
|
189
|
+
|
190
|
+
##
|
191
|
+
# The time when the job ended.
|
192
|
+
# This field is present when the job's state is `DONE`.
|
193
|
+
#
|
194
|
+
# @return [Time, nil] The end time from the job statistics.
|
195
|
+
#
|
196
|
+
def ended_at
|
197
|
+
Convert.millis_to_time @gapi.statistics.end_time
|
198
|
+
end
|
199
|
+
|
200
|
+
##
|
201
|
+
# The number of child jobs executed.
|
202
|
+
#
|
203
|
+
# @return [Integer] The number of child jobs executed.
|
204
|
+
#
|
205
|
+
def num_child_jobs
|
206
|
+
@gapi.statistics.num_child_jobs || 0
|
207
|
+
end
|
208
|
+
|
209
|
+
##
|
210
|
+
# If this is a child job, the id of the parent.
|
211
|
+
#
|
212
|
+
# @return [String, nil] The ID of the parent job, or `nil` if not a child job.
|
213
|
+
#
|
214
|
+
def parent_job_id
|
215
|
+
@gapi.statistics.parent_job_id
|
216
|
+
end
|
217
|
+
|
218
|
+
##
|
219
|
+
# The statistics including stack frames for a child job of a script.
|
220
|
+
#
|
221
|
+
# @return [Google::Cloud::Bigquery::Job::ScriptStatistics, nil] The script statistics, or `nil` if the job is
|
222
|
+
# not a child job.
|
223
|
+
#
|
224
|
+
# @example
|
225
|
+
# require "google/cloud/bigquery"
|
226
|
+
#
|
227
|
+
# bigquery = Google::Cloud::Bigquery.new
|
228
|
+
#
|
229
|
+
# multi_statement_sql = <<~SQL
|
230
|
+
# -- Declare a variable to hold names as an array.
|
231
|
+
# DECLARE top_names ARRAY<STRING>;
|
232
|
+
# -- Build an array of the top 100 names from the year 2017.
|
233
|
+
# SET top_names = (
|
234
|
+
# SELECT ARRAY_AGG(name ORDER BY number DESC LIMIT 100)
|
235
|
+
# FROM `bigquery-public-data.usa_names.usa_1910_current`
|
236
|
+
# WHERE year = 2017
|
237
|
+
# );
|
238
|
+
# -- Which names appear as words in Shakespeare's plays?
|
239
|
+
# SELECT
|
240
|
+
# name AS shakespeare_name
|
241
|
+
# FROM UNNEST(top_names) AS name
|
242
|
+
# WHERE name IN (
|
243
|
+
# SELECT word
|
244
|
+
# FROM `bigquery-public-data.samples.shakespeare`
|
245
|
+
# );
|
246
|
+
# SQL
|
247
|
+
#
|
248
|
+
# job = bigquery.query_job multi_statement_sql
|
249
|
+
#
|
250
|
+
# job.wait_until_done!
|
251
|
+
#
|
252
|
+
# child_jobs = bigquery.jobs parent_job: job
|
253
|
+
#
|
254
|
+
# child_jobs.each do |child_job|
|
255
|
+
# script_statistics = child_job.script_statistics
|
256
|
+
# puts script_statistics.evaluation_kind
|
257
|
+
# script_statistics.stack_frames.each do |stack_frame|
|
258
|
+
# puts stack_frame.text
|
259
|
+
# end
|
260
|
+
# end
|
261
|
+
#
|
262
|
+
def script_statistics
|
263
|
+
ScriptStatistics.from_gapi @gapi.statistics.script_statistics if @gapi.statistics.script_statistics
|
264
|
+
end
|
265
|
+
|
266
|
+
##
|
267
|
+
# The configuration for the job. Returns a hash.
|
268
|
+
#
|
269
|
+
# @see https://cloud.google.com/bigquery/docs/reference/v2/jobs Jobs API
|
270
|
+
# reference
|
271
|
+
def configuration
|
272
|
+
JSON.parse @gapi.configuration.to_json
|
273
|
+
end
|
274
|
+
alias config configuration
|
275
|
+
|
276
|
+
##
|
277
|
+
# The statistics for the job. Returns a hash.
|
278
|
+
#
|
279
|
+
# @see https://cloud.google.com/bigquery/docs/reference/v2/jobs Jobs API
|
280
|
+
# reference
|
281
|
+
#
|
282
|
+
# @return [Hash] The job statistics.
|
283
|
+
#
|
284
|
+
def statistics
|
285
|
+
JSON.parse @gapi.statistics.to_json
|
286
|
+
end
|
287
|
+
alias stats statistics
|
288
|
+
|
289
|
+
##
|
290
|
+
# The job's status. Returns a hash. The values contained in the hash are
|
291
|
+
# also exposed by {#state}, {#error}, and {#errors}.
|
292
|
+
#
|
293
|
+
# @return [Hash] The job status.
|
294
|
+
#
|
295
|
+
def status
|
296
|
+
JSON.parse @gapi.status.to_json
|
297
|
+
end
|
298
|
+
|
299
|
+
##
|
300
|
+
# The last error for the job, if any errors have occurred. Returns a
|
301
|
+
# hash.
|
302
|
+
#
|
303
|
+
# @see https://cloud.google.com/bigquery/docs/reference/v2/jobs Jobs API
|
304
|
+
# reference
|
305
|
+
#
|
306
|
+
# @return [Hash, nil] Returns a hash containing `reason` and `message`
|
307
|
+
# keys:
|
308
|
+
#
|
309
|
+
# {
|
310
|
+
# "reason"=>"notFound",
|
311
|
+
# "message"=>"Not found: Table bigquery-public-data:samples.BAD_ID"
|
312
|
+
# }
|
313
|
+
#
|
314
|
+
def error
|
315
|
+
status["errorResult"]
|
316
|
+
end
|
317
|
+
|
318
|
+
##
|
319
|
+
# The errors for the job, if any errors have occurred. Returns an array
|
320
|
+
# of hash objects. See {#error}.
|
321
|
+
#
|
322
|
+
# @return [Array<Hash>, nil] Returns an array of hashes containing
|
323
|
+
# `reason` and `message` keys:
|
324
|
+
#
|
325
|
+
# {
|
326
|
+
# "reason"=>"notFound",
|
327
|
+
# "message"=>"Not found: Table bigquery-public-data:samples.BAD_ID"
|
328
|
+
# }
|
329
|
+
#
|
330
|
+
def errors
|
331
|
+
Array status["errors"]
|
332
|
+
end
|
333
|
+
|
334
|
+
##
|
335
|
+
# A hash of user-provided labels associated with this job. Labels can be
|
336
|
+
# provided when the job is created, and used to organize and group jobs.
|
337
|
+
#
|
338
|
+
# The returned hash is frozen and changes are not allowed. Use
|
339
|
+
# {CopyJob::Updater#labels=} or {ExtractJob::Updater#labels=} or
|
340
|
+
# {LoadJob::Updater#labels=} or {QueryJob::Updater#labels=} to replace
|
341
|
+
# the entire hash.
|
342
|
+
#
|
343
|
+
# @return [Hash] The job labels.
|
344
|
+
#
|
345
|
+
# @!group Attributes
|
346
|
+
#
|
347
|
+
def labels
|
348
|
+
m = @gapi.configuration.labels
|
349
|
+
m = m.to_h if m.respond_to? :to_h
|
350
|
+
m.dup.freeze
|
351
|
+
end
|
352
|
+
|
353
|
+
##
|
354
|
+
# Cancels the job.
|
355
|
+
#
|
356
|
+
# @example
|
357
|
+
# require "google/cloud/bigquery"
|
358
|
+
#
|
359
|
+
# bigquery = Google::Cloud::Bigquery.new
|
360
|
+
#
|
361
|
+
# query = "SELECT COUNT(word) as count FROM " \
|
362
|
+
# "`bigquery-public-data.samples.shakespeare`"
|
363
|
+
#
|
364
|
+
# job = bigquery.query_job query
|
365
|
+
#
|
366
|
+
# job.cancel
|
367
|
+
#
|
368
|
+
def cancel
|
369
|
+
ensure_service!
|
370
|
+
resp = service.cancel_job job_id, location: location
|
371
|
+
@gapi = resp.job
|
372
|
+
true
|
373
|
+
end
|
374
|
+
|
375
|
+
##
|
376
|
+
# Created a new job with the current configuration.
|
377
|
+
#
|
378
|
+
# @example
|
379
|
+
# require "google/cloud/bigquery"
|
380
|
+
#
|
381
|
+
# bigquery = Google::Cloud::Bigquery.new
|
382
|
+
#
|
383
|
+
# query = "SELECT COUNT(word) as count FROM " \
|
384
|
+
# "`bigquery-public-data.samples.shakespeare`"
|
385
|
+
#
|
386
|
+
# job = bigquery.query_job query
|
387
|
+
#
|
388
|
+
# job.wait_until_done!
|
389
|
+
# job.rerun!
|
390
|
+
#
|
391
|
+
def rerun!
|
392
|
+
ensure_service!
|
393
|
+
gapi = service.insert_job @gapi.configuration, location: location
|
394
|
+
Job.from_gapi gapi, service
|
395
|
+
end
|
396
|
+
|
397
|
+
##
|
398
|
+
# Reloads the job with current data from the BigQuery service.
|
399
|
+
#
|
400
|
+
# @example
|
401
|
+
# require "google/cloud/bigquery"
|
402
|
+
#
|
403
|
+
# bigquery = Google::Cloud::Bigquery.new
|
404
|
+
#
|
405
|
+
# query = "SELECT COUNT(word) as count FROM " \
|
406
|
+
# "`bigquery-public-data.samples.shakespeare`"
|
407
|
+
#
|
408
|
+
# job = bigquery.query_job query
|
409
|
+
#
|
410
|
+
# job.done?
|
411
|
+
# job.reload!
|
412
|
+
# job.done? #=> true
|
413
|
+
#
|
414
|
+
def reload!
|
415
|
+
ensure_service!
|
416
|
+
gapi = service.get_job job_id, location: location
|
417
|
+
@gapi = gapi
|
418
|
+
end
|
419
|
+
alias refresh! reload!
|
420
|
+
|
421
|
+
##
|
422
|
+
# Refreshes the job until the job is `DONE`. The delay between refreshes
|
423
|
+
# starts at 5 seconds and increases exponentially to a maximum of 60
|
424
|
+
# seconds.
|
425
|
+
#
|
426
|
+
# @example
|
427
|
+
# require "google/cloud/bigquery"
|
428
|
+
#
|
429
|
+
# bigquery = Google::Cloud::Bigquery.new
|
430
|
+
# dataset = bigquery.dataset "my_dataset"
|
431
|
+
# table = dataset.table "my_table"
|
432
|
+
#
|
433
|
+
# extract_job = table.extract_job "gs://my-bucket/file-name.json",
|
434
|
+
# format: "json"
|
435
|
+
# extract_job.wait_until_done!
|
436
|
+
# extract_job.done? #=> true
|
437
|
+
#
|
438
|
+
def wait_until_done!
|
439
|
+
backoff = lambda do |retries|
|
440
|
+
delay = [retries**2 + 5, 60].min # Maximum delay is 60
|
441
|
+
sleep delay
|
442
|
+
end
|
443
|
+
retries = 0
|
444
|
+
until done?
|
445
|
+
backoff.call retries
|
446
|
+
retries += 1
|
447
|
+
reload!
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
##
|
452
|
+
# @private New Job from a Google API Client object.
|
453
|
+
def self.from_gapi gapi, conn
|
454
|
+
klass = klass_for gapi
|
455
|
+
klass.new.tap do |f|
|
456
|
+
f.gapi = gapi
|
457
|
+
f.service = conn
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
##
|
462
|
+
# @private New Google::Apis::Error with job failure details
|
463
|
+
def gapi_error
|
464
|
+
return nil unless failed?
|
465
|
+
|
466
|
+
error_status_code = status_code_for_reason error["reason"]
|
467
|
+
error_body = error
|
468
|
+
error_body["errors"] = errors
|
469
|
+
|
470
|
+
Google::Apis::Error.new error["message"],
|
471
|
+
status_code: error_status_code,
|
472
|
+
body: error_body
|
473
|
+
end
|
474
|
+
|
475
|
+
##
|
476
|
+
# @private
|
477
|
+
# Get the subclass for a job type
|
478
|
+
def self.klass_for gapi
|
479
|
+
if gapi.configuration.copy
|
480
|
+
CopyJob
|
481
|
+
elsif gapi.configuration.extract
|
482
|
+
ExtractJob
|
483
|
+
elsif gapi.configuration.load
|
484
|
+
LoadJob
|
485
|
+
elsif gapi.configuration.query
|
486
|
+
QueryJob
|
487
|
+
else
|
488
|
+
Job
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
##
|
493
|
+
# Represents statistics for a child job of a script.
|
494
|
+
#
|
495
|
+
# @attr_reader [String] evaluation_kind Indicates the type of child job. Possible values include `STATEMENT` and
|
496
|
+
# `EXPRESSION`.
|
497
|
+
# @attr_reader [Array<Google::Cloud::Bigquery::Job::ScriptStackFrame>] stack_frames Stack trace where the
|
498
|
+
# current evaluation happened. Shows line/column/procedure name of each frame on the stack at the point where
|
499
|
+
# the current evaluation happened. The leaf frame is first, the primary script is last.
|
500
|
+
#
|
501
|
+
# @example
|
502
|
+
# require "google/cloud/bigquery"
|
503
|
+
#
|
504
|
+
# bigquery = Google::Cloud::Bigquery.new
|
505
|
+
#
|
506
|
+
# multi_statement_sql = <<~SQL
|
507
|
+
# -- Declare a variable to hold names as an array.
|
508
|
+
# DECLARE top_names ARRAY<STRING>;
|
509
|
+
# -- Build an array of the top 100 names from the year 2017.
|
510
|
+
# SET top_names = (
|
511
|
+
# SELECT ARRAY_AGG(name ORDER BY number DESC LIMIT 100)
|
512
|
+
# FROM `bigquery-public-data.usa_names.usa_1910_current`
|
513
|
+
# WHERE year = 2017
|
514
|
+
# );
|
515
|
+
# -- Which names appear as words in Shakespeare's plays?
|
516
|
+
# SELECT
|
517
|
+
# name AS shakespeare_name
|
518
|
+
# FROM UNNEST(top_names) AS name
|
519
|
+
# WHERE name IN (
|
520
|
+
# SELECT word
|
521
|
+
# FROM `bigquery-public-data.samples.shakespeare`
|
522
|
+
# );
|
523
|
+
# SQL
|
524
|
+
#
|
525
|
+
# job = bigquery.query_job multi_statement_sql
|
526
|
+
#
|
527
|
+
# job.wait_until_done!
|
528
|
+
#
|
529
|
+
# child_jobs = bigquery.jobs parent_job: job
|
530
|
+
#
|
531
|
+
# child_jobs.each do |child_job|
|
532
|
+
# script_statistics = child_job.script_statistics
|
533
|
+
# puts script_statistics.evaluation_kind
|
534
|
+
# script_statistics.stack_frames.each do |stack_frame|
|
535
|
+
# puts stack_frame.text
|
536
|
+
# end
|
537
|
+
# end
|
538
|
+
#
|
539
|
+
class ScriptStatistics
|
540
|
+
attr_reader :evaluation_kind, :stack_frames
|
541
|
+
|
542
|
+
##
|
543
|
+
# @private Creates a new ScriptStatistics instance.
|
544
|
+
def initialize evaluation_kind, stack_frames
|
545
|
+
@evaluation_kind = evaluation_kind
|
546
|
+
@stack_frames = stack_frames
|
547
|
+
end
|
548
|
+
|
549
|
+
##
|
550
|
+
# @private New ScriptStatistics from a statistics.script_statistics object.
|
551
|
+
def self.from_gapi gapi
|
552
|
+
frames = Array(gapi.stack_frames).map { |g| ScriptStackFrame.from_gapi g }
|
553
|
+
new gapi.evaluation_kind, frames
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
##
|
558
|
+
# Represents a stack frame showing the line/column/procedure name where the current evaluation happened.
|
559
|
+
#
|
560
|
+
# @attr_reader [Integer] start_line One-based start line.
|
561
|
+
# @attr_reader [Integer] start_column One-based start column.
|
562
|
+
# @attr_reader [Integer] end_line One-based end line.
|
563
|
+
# @attr_reader [Integer] end_column One-based end column.
|
564
|
+
# @attr_reader [String] text Text of the current statement/expression.
|
565
|
+
#
|
566
|
+
# @example
|
567
|
+
# require "google/cloud/bigquery"
|
568
|
+
#
|
569
|
+
# bigquery = Google::Cloud::Bigquery.new
|
570
|
+
#
|
571
|
+
# multi_statement_sql = <<~SQL
|
572
|
+
# -- Declare a variable to hold names as an array.
|
573
|
+
# DECLARE top_names ARRAY<STRING>;
|
574
|
+
# -- Build an array of the top 100 names from the year 2017.
|
575
|
+
# SET top_names = (
|
576
|
+
# SELECT ARRAY_AGG(name ORDER BY number DESC LIMIT 100)
|
577
|
+
# FROM `bigquery-public-data.usa_names.usa_1910_current`
|
578
|
+
# WHERE year = 2017
|
579
|
+
# );
|
580
|
+
# -- Which names appear as words in Shakespeare's plays?
|
581
|
+
# SELECT
|
582
|
+
# name AS shakespeare_name
|
583
|
+
# FROM UNNEST(top_names) AS name
|
584
|
+
# WHERE name IN (
|
585
|
+
# SELECT word
|
586
|
+
# FROM `bigquery-public-data.samples.shakespeare`
|
587
|
+
# );
|
588
|
+
# SQL
|
589
|
+
#
|
590
|
+
# job = bigquery.query_job multi_statement_sql
|
591
|
+
#
|
592
|
+
# job.wait_until_done!
|
593
|
+
#
|
594
|
+
# child_jobs = bigquery.jobs parent_job: job
|
595
|
+
#
|
596
|
+
# child_jobs.each do |child_job|
|
597
|
+
# script_statistics = child_job.script_statistics
|
598
|
+
# puts script_statistics.evaluation_kind
|
599
|
+
# script_statistics.stack_frames.each do |stack_frame|
|
600
|
+
# puts stack_frame.text
|
601
|
+
# end
|
602
|
+
# end
|
603
|
+
#
|
604
|
+
class ScriptStackFrame
|
605
|
+
attr_reader :start_line, :start_column, :end_line, :end_column, :text
|
606
|
+
|
607
|
+
##
|
608
|
+
# @private Creates a new ScriptStackFrame instance.
|
609
|
+
def initialize start_line, start_column, end_line, end_column, text
|
610
|
+
@start_line = start_line
|
611
|
+
@start_column = start_column
|
612
|
+
@end_line = end_line
|
613
|
+
@end_column = end_column
|
614
|
+
@text = text
|
615
|
+
end
|
616
|
+
|
617
|
+
##
|
618
|
+
# @private New ScriptStackFrame from a statistics.script_statistics[].stack_frames element.
|
619
|
+
def self.from_gapi gapi
|
620
|
+
new gapi.start_line, gapi.start_column, gapi.end_line, gapi.end_column, gapi.text
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
protected
|
625
|
+
|
626
|
+
##
|
627
|
+
# Raise an error unless an active connection is available.
|
628
|
+
def ensure_service!
|
629
|
+
raise "Must have active connection" unless service
|
630
|
+
end
|
631
|
+
|
632
|
+
def retrieve_table project_id, dataset_id, table_id
|
633
|
+
ensure_service!
|
634
|
+
gapi = service.get_project_table project_id, dataset_id, table_id
|
635
|
+
Table.from_gapi gapi, service
|
636
|
+
rescue Google::Cloud::NotFoundError
|
637
|
+
nil
|
638
|
+
end
|
639
|
+
|
640
|
+
def status_code_for_reason reason
|
641
|
+
codes = { "accessDenied" => 403, "backendError" => 500, "billingNotEnabled" => 403,
|
642
|
+
"billingTierLimitExceeded" => 400, "blocked" => 403, "duplicate" => 409, "internalError" => 500,
|
643
|
+
"invalid" => 400, "invalidQuery" => 400, "notFound" => 404, "notImplemented" => 501,
|
644
|
+
"quotaExceeded" => 403, "rateLimitExceeded" => 403, "resourceInUse" => 400,
|
645
|
+
"resourcesExceeded" => 400, "responseTooLarge" => 403, "tableUnavailable" => 400 }
|
646
|
+
codes[reason] || 0
|
647
|
+
end
|
648
|
+
end
|
649
|
+
end
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
# We need Job to be defined before loading these.
|
654
|
+
require "google/cloud/bigquery/copy_job"
|
655
|
+
require "google/cloud/bigquery/extract_job"
|
656
|
+
require "google/cloud/bigquery/load_job"
|
657
|
+
require "google/cloud/bigquery/query_job"
|