google-cloud-bigquery 0.28.0 → 0.29.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/google-cloud-bigquery.rb +2 -2
- data/lib/google/cloud/bigquery.rb +10 -12
- data/lib/google/cloud/bigquery/copy_job.rb +42 -6
- data/lib/google/cloud/bigquery/data.rb +129 -23
- data/lib/google/cloud/bigquery/dataset.rb +708 -66
- data/lib/google/cloud/bigquery/dataset/access.rb +533 -27
- data/lib/google/cloud/bigquery/dataset/list.rb +5 -3
- data/lib/google/cloud/bigquery/external.rb +2353 -0
- data/lib/google/cloud/bigquery/extract_job.rb +52 -11
- data/lib/google/cloud/bigquery/insert_response.rb +90 -2
- data/lib/google/cloud/bigquery/job.rb +160 -21
- data/lib/google/cloud/bigquery/load_job.rb +128 -11
- data/lib/google/cloud/bigquery/project.rb +187 -44
- data/lib/google/cloud/bigquery/query_job.rb +323 -13
- data/lib/google/cloud/bigquery/schema.rb +57 -1
- data/lib/google/cloud/bigquery/schema/field.rb +118 -17
- data/lib/google/cloud/bigquery/service.rb +196 -43
- data/lib/google/cloud/bigquery/table.rb +739 -49
- data/lib/google/cloud/bigquery/table/async_inserter.rb +280 -0
- data/lib/google/cloud/bigquery/version.rb +1 -1
- data/lib/google/cloud/bigquery/view.rb +306 -69
- metadata +18 -3
- data/lib/google/cloud/bigquery/query_data.rb +0 -234
@@ -21,13 +21,25 @@ module Google
|
|
21
21
|
#
|
22
22
|
# A {Job} subclass representing an export operation that may be performed
|
23
23
|
# on a {Table}. A ExtractJob instance is created when you call
|
24
|
-
# {Table#
|
24
|
+
# {Table#extract_job}.
|
25
25
|
#
|
26
|
-
# @see https://cloud.google.com/bigquery/exporting-data
|
26
|
+
# @see https://cloud.google.com/bigquery/docs/exporting-data
|
27
27
|
# Exporting Data From BigQuery
|
28
28
|
# @see https://cloud.google.com/bigquery/docs/reference/v2/jobs Jobs API
|
29
29
|
# reference
|
30
30
|
#
|
31
|
+
# @example
|
32
|
+
# require "google/cloud/bigquery"
|
33
|
+
#
|
34
|
+
# bigquery = Google::Cloud::Bigquery.new
|
35
|
+
# dataset = bigquery.dataset "my_dataset"
|
36
|
+
# table = dataset.table "my_table"
|
37
|
+
#
|
38
|
+
# extract_job = table.extract_job "gs://my-bucket/file-name.json",
|
39
|
+
# format: "json"
|
40
|
+
# extract_job.wait_until_done!
|
41
|
+
# extract_job.done? #=> true
|
42
|
+
#
|
31
43
|
class ExtractJob < Job
|
32
44
|
##
|
33
45
|
# The URI or URIs representing the Google Cloud Storage files to which
|
@@ -38,7 +50,10 @@ module Google
|
|
38
50
|
|
39
51
|
##
|
40
52
|
# The table from which the data is exported. This is the table upon
|
41
|
-
# which {Table#
|
53
|
+
# which {Table#extract_job} was called.
|
54
|
+
#
|
55
|
+
# @return [Table] A table instance.
|
56
|
+
#
|
42
57
|
def source
|
43
58
|
table = @gapi.configuration.extract.source_table
|
44
59
|
return nil unless table
|
@@ -50,6 +65,9 @@ module Google
|
|
50
65
|
##
|
51
66
|
# Checks if the export operation compresses the data using gzip. The
|
52
67
|
# default is `false`.
|
68
|
+
#
|
69
|
+
# @return [Boolean] `true` when `GZIP`, `false` otherwise.
|
70
|
+
#
|
53
71
|
def compression?
|
54
72
|
val = @gapi.configuration.extract.compression
|
55
73
|
val == "GZIP"
|
@@ -58,6 +76,10 @@ module Google
|
|
58
76
|
##
|
59
77
|
# Checks if the destination format for the data is [newline-delimited
|
60
78
|
# JSON](http://jsonlines.org/). The default is `false`.
|
79
|
+
#
|
80
|
+
# @return [Boolean] `true` when `NEWLINE_DELIMITED_JSON`, `false`
|
81
|
+
# otherwise.
|
82
|
+
#
|
61
83
|
def json?
|
62
84
|
val = @gapi.configuration.extract.destination_format
|
63
85
|
val == "NEWLINE_DELIMITED_JSON"
|
@@ -67,6 +89,9 @@ module Google
|
|
67
89
|
# Checks if the destination format for the data is CSV. Tables with
|
68
90
|
# nested or repeated fields cannot be exported as CSV. The default is
|
69
91
|
# `true`.
|
92
|
+
#
|
93
|
+
# @return [Boolean] `true` when `CSV`, `false` otherwise.
|
94
|
+
#
|
70
95
|
def csv?
|
71
96
|
val = @gapi.configuration.extract.destination_format
|
72
97
|
return true if val.nil?
|
@@ -76,14 +101,20 @@ module Google
|
|
76
101
|
##
|
77
102
|
# Checks if the destination format for the data is
|
78
103
|
# [Avro](http://avro.apache.org/). The default is `false`.
|
104
|
+
#
|
105
|
+
# @return [Boolean] `true` when `AVRO`, `false` otherwise.
|
106
|
+
#
|
79
107
|
def avro?
|
80
108
|
val = @gapi.configuration.extract.destination_format
|
81
109
|
val == "AVRO"
|
82
110
|
end
|
83
111
|
|
84
112
|
##
|
85
|
-
# The symbol the operation uses to delimit fields in the
|
86
|
-
# The default is a comma (,).
|
113
|
+
# The character or symbol the operation uses to delimit fields in the
|
114
|
+
# exported data. The default is a comma (,).
|
115
|
+
#
|
116
|
+
# @return [String] A string containing the character, such as `","`.
|
117
|
+
#
|
87
118
|
def delimiter
|
88
119
|
val = @gapi.configuration.extract.field_delimiter
|
89
120
|
val = "," if val.nil?
|
@@ -93,6 +124,10 @@ module Google
|
|
93
124
|
##
|
94
125
|
# Checks if the exported data contains a header row. The default is
|
95
126
|
# `true`.
|
127
|
+
#
|
128
|
+
# @return [Boolean] `true` when the print header configuration is
|
129
|
+
# present or `nil`, `false` otherwise.
|
130
|
+
#
|
96
131
|
def print_header?
|
97
132
|
val = @gapi.configuration.extract.print_header
|
98
133
|
val = true if val.nil?
|
@@ -100,17 +135,23 @@ module Google
|
|
100
135
|
end
|
101
136
|
|
102
137
|
##
|
103
|
-
# The
|
104
|
-
# {#destinations}.
|
105
|
-
#
|
138
|
+
# The number of files per destination URI or URI pattern specified in
|
139
|
+
# {#destinations}.
|
140
|
+
#
|
141
|
+
# @return [Array<Integer>] An array of values in the same order as the
|
142
|
+
# URI patterns.
|
143
|
+
#
|
106
144
|
def destinations_file_counts
|
107
145
|
Array @gapi.statistics.extract.destination_uri_file_counts
|
108
146
|
end
|
109
147
|
|
110
148
|
##
|
111
|
-
#
|
112
|
-
# {#destinations}
|
113
|
-
#
|
149
|
+
# A hash containing the URI or URI pattern specified in
|
150
|
+
# {#destinations} mapped to the counts of files per destination.
|
151
|
+
#
|
152
|
+
# @return [Hash<String, Integer>] A Hash with the URI patterns as keys
|
153
|
+
# and the counts as values.
|
154
|
+
#
|
114
155
|
def destinations_counts
|
115
156
|
Hash[destinations.zip destinations_file_counts]
|
116
157
|
end
|
@@ -20,6 +20,28 @@ module Google
|
|
20
20
|
module Bigquery
|
21
21
|
##
|
22
22
|
# InsertResponse
|
23
|
+
#
|
24
|
+
# Represents the response from BigQuery when data is inserted into a table
|
25
|
+
# for near-immediate querying, without the need to complete a load
|
26
|
+
# operation before the data can appear in query results. See
|
27
|
+
# {Dataset#insert} and {Table#insert}.
|
28
|
+
#
|
29
|
+
# @see https://cloud.google.com/bigquery/streaming-data-into-bigquery
|
30
|
+
# Streaming Data Into BigQuery
|
31
|
+
#
|
32
|
+
# @example
|
33
|
+
# require "google/cloud/bigquery"
|
34
|
+
#
|
35
|
+
# bigquery = Google::Cloud::Bigquery.new
|
36
|
+
# dataset = bigquery.dataset "my_dataset"
|
37
|
+
#
|
38
|
+
# rows = [
|
39
|
+
# { "first_name" => "Alice", "age" => 21 },
|
40
|
+
# { "first_name" => "Bob", "age" => 22 }
|
41
|
+
# ]
|
42
|
+
#
|
43
|
+
# insert_response = dataset.insert "my_table", rows
|
44
|
+
#
|
23
45
|
class InsertResponse
|
24
46
|
# @private
|
25
47
|
def initialize rows, gapi
|
@@ -27,18 +49,43 @@ module Google
|
|
27
49
|
@gapi = gapi
|
28
50
|
end
|
29
51
|
|
52
|
+
##
|
53
|
+
# Checks if the error count is zero, meaning that all of the rows were
|
54
|
+
# inserted. Use {#insert_errors} to access the errors.
|
55
|
+
#
|
56
|
+
# @return [Boolean] `true` when the error count is zero, `false`
|
57
|
+
# otherwise.
|
58
|
+
#
|
30
59
|
def success?
|
31
60
|
error_count.zero?
|
32
61
|
end
|
33
62
|
|
63
|
+
|
64
|
+
##
|
65
|
+
# The count of rows in the response, minus the count of errors for rows
|
66
|
+
# that were not inserted.
|
67
|
+
#
|
68
|
+
# @return [Integer] The number of rows inserted.
|
69
|
+
#
|
34
70
|
def insert_count
|
35
71
|
@rows.count - error_count
|
36
72
|
end
|
37
73
|
|
74
|
+
|
75
|
+
##
|
76
|
+
# The count of errors for rows that were not inserted.
|
77
|
+
#
|
78
|
+
# @return [Integer] The number of errors.
|
79
|
+
#
|
38
80
|
def error_count
|
39
81
|
Array(@gapi.insert_errors).count
|
40
82
|
end
|
41
83
|
|
84
|
+
##
|
85
|
+
# The error objects for rows that were not inserted.
|
86
|
+
#
|
87
|
+
# @return [Array<InsertError>] An array containing error objects.
|
88
|
+
#
|
42
89
|
def insert_errors
|
43
90
|
Array(@gapi.insert_errors).map do |ie|
|
44
91
|
row = @rows[ie.index]
|
@@ -47,23 +94,54 @@ module Google
|
|
47
94
|
end
|
48
95
|
end
|
49
96
|
|
97
|
+
##
|
98
|
+
# The rows that were not inserted.
|
99
|
+
#
|
100
|
+
# @return [Array<Hash>] An array of hash objects containing the row
|
101
|
+
# data.
|
102
|
+
#
|
50
103
|
def error_rows
|
51
104
|
Array(@gapi.insert_errors).map do |ie|
|
52
105
|
@rows[ie.index]
|
53
106
|
end
|
54
107
|
end
|
55
108
|
|
109
|
+
##
|
110
|
+
# Returns the error object for a row that was not inserted.
|
111
|
+
#
|
112
|
+
# @param [Hash] row A hash containing the data for a row.
|
113
|
+
#
|
114
|
+
# @return [InsertError, nil] An error object, or `nil` if no error is
|
115
|
+
# found in the response for the row.
|
116
|
+
#
|
56
117
|
def insert_error_for row
|
57
|
-
|
58
|
-
insert_errors.detect { |e| e.row == json_row }
|
118
|
+
insert_errors.detect { |e| e.row == row }
|
59
119
|
end
|
60
120
|
|
121
|
+
##
|
122
|
+
# Returns the error hashes for a row that was not inserted. Each error
|
123
|
+
# hash contains the following keys: `reason`, `location`, `debugInfo`,
|
124
|
+
# and `message`.
|
125
|
+
#
|
126
|
+
# @param [Hash] row A hash containing the data for a row.
|
127
|
+
#
|
128
|
+
# @return [Array<Hash>, nil] An array of error hashes, or `nil` if no
|
129
|
+
# errors are found in the response for the row.
|
130
|
+
#
|
61
131
|
def errors_for row
|
62
132
|
ie = insert_error_for row
|
63
133
|
return ie.errors if ie
|
64
134
|
[]
|
65
135
|
end
|
66
136
|
|
137
|
+
##
|
138
|
+
# Returns the index for a row that was not inserted.
|
139
|
+
#
|
140
|
+
# @param [Hash] row A hash containing the data for a row.
|
141
|
+
#
|
142
|
+
# @return [Integer, nil] An error object, or `nil` if no error is
|
143
|
+
# found in the response for the row.
|
144
|
+
#
|
67
145
|
def index_for row
|
68
146
|
ie = insert_error_for row
|
69
147
|
return ie.index if ie
|
@@ -78,6 +156,16 @@ module Google
|
|
78
156
|
|
79
157
|
##
|
80
158
|
# InsertError
|
159
|
+
#
|
160
|
+
# Represents the errors for a row that was not inserted.
|
161
|
+
#
|
162
|
+
# @attr_reader [Integer] index The index of the row that error applies
|
163
|
+
# to.
|
164
|
+
# @attr_reader [Hash] row The row that error applies to.
|
165
|
+
# @attr_reader [Hash] errors Error information for the row indicated by
|
166
|
+
# the index property, with the following keys: `reason`, `location`,
|
167
|
+
# `debugInfo`, and `message`.
|
168
|
+
#
|
81
169
|
class InsertError
|
82
170
|
attr_reader :index
|
83
171
|
attr_reader :row
|
@@ -15,7 +15,6 @@
|
|
15
15
|
|
16
16
|
require "google/cloud/errors"
|
17
17
|
require "google/cloud/bigquery/service"
|
18
|
-
require "google/cloud/bigquery/query_data"
|
19
18
|
require "google/cloud/bigquery/job/list"
|
20
19
|
require "json"
|
21
20
|
|
@@ -31,11 +30,11 @@ module Google
|
|
31
30
|
# {CopyJob}, {ExtractJob}, {LoadJob}, and {QueryJob}.
|
32
31
|
#
|
33
32
|
# A job instance is created when you call {Project#query_job},
|
34
|
-
# {Dataset#query_job}, {Table#
|
35
|
-
# {View#data}.
|
33
|
+
# {Dataset#query_job}, {Table#copy_job}, {Table#extract_job},
|
34
|
+
# {Table#load_job}, or {View#data}.
|
36
35
|
#
|
37
|
-
# @see https://cloud.google.com/bigquery/docs/
|
38
|
-
# Managing Jobs
|
36
|
+
# @see https://cloud.google.com/bigquery/docs/managing-jobs Running and
|
37
|
+
# Managing Jobs
|
39
38
|
# @see https://cloud.google.com/bigquery/docs/reference/v2/jobs Jobs API
|
40
39
|
# reference
|
41
40
|
#
|
@@ -52,7 +51,7 @@ module Google
|
|
52
51
|
# if job.failed?
|
53
52
|
# puts job.error
|
54
53
|
# else
|
55
|
-
# puts job.
|
54
|
+
# puts job.data.first
|
56
55
|
# end
|
57
56
|
#
|
58
57
|
class Job
|
@@ -73,21 +72,41 @@ module Google
|
|
73
72
|
|
74
73
|
##
|
75
74
|
# The ID of the job.
|
75
|
+
#
|
76
|
+
# @return [String] The ID must contain only letters (a-z, A-Z), numbers
|
77
|
+
# (0-9), underscores (_), or dashes (-). The maximum length is 1,024
|
78
|
+
# characters.
|
79
|
+
#
|
76
80
|
def job_id
|
77
81
|
@gapi.job_reference.job_id
|
78
82
|
end
|
79
83
|
|
80
84
|
##
|
81
85
|
# The ID of the project containing the job.
|
86
|
+
#
|
87
|
+
# @return [String] The project ID.
|
88
|
+
#
|
82
89
|
def project_id
|
83
90
|
@gapi.job_reference.project_id
|
84
91
|
end
|
85
92
|
|
86
93
|
##
|
87
|
-
# The
|
88
|
-
#
|
89
|
-
#
|
94
|
+
# The email address of the user who ran the job.
|
95
|
+
#
|
96
|
+
# @return [String] The email address.
|
97
|
+
#
|
98
|
+
def user_email
|
99
|
+
@gapi.user_email
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# The current state of the job. A `DONE` state does not mean that the
|
104
|
+
# job completed successfully. Use {#failed?} to discover if an error
|
90
105
|
# occurred or if the job was successful.
|
106
|
+
#
|
107
|
+
# @return [String] The state code. The possible values are `PENDING`,
|
108
|
+
# `RUNNING`, and `DONE`.
|
109
|
+
#
|
91
110
|
def state
|
92
111
|
return nil if @gapi.status.nil?
|
93
112
|
@gapi.status.state
|
@@ -95,6 +114,9 @@ module Google
|
|
95
114
|
|
96
115
|
##
|
97
116
|
# Checks if the job's state is `RUNNING`.
|
117
|
+
#
|
118
|
+
# @return [Boolean] `true` when `RUNNING`, `false` otherwise.
|
119
|
+
#
|
98
120
|
def running?
|
99
121
|
return false if state.nil?
|
100
122
|
"running".casecmp(state).zero?
|
@@ -102,6 +124,9 @@ module Google
|
|
102
124
|
|
103
125
|
##
|
104
126
|
# Checks if the job's state is `PENDING`.
|
127
|
+
#
|
128
|
+
# @return [Boolean] `true` when `PENDING`, `false` otherwise.
|
129
|
+
#
|
105
130
|
def pending?
|
106
131
|
return false if state.nil?
|
107
132
|
"pending".casecmp(state).zero?
|
@@ -112,19 +137,29 @@ module Google
|
|
112
137
|
# running. However, a `DONE` state does not mean that the job completed
|
113
138
|
# successfully. Use {#failed?} to detect if an error occurred or if the
|
114
139
|
# job was successful.
|
140
|
+
#
|
141
|
+
# @return [Boolean] `true` when `DONE`, `false` otherwise.
|
142
|
+
#
|
115
143
|
def done?
|
116
144
|
return false if state.nil?
|
117
145
|
"done".casecmp(state).zero?
|
118
146
|
end
|
119
147
|
|
120
148
|
##
|
121
|
-
# Checks if an error is present.
|
149
|
+
# Checks if an error is present. Use {#error} to access the error
|
150
|
+
# object.
|
151
|
+
#
|
152
|
+
# @return [Boolean] `true` when there is an error, `false` otherwise.
|
153
|
+
#
|
122
154
|
def failed?
|
123
155
|
!error.nil?
|
124
156
|
end
|
125
157
|
|
126
158
|
##
|
127
159
|
# The time when the job was created.
|
160
|
+
#
|
161
|
+
# @return [Time, nil] The creation time from the job statistics.
|
162
|
+
#
|
128
163
|
def created_at
|
129
164
|
::Time.at(Integer(@gapi.statistics.creation_time) / 1000.0)
|
130
165
|
rescue
|
@@ -135,6 +170,9 @@ module Google
|
|
135
170
|
# The time when the job was started.
|
136
171
|
# This field is present after the job's state changes from `PENDING`
|
137
172
|
# to either `RUNNING` or `DONE`.
|
173
|
+
#
|
174
|
+
# @return [Time, nil] The start time from the job statistics.
|
175
|
+
#
|
138
176
|
def started_at
|
139
177
|
::Time.at(Integer(@gapi.statistics.start_time) / 1000.0)
|
140
178
|
rescue
|
@@ -144,6 +182,9 @@ module Google
|
|
144
182
|
##
|
145
183
|
# The time when the job ended.
|
146
184
|
# This field is present when the job's state is `DONE`.
|
185
|
+
#
|
186
|
+
# @return [Time, nil] The end time from the job statistics.
|
187
|
+
#
|
147
188
|
def ended_at
|
148
189
|
::Time.at(Integer(@gapi.statistics.end_time) / 1000.0)
|
149
190
|
rescue
|
@@ -165,6 +206,9 @@ module Google
|
|
165
206
|
#
|
166
207
|
# @see https://cloud.google.com/bigquery/docs/reference/v2/jobs Jobs API
|
167
208
|
# reference
|
209
|
+
#
|
210
|
+
# @return [Hash] The job statistics.
|
211
|
+
#
|
168
212
|
def statistics
|
169
213
|
JSON.parse @gapi.statistics.to_json
|
170
214
|
end
|
@@ -173,6 +217,9 @@ module Google
|
|
173
217
|
##
|
174
218
|
# The job's status. Returns a hash. The values contained in the hash are
|
175
219
|
# also exposed by {#state}, {#error}, and {#errors}.
|
220
|
+
#
|
221
|
+
# @return [Hash] The job status.
|
222
|
+
#
|
176
223
|
def status
|
177
224
|
JSON.parse @gapi.status.to_json
|
178
225
|
end
|
@@ -184,7 +231,8 @@ module Google
|
|
184
231
|
# @see https://cloud.google.com/bigquery/docs/reference/v2/jobs Jobs API
|
185
232
|
# reference
|
186
233
|
#
|
187
|
-
# @return [Hash] Returns a hash containing `reason` and `message`
|
234
|
+
# @return [Hash, nil] Returns a hash containing `reason` and `message`
|
235
|
+
# keys:
|
188
236
|
#
|
189
237
|
# {
|
190
238
|
# "reason"=>"notFound",
|
@@ -192,21 +240,55 @@ module Google
|
|
192
240
|
# }
|
193
241
|
#
|
194
242
|
def error
|
195
|
-
|
196
|
-
return nil if @gapi.status.error_result.nil?
|
197
|
-
JSON.parse @gapi.status.error_result.to_json
|
243
|
+
status["errorResult"]
|
198
244
|
end
|
199
245
|
|
200
246
|
##
|
201
247
|
# The errors for the job, if any errors have occurred. Returns an array
|
202
248
|
# of hash objects. See {#error}.
|
249
|
+
#
|
250
|
+
# @return [Array<Hash>, nil] Returns an array of hashes containing
|
251
|
+
# `reason` and `message` keys:
|
252
|
+
#
|
253
|
+
# {
|
254
|
+
# "reason"=>"notFound",
|
255
|
+
# "message"=>"Not found: Table publicdata:samples.BAD_ID"
|
256
|
+
# }
|
257
|
+
#
|
203
258
|
def errors
|
204
|
-
|
205
|
-
|
259
|
+
Array status["errors"]
|
260
|
+
end
|
261
|
+
|
262
|
+
##
|
263
|
+
# A hash of user-provided labels associated with this job. Labels can be
|
264
|
+
# provided when the job is created, and used to organize and group jobs.
|
265
|
+
#
|
266
|
+
# The returned hash is frozen and changes are not allowed. Use
|
267
|
+
# {#labels=} to replace the entire hash.
|
268
|
+
#
|
269
|
+
# @return [Hash] The job labels.
|
270
|
+
#
|
271
|
+
# @!group Attributes
|
272
|
+
#
|
273
|
+
def labels
|
274
|
+
m = @gapi.configuration.labels
|
275
|
+
m = m.to_h if m.respond_to? :to_h
|
276
|
+
m.dup.freeze
|
206
277
|
end
|
207
278
|
|
208
279
|
##
|
209
280
|
# Cancels the job.
|
281
|
+
#
|
282
|
+
# @example
|
283
|
+
# require "google/cloud/bigquery"
|
284
|
+
#
|
285
|
+
# bigquery = Google::Cloud::Bigquery.new
|
286
|
+
#
|
287
|
+
# job = bigquery.query_job "SELECT COUNT(word) as count FROM " \
|
288
|
+
# "publicdata.samples.shakespeare"
|
289
|
+
#
|
290
|
+
# job.cancel
|
291
|
+
#
|
210
292
|
def cancel
|
211
293
|
ensure_service!
|
212
294
|
resp = service.cancel_job job_id
|
@@ -216,6 +298,18 @@ module Google
|
|
216
298
|
|
217
299
|
##
|
218
300
|
# Created a new job with the current configuration.
|
301
|
+
#
|
302
|
+
# @example
|
303
|
+
# require "google/cloud/bigquery"
|
304
|
+
#
|
305
|
+
# bigquery = Google::Cloud::Bigquery.new
|
306
|
+
#
|
307
|
+
# job = bigquery.query_job "SELECT COUNT(word) as count FROM " \
|
308
|
+
# "publicdata.samples.shakespeare"
|
309
|
+
#
|
310
|
+
# job.wait_until_done!
|
311
|
+
# job.rerun!
|
312
|
+
#
|
219
313
|
def rerun!
|
220
314
|
ensure_service!
|
221
315
|
gapi = service.insert_job @gapi.configuration
|
@@ -224,6 +318,19 @@ module Google
|
|
224
318
|
|
225
319
|
##
|
226
320
|
# Reloads the job with current data from the BigQuery service.
|
321
|
+
#
|
322
|
+
# @example
|
323
|
+
# require "google/cloud/bigquery"
|
324
|
+
#
|
325
|
+
# bigquery = Google::Cloud::Bigquery.new
|
326
|
+
#
|
327
|
+
# job = bigquery.query_job "SELECT COUNT(word) as count FROM " \
|
328
|
+
# "publicdata.samples.shakespeare"
|
329
|
+
#
|
330
|
+
# job.done?
|
331
|
+
# job.reload!
|
332
|
+
# job.done? #=> true
|
333
|
+
#
|
227
334
|
def reload!
|
228
335
|
ensure_service!
|
229
336
|
gapi = service.get_job job_id
|
@@ -232,8 +339,9 @@ module Google
|
|
232
339
|
alias_method :refresh!, :reload!
|
233
340
|
|
234
341
|
##
|
235
|
-
# Refreshes the job until the job is `DONE`.
|
236
|
-
#
|
342
|
+
# Refreshes the job until the job is `DONE`. The delay between refreshes
|
343
|
+
# starts at 5 seconds and increases exponentially to a maximum of 60
|
344
|
+
# seconds.
|
237
345
|
#
|
238
346
|
# @example
|
239
347
|
# require "google/cloud/bigquery"
|
@@ -242,12 +350,16 @@ module Google
|
|
242
350
|
# dataset = bigquery.dataset "my_dataset"
|
243
351
|
# table = dataset.table "my_table"
|
244
352
|
#
|
245
|
-
# extract_job = table.
|
246
|
-
#
|
353
|
+
# extract_job = table.extract_job "gs://my-bucket/file-name.json",
|
354
|
+
# format: "json"
|
247
355
|
# extract_job.wait_until_done!
|
248
356
|
# extract_job.done? #=> true
|
357
|
+
#
|
249
358
|
def wait_until_done!
|
250
|
-
backoff =
|
359
|
+
backoff = lambda do |retries|
|
360
|
+
delay = [retries ** 2 + 5, 60].min # Maximum delay is 60
|
361
|
+
sleep delay
|
362
|
+
end
|
251
363
|
retries = 0
|
252
364
|
until done?
|
253
365
|
backoff.call retries
|
@@ -266,6 +378,20 @@ module Google
|
|
266
378
|
end
|
267
379
|
end
|
268
380
|
|
381
|
+
##
|
382
|
+
# @private New Google::Apis::Error with job failure details
|
383
|
+
def gapi_error
|
384
|
+
return nil unless failed?
|
385
|
+
|
386
|
+
error_status_code = status_code_for_reason error["reason"]
|
387
|
+
error_body = error
|
388
|
+
error_body["errors"] = errors
|
389
|
+
|
390
|
+
Google::Apis::Error.new error["message"],
|
391
|
+
status_code: error_status_code,
|
392
|
+
body: error_body
|
393
|
+
end
|
394
|
+
|
269
395
|
protected
|
270
396
|
|
271
397
|
##
|
@@ -296,6 +422,19 @@ module Google
|
|
296
422
|
rescue Google::Cloud::NotFoundError
|
297
423
|
nil
|
298
424
|
end
|
425
|
+
|
426
|
+
def status_code_for_reason reason
|
427
|
+
codes = { "accessDenied" => 403, "backendError" => 500,
|
428
|
+
"billingNotEnabled" => 403,
|
429
|
+
"billingTierLimitExceeded" => 400, "blocked" => 403,
|
430
|
+
"duplicate" => 409, "internalError" =>500, "invalid" => 400,
|
431
|
+
"invalidQuery" => 400, "notFound" =>404,
|
432
|
+
"notImplemented" => 501, "quotaExceeded" => 403,
|
433
|
+
"rateLimitExceeded" => 403, "resourceInUse" => 400,
|
434
|
+
"resourcesExceeded" => 400, "responseTooLarge" => 403,
|
435
|
+
"tableUnavailable" => 400 }
|
436
|
+
codes[reason] || 0
|
437
|
+
end
|
299
438
|
end
|
300
439
|
end
|
301
440
|
end
|