google-cloud-bigquery 1.8.2 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a0a449a43a1a49603e04ea3d47e7e60a3145fc47da1637e6a995209701479f2b
4
- data.tar.gz: 7f18be48f6a7dd9cb641a60dc38a20f5f0cdc6005b932a54339203c585b153bb
3
+ metadata.gz: 783828eac7c8335eced73772ee8d7f154daa10adfb2976c0961bca8132d449f5
4
+ data.tar.gz: baf70806a64f209d6fd7d5567b11df9287879ea941478054ca4806dafb8980ec
5
5
  SHA512:
6
- metadata.gz: 07070a48ae7e992e29df9da0b4fe105bbfc7bdc038eef7a861ccdce4dd5269dea568df3190286f48b52a95bbf85e9b66957bc03a17180bbb84e4dbde5b0aceb5
7
- data.tar.gz: ef0afe60412417e242ae1d0acfe1ddd72d6e0473535f8d1b425e451372411bd7fdac427ae6c3faa325552daca83e9f6a9614314e1b226684f783b230a79a61db
6
+ metadata.gz: dd35d5d59e70c1d6aa417bed8e48a5d94454353953b8a674f28f94a03ee748d8c559028ef5551a9e2c6d3ec98a67b036e48c8bbc261cf44671eb3f4d1ccd6086
7
+ data.tar.gz: 2bffb6a6249419fdc9745e8f829a04f53daae9bdbc67fd72c9e58e8a70741e9261df1f189139bd73b5aacfe0584acacbd438720a6d314c5410cd40c30c675063
@@ -1,5 +1,13 @@
1
1
  # Release History
2
2
 
3
+ ### 1.9.0 / 2018-10-25
4
+
5
+ * Add clustering fields to LoadJob, QueryJob and Table
6
+ * Add DDL/DML support
7
+ * Update QueryJob#data to not return table rows for DDL/DML
8
+ * Add DDL/DML statistics attrs to QueryJob and Data
9
+ * Add #numeric to Table::Updater and LoadJob::Updater (@leklund)
10
+
3
11
  ### 1.8.2 / 2018-09-20
4
12
 
5
13
  * Update documentation.
@@ -59,6 +59,10 @@ module Google
59
59
  # @private The Google API Client object in JSON Hash.
60
60
  attr_accessor :gapi_json
61
61
 
62
+ ##
63
+ # @private The query Job gapi object, or nil if from Table#data.
64
+ attr_accessor :job_gapi
65
+
62
66
  # @private
63
67
  def initialize arr = []
64
68
  @service = nil
@@ -195,6 +199,130 @@ module Google
195
199
  schema.headers
196
200
  end
197
201
 
202
+ ##
203
+ # The type of query statement, if valid. Possible values (new values
204
+ # might be added in the future):
205
+ #
206
+ # * "CREATE_MODEL": DDL statement, see [Using Data Definition Language
207
+ # Statements](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)
208
+ # * "CREATE_TABLE": DDL statement, see [Using Data Definition Language
209
+ # Statements](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)
210
+ # * "CREATE_TABLE_AS_SELECT": DDL statement, see [Using Data Definition
211
+ # Language Statements](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)
212
+ # * "CREATE_VIEW": DDL statement, see [Using Data Definition Language
213
+ # Statements](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)
214
+ # * "DELETE": DML statement, see [Data Manipulation Language Syntax](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax)
215
+ # * "DROP_MODEL": DDL statement, see [Using Data Definition Language
216
+ # Statements](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)
217
+ # * "DROP_TABLE": DDL statement, see [Using Data Definition Language
218
+ # Statements](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)
219
+ # * "DROP_VIEW": DDL statement, see [Using Data Definition Language
220
+ # Statements](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)
221
+ # * "INSERT": DML statement, see [Data Manipulation Language Syntax](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax)
222
+ # * "MERGE": DML statement, see [Data Manipulation Language Syntax](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax)
223
+ # * "SELECT": SQL query, see [Standard SQL Query Syntax](https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax)
224
+ # * "UPDATE": DML statement, see [Data Manipulation Language Syntax](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax)
225
+ #
226
+ # @return [String, nil] The type of query statement.
227
+ #
228
+ def statement_type
229
+ return nil unless job_gapi && job_gapi.statistics.query
230
+ job_gapi.statistics.query.statement_type
231
+ end
232
+
233
+ ##
234
+ # Whether the query that created this data was a DDL statement.
235
+ #
236
+ # @see https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language
237
+ # Using Data Definition Language Statements
238
+ #
239
+ # @return [Boolean]
240
+ #
241
+ # @example
242
+ # require "google/cloud/bigquery"
243
+ #
244
+ # bigquery = Google::Cloud::Bigquery.new
245
+ # data = bigquery.query "CREATE TABLE my_table (x INT64)"
246
+ #
247
+ # data.statement_type #=> "CREATE_TABLE"
248
+ # data.ddl? #=> true
249
+ #
250
+ def ddl?
251
+ %w[CREATE_MODEL CREATE_TABLE CREATE_TABLE_AS_SELECT CREATE_VIEW \
252
+ DROP_MODEL DROP_TABLE DROP_VIEW].include? statement_type
253
+ end
254
+
255
+ ##
256
+ # Whether the query that created this data was a DML statement.
257
+ #
258
+ # @see https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax
259
+ # Data Manipulation Language Syntax
260
+ #
261
+ # @return [Boolean]
262
+ #
263
+ # @example
264
+ # require "google/cloud/bigquery"
265
+ #
266
+ # bigquery = Google::Cloud::Bigquery.new
267
+ # data = bigquery.query "UPDATE my_table " \
268
+ # "SET x = x + 1 " \
269
+ # "WHERE x IS NOT NULL"
270
+ #
271
+ # data.statement_type #=> "UPDATE"
272
+ # data.dml? #=> true
273
+ #
274
+ def dml?
275
+ %w[INSERT UPDATE MERGE DELETE].include? statement_type
276
+ end
277
+
278
+ ##
279
+ # The DDL operation performed, possibly dependent on the pre-existence
280
+ # of the DDL target. (See {#ddl_target_table}.) Possible values (new
281
+ # values might be added in the future):
282
+ #
283
+ # * "CREATE": The query created the DDL target.
284
+ # * "SKIP": No-op. Example cases: the query is
285
+ # `CREATE TABLE IF NOT EXISTS` while the table already exists, or the
286
+ # query is `DROP TABLE IF EXISTS` while the table does not exist.
287
+ # * "REPLACE": The query replaced the DDL target. Example case: the
288
+ # query is `CREATE OR REPLACE TABLE`, and the table already exists.
289
+ # * "DROP": The query deleted the DDL target.
290
+ #
291
+ # @return [String, nil] The DDL operation performed.
292
+ #
293
+ def ddl_operation_performed
294
+ return nil unless job_gapi && job_gapi.statistics.query
295
+ job_gapi.statistics.query.ddl_operation_performed
296
+ end
297
+
298
+ ##
299
+ # The DDL target table, in reference state. (See {Table#reference?}.)
300
+ # Present only for `CREATE/DROP TABLE/VIEW` queries. (See
301
+ # {#statement_type}.)
302
+ #
303
+ # @return [Google::Cloud::Bigquery::Table, nil] The DDL target table, in
304
+ # reference state.
305
+ #
306
+ def ddl_target_table
307
+ return nil unless job_gapi && job_gapi.statistics.query
308
+ ensure_service!
309
+ table = job_gapi.statistics.query.ddl_target_table
310
+ return nil unless table
311
+ Google::Cloud::Bigquery::Table.new_reference_from_gapi table, service
312
+ end
313
+
314
+ ##
315
+ # The number of rows affected by a DML statement. Present only for DML
316
+ # statements `INSERT`, `UPDATE` or `DELETE`. (See {#statement_type}.)
317
+ #
318
+ # @return [Integer, nil] The number of rows affected by a DML statement,
319
+ # or `nil` if the query is not a DML statement.
320
+ #
321
+ def num_dml_affected_rows
322
+ return nil unless job_gapi && job_gapi.statistics.query
323
+ job_gapi.statistics.query.num_dml_affected_rows
324
+ end
325
+
198
326
  ##
199
327
  # Whether there is a next page of data.
200
328
  #
@@ -252,7 +380,7 @@ module Google
252
380
  @table_gapi.table_reference.dataset_id,
253
381
  @table_gapi.table_reference.table_id,
254
382
  token: token
255
- self.class.from_gapi_json data_json, @table_gapi, @service
383
+ self.class.from_gapi_json data_json, @table_gapi, job_gapi, @service
256
384
  end
257
385
 
258
386
  ##
@@ -327,13 +455,16 @@ module Google
327
455
 
328
456
  ##
329
457
  # @private New Data from a response object.
330
- def self.from_gapi_json gapi_json, table_gapi, service
331
- formatted_rows = Convert.format_rows(gapi_json[:rows],
332
- table_gapi.schema.fields)
458
+ def self.from_gapi_json gapi_json, table_gapi, job_gapi, service
459
+ rows = gapi_json[:rows] || []
460
+ unless rows.empty?
461
+ rows = Convert.format_rows rows, table_gapi.schema.fields
462
+ end
333
463
 
334
- data = new formatted_rows
464
+ data = new rows
335
465
  data.table_gapi = table_gapi
336
466
  data.gapi_json = gapi_json
467
+ data.job_gapi = job_gapi
337
468
  data.service = service
338
469
  data
339
470
  end
@@ -874,6 +874,32 @@ module Google
874
874
  # end
875
875
  # end
876
876
  #
877
+ # @example Execute a DDL statement:
878
+ # require "google/cloud/bigquery"
879
+ #
880
+ # bigquery = Google::Cloud::Bigquery.new
881
+ #
882
+ # job = bigquery.query_job "CREATE TABLE my_table (x INT64)"
883
+ #
884
+ # job.wait_until_done!
885
+ # if !job.failed?
886
+ # table_ref = job.ddl_target_table
887
+ # end
888
+ #
889
+ # @example Execute a DML statement:
890
+ # require "google/cloud/bigquery"
891
+ #
892
+ # bigquery = Google::Cloud::Bigquery.new
893
+ #
894
+ # job = bigquery.query_job "UPDATE my_table " \
895
+ # "SET x = x + 1 " \
896
+ # "WHERE x IS NOT NULL"
897
+ #
898
+ # job.wait_until_done!
899
+ # if !job.failed?
900
+ # puts job.num_dml_affected_rows
901
+ # end
902
+ #
877
903
  # @example Query using external data source, set destination:
878
904
  # require "google/cloud/bigquery"
879
905
  #
@@ -930,7 +956,8 @@ module Google
930
956
  # Queries data and waits for the results. In this method, a {QueryJob}
931
957
  # is created and its results are saved to a temporary table, then read
932
958
  # from the table. Timeouts and transient errors are generally handled
933
- # as needed to complete the query.
959
+ # as needed to complete the query. When used for executing DDL/DML
960
+ # statements, this method does not return row data.
934
961
  #
935
962
  # Sets the current dataset as the default dataset in the query. Useful
936
963
  # for using unqualified table names.
@@ -1066,6 +1093,26 @@ module Google
1066
1093
  # puts row[:name]
1067
1094
  # end
1068
1095
  #
1096
+ # @example Execute a DDL statement:
1097
+ # require "google/cloud/bigquery"
1098
+ #
1099
+ # bigquery = Google::Cloud::Bigquery.new
1100
+ #
1101
+ # data = bigquery.query "CREATE TABLE my_table (x INT64)"
1102
+ #
1103
+ # table_ref = data.ddl_target_table
1104
+ #
1105
+ # @example Execute a DML statement:
1106
+ # require "google/cloud/bigquery"
1107
+ #
1108
+ # bigquery = Google::Cloud::Bigquery.new
1109
+ #
1110
+ # data = bigquery.query "UPDATE my_table " \
1111
+ # "SET x = x + 1 " \
1112
+ # "WHERE x IS NOT NULL"
1113
+ #
1114
+ # puts data.num_dml_affected_rows
1115
+ #
1069
1116
  # @example Query using external data source, set destination:
1070
1117
  # require "google/cloud/bigquery"
1071
1118
  #
@@ -1798,7 +1845,7 @@ module Google
1798
1845
  ##
1799
1846
  # @private New lazy Dataset object without making an HTTP request.
1800
1847
  def self.new_reference project_id, dataset_id, service
1801
- # TODO: raise if dataset_id is nil?
1848
+ raise ArgumentError, "dataset_id is required" unless dataset_id
1802
1849
  new.tap do |b|
1803
1850
  reference_gapi = Google::Apis::BigqueryV2::DatasetReference.new(
1804
1851
  project_id: project_id,
@@ -424,6 +424,45 @@ module Google
424
424
  tp.require_partition_filter
425
425
  end
426
426
 
427
+ ###
428
+ # Checks if the destination table will be clustered.
429
+ #
430
+ # @see https://cloud.google.com/bigquery/docs/clustered-tables
431
+ # Introduction to Clustered Tables
432
+ #
433
+ # @return [Boolean, nil] `true` when the table will be clustered,
434
+ # or `false` otherwise.
435
+ #
436
+ # @!group Attributes
437
+ #
438
+ def clustering?
439
+ !@gapi.configuration.load.clustering.nil?
440
+ end
441
+
442
+ ###
443
+ # One or more fields on which the destination table should be clustered.
444
+ # Must be specified with time-based partitioning, data in the table will
445
+ # be first partitioned and subsequently clustered. The order of the
446
+ # returned fields determines the sort order of the data.
447
+ #
448
+ # See {LoadJob::Updater#clustering_fields=}.
449
+ #
450
+ # @see https://cloud.google.com/bigquery/docs/partitioned-tables
451
+ # Partitioned Tables
452
+ # @see https://cloud.google.com/bigquery/docs/clustered-tables
453
+ # Introduction to Clustered Tables
454
+ # @see https://cloud.google.com/bigquery/docs/creating-clustered-tables
455
+ # Creating and Using Clustered Tables
456
+ #
457
+ # @return [Array<String>, nil] The clustering fields, or `nil` if the
458
+ # destination table will not be clustered.
459
+ #
460
+ # @!group Attributes
461
+ #
462
+ def clustering_fields
463
+ @gapi.configuration.load.clustering.fields if clustering?
464
+ end
465
+
427
466
  ##
428
467
  # Yielded to a block to accumulate changes for a patch request.
429
468
  class Updater < LoadJob
@@ -599,6 +638,36 @@ module Google
599
638
  schema.float name, description: description, mode: mode
600
639
  end
601
640
 
641
+ ##
642
+ # Adds a numeric number field to the schema. Numeric is a
643
+ # fixed-precision numeric type with 38 decimal digits, 9 that follow
644
+ # the decimal point.
645
+ #
646
+ # See {Schema#numeric}
647
+ #
648
+ # @param [String] name The field name. The name must contain only
649
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
650
+ # start with a letter or underscore. The maximum length is 128
651
+ # characters.
652
+ # @param [String] description A description of the field.
653
+ # @param [Symbol] mode The field's mode. The possible values are
654
+ # `:nullable`, `:required`, and `:repeated`. The default value is
655
+ # `:nullable`.
656
+ #
657
+ # @example
658
+ # require "google/cloud/bigquery"
659
+ #
660
+ # bigquery = Google::Cloud::Bigquery.new
661
+ # dataset = bigquery.dataset "my_dataset"
662
+ # job = dataset.load_job "my_table", "gs://abc/file" do |schema|
663
+ # schema.numeric "total_cost", mode: :required
664
+ # end
665
+ #
666
+ # @!group Schema
667
+ def numeric name, description: nil, mode: :nullable
668
+ schema.numeric name, description: description, mode: mode
669
+ end
670
+
602
671
  ##
603
672
  # Adds a boolean field to the schema.
604
673
  #
@@ -1320,6 +1389,57 @@ module Google
1320
1389
  require_partition_filter: val
1321
1390
  end
1322
1391
 
1392
+ ##
1393
+ # Sets one or more fields on which the destination table should be
1394
+ # clustered. Must be specified with time-based partitioning, data in
1395
+ # the table will be first partitioned and subsequently clustered.
1396
+ #
1397
+ # Only top-level, non-repeated, simple-type fields are supported. When
1398
+ # you cluster a table using multiple columns, the order of columns you
1399
+ # specify is important. The order of the specified columns determines
1400
+ # the sort order of the data.
1401
+ #
1402
+ # See {LoadJob#clustering_fields}.
1403
+ #
1404
+ # @see https://cloud.google.com/bigquery/docs/partitioned-tables
1405
+ # Partitioned Tables
1406
+ # @see https://cloud.google.com/bigquery/docs/clustered-tables
1407
+ # Introduction to Clustered Tables
1408
+ # @see https://cloud.google.com/bigquery/docs/creating-clustered-tables
1409
+ # Creating and Using Clustered Tables
1410
+ #
1411
+ # @param [Array<String>] fields The clustering fields. Only top-level,
1412
+ # non-repeated, simple-type fields are supported.
1413
+ #
1414
+ # @example
1415
+ # require "google/cloud/bigquery"
1416
+ #
1417
+ # bigquery = Google::Cloud::Bigquery.new
1418
+ # dataset = bigquery.dataset "my_dataset"
1419
+ #
1420
+ # gs_url = "gs://my-bucket/file-name.csv"
1421
+ # load_job = dataset.load_job "my_new_table", gs_url do |job|
1422
+ # job.time_partitioning_type = "DAY"
1423
+ # job.time_partitioning_field = "dob"
1424
+ # job.schema do |schema|
1425
+ # schema.timestamp "dob", mode: :required
1426
+ # schema.string "first_name", mode: :required
1427
+ # schema.string "last_name", mode: :required
1428
+ # end
1429
+ # job.clustering_fields = ["last_name", "first_name"]
1430
+ # end
1431
+ #
1432
+ # load_job.wait_until_done!
1433
+ # load_job.done? #=> true
1434
+ #
1435
+ # @!group Attributes
1436
+ #
1437
+ def clustering_fields= fields
1438
+ @gapi.configuration.load.clustering ||= \
1439
+ Google::Apis::BigqueryV2::Clustering.new
1440
+ @gapi.configuration.load.clustering.fields = fields
1441
+ end
1442
+
1323
1443
  ##
1324
1444
  # @private Returns the Google API client library version of this job.
1325
1445
  #
@@ -302,6 +302,35 @@ module Google
302
302
  # end
303
303
  # end
304
304
  #
305
+ # @example Execute a DDL statement:
306
+ # require "google/cloud/bigquery"
307
+ #
308
+ # bigquery = Google::Cloud::Bigquery.new
309
+ #
310
+ # job = bigquery.query_job "CREATE TABLE " \
311
+ # "`my_dataset.my_table` " \
312
+ # "(x INT64)"
313
+ #
314
+ # job.wait_until_done!
315
+ # if !job.failed?
316
+ # table_ref = job.ddl_target_table
317
+ # end
318
+ #
319
+ # @example Execute a DML statement:
320
+ # require "google/cloud/bigquery"
321
+ #
322
+ # bigquery = Google::Cloud::Bigquery.new
323
+ #
324
+ # job = bigquery.query_job "UPDATE " \
325
+ # "`my_dataset.my_table` " \
326
+ # "SET x = x + 1 " \
327
+ # "WHERE x IS NOT NULL"
328
+ #
329
+ # job.wait_until_done!
330
+ # if !job.failed?
331
+ # puts job.num_dml_affected_rows
332
+ # end
333
+ #
305
334
  # @example Query using external data source, set destination:
306
335
  # require "google/cloud/bigquery"
307
336
  #
@@ -356,7 +385,8 @@ module Google
356
385
  # Queries data and waits for the results. In this method, a {QueryJob}
357
386
  # is created and its results are saved to a temporary table, then read
358
387
  # from the table. Timeouts and transient errors are generally handled
359
- # as needed to complete the query.
388
+ # as needed to complete the query. When used for executing DDL/DML
389
+ # statements, this method does not return row data.
360
390
  #
361
391
  # When using standard SQL and passing arguments using `params`, Ruby
362
392
  # types are mapped to BigQuery types as follows:
@@ -505,6 +535,26 @@ module Google
505
535
  # puts row[:name]
506
536
  # end
507
537
  #
538
+ # @example Execute a DDL statement:
539
+ # require "google/cloud/bigquery"
540
+ #
541
+ # bigquery = Google::Cloud::Bigquery.new
542
+ #
543
+ # data = bigquery.query "CREATE TABLE `my_dataset.my_table` (x INT64)"
544
+ #
545
+ # table_ref = data.ddl_target_table
546
+ #
547
+ # @example Execute a DML statement:
548
+ # require "google/cloud/bigquery"
549
+ #
550
+ # bigquery = Google::Cloud::Bigquery.new
551
+ #
552
+ # data = bigquery.query "UPDATE `my_dataset.my_table` " \
553
+ # "SET x = x + 1 " \
554
+ # "WHERE x IS NOT NULL"
555
+ #
556
+ # puts data.num_dml_affected_rows
557
+ #
508
558
  # @example Query using external data source, set destination:
509
559
  # require "google/cloud/bigquery"
510
560
  #
@@ -200,15 +200,25 @@ module Google
200
200
  # The type of query statement, if valid. Possible values (new values
201
201
  # might be added in the future):
202
202
  #
203
- # * "SELECT": `SELECT` query.
204
- # * "INSERT": `INSERT` query; see https://cloud.google.com/bigquery/docs/reference/standard-sql/data-manipulation-language
205
- # * "UPDATE": `UPDATE` query; see https://cloud.google.com/bigquery/docs/reference/standard-sql/data-manipulation-language
206
- # * "DELETE": `DELETE` query; see https://cloud.google.com/bigquery/docs/reference/standard-sql/data-manipulation-language
207
- # * "CREATE_TABLE": `CREATE [OR REPLACE] TABLE` without `AS SELECT`.
208
- # * "CREATE_TABLE_AS_SELECT": `CREATE [OR REPLACE] TABLE ... AS SELECT`.
209
- # * "DROP_TABLE": `DROP TABLE` query.
210
- # * "CREATE_VIEW": `CREATE [OR REPLACE] VIEW ... AS SELECT ...`.
211
- # * "DROP_VIEW": `DROP VIEW` query.
203
+ # * "CREATE_MODEL": DDL statement, see [Using Data Definition Language
204
+ # Statements](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)
205
+ # * "CREATE_TABLE": DDL statement, see [Using Data Definition Language
206
+ # Statements](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)
207
+ # * "CREATE_TABLE_AS_SELECT": DDL statement, see [Using Data Definition
208
+ # Language Statements](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)
209
+ # * "CREATE_VIEW": DDL statement, see [Using Data Definition Language
210
+ # Statements](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)
211
+ # * "DELETE": DML statement, see [Data Manipulation Language Syntax](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax)
212
+ # * "DROP_MODEL": DDL statement, see [Using Data Definition Language
213
+ # Statements](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)
214
+ # * "DROP_TABLE": DDL statement, see [Using Data Definition Language
215
+ # Statements](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)
216
+ # * "DROP_VIEW": DDL statement, see [Using Data Definition Language
217
+ # Statements](https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language)
218
+ # * "INSERT": DML statement, see [Data Manipulation Language Syntax](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax)
219
+ # * "MERGE": DML statement, see [Data Manipulation Language Syntax](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax)
220
+ # * "SELECT": SQL query, see [Standard SQL Query Syntax](https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax)
221
+ # * "UPDATE": DML statement, see [Data Manipulation Language Syntax](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax)
212
222
  #
213
223
  # @return [String, nil] The type of query statement.
214
224
  #
@@ -217,6 +227,51 @@ module Google
217
227
  @gapi.statistics.query.statement_type
218
228
  end
219
229
 
230
+ ##
231
+ # Whether the query is a DDL statement.
232
+ #
233
+ # @see https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language
234
+ # Using Data Definition Language Statements
235
+ #
236
+ # @return [Boolean]
237
+ #
238
+ # @example
239
+ # require "google/cloud/bigquery"
240
+ #
241
+ # bigquery = Google::Cloud::Bigquery.new
242
+ # query_job = bigquery.query_job "CREATE TABLE my_table (x INT64)"
243
+ #
244
+ # query_job.statement_type #=> "CREATE_TABLE"
245
+ # query_job.ddl? #=> true
246
+ #
247
+ def ddl?
248
+ %w[CREATE_MODEL CREATE_TABLE CREATE_TABLE_AS_SELECT CREATE_VIEW \
249
+ DROP_MODEL DROP_TABLE DROP_VIEW].include? statement_type
250
+ end
251
+
252
+ ##
253
+ # Whether the query is a DML statement.
254
+ #
255
+ # @see https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax
256
+ # Data Manipulation Language Syntax
257
+ #
258
+ # @return [Boolean]
259
+ #
260
+ # @example
261
+ # require "google/cloud/bigquery"
262
+ #
263
+ # bigquery = Google::Cloud::Bigquery.new
264
+ # query_job = bigquery.query_job "UPDATE my_table " \
265
+ # "SET x = x + 1 " \
266
+ # "WHERE x IS NOT NULL"
267
+ #
268
+ # query_job.statement_type #=> "UPDATE"
269
+ # query_job.dml? #=> true
270
+ #
271
+ def dml?
272
+ %w[INSERT UPDATE MERGE DELETE].include? statement_type
273
+ end
274
+
220
275
  ##
221
276
  # The DDL operation performed, possibly dependent on the pre-existence
222
277
  # of the DDL target. (See {#ddl_target_table}.) Possible values (new
@@ -253,6 +308,18 @@ module Google
253
308
  Google::Cloud::Bigquery::Table.new_reference_from_gapi table, service
254
309
  end
255
310
 
311
+ ##
312
+ # The number of rows affected by a DML statement. Present only for DML
313
+ # statements `INSERT`, `UPDATE` or `DELETE`. (See {#statement_type}.)
314
+ #
315
+ # @return [Integer, nil] The number of rows affected by a DML statement,
316
+ # or `nil` if the query is not a DML statement.
317
+ #
318
+ def num_dml_affected_rows
319
+ return nil unless @gapi.statistics.query
320
+ @gapi.statistics.query.num_dml_affected_rows
321
+ end
322
+
256
323
  ##
257
324
  # The table in which the query results are stored.
258
325
  #
@@ -393,6 +460,45 @@ module Google
393
460
  tp.require_partition_filter
394
461
  end
395
462
 
463
+ ###
464
+ # Checks if the destination table will be clustered.
465
+ #
466
+ # @see https://cloud.google.com/bigquery/docs/clustered-tables
467
+ # Introduction to Clustered Tables
468
+ #
469
+ # @return [Boolean, nil] `true` when the table will be clustered,
470
+ # or `false` otherwise.
471
+ #
472
+ # @!group Attributes
473
+ #
474
+ def clustering?
475
+ !@gapi.configuration.query.clustering.nil?
476
+ end
477
+
478
+ ###
479
+ # One or more fields on which the destination table should be clustered.
480
+ # Must be specified with time-based partitioning, data in the table will
481
+ # be first partitioned and subsequently clustered. The order of the
482
+ # returned fields determines the sort order of the data.
483
+ #
484
+ # See {QueryJob::Updater#clustering_fields=}.
485
+ #
486
+ # @see https://cloud.google.com/bigquery/docs/partitioned-tables
487
+ # Partitioned Tables
488
+ # @see https://cloud.google.com/bigquery/docs/clustered-tables
489
+ # Introduction to Clustered Tables
490
+ # @see https://cloud.google.com/bigquery/docs/creating-clustered-tables
491
+ # Creating and Using Clustered Tables
492
+ #
493
+ # @return [Array<String>, nil] The clustering fields, or `nil` if the
494
+ # destination table will not be clustered.
495
+ #
496
+ # @!group Attributes
497
+ #
498
+ def clustering_fields
499
+ @gapi.configuration.query.clustering.fields if clustering?
500
+ end
501
+
396
502
  ##
397
503
  # Refreshes the job until the job is `DONE`.
398
504
  # The delay between refreshes will incrementally increase.
@@ -451,7 +557,10 @@ module Google
451
557
  #
452
558
  def data token: nil, max: nil, start: nil
453
559
  return nil unless done?
454
-
560
+ if ddl? || dml?
561
+ data_hash = { totalRows: nil, rows: [] }
562
+ return Data.from_gapi_json data_hash, nil, @gapi, service
563
+ end
455
564
  ensure_schema!
456
565
 
457
566
  options = { token: token, max: max, start: start }
@@ -459,7 +568,7 @@ module Google
459
568
  destination_table_dataset_id,
460
569
  destination_table_table_id,
461
570
  options
462
- Data.from_gapi_json data_hash, destination_table_gapi, service
571
+ Data.from_gapi_json data_hash, destination_table_gapi, @gapi, service
463
572
  end
464
573
  alias query_results data
465
574
 
@@ -800,9 +909,9 @@ module Google
800
909
  #
801
910
  # key_name = "projects/a/locations/b/keyRings/c/cryptoKeys/d"
802
911
  # encrypt_config = bigquery.encryption kms_key: key_name
803
- # job = bigquery.query_job "SELECT 1;" do |query|
804
- # query.table = dataset.table "my_table", skip_lookup: true
805
- # query.encryption = encrypt_config
912
+ # job = bigquery.query_job "SELECT 1;" do |job|
913
+ # job.table = dataset.table "my_table", skip_lookup: true
914
+ # job.encryption = encrypt_config
806
915
  # end
807
916
  #
808
917
  # @!group Attributes
@@ -828,12 +937,15 @@ module Google
828
937
  #
829
938
  # bigquery = Google::Cloud::Bigquery.new
830
939
  # dataset = bigquery.dataset "my_dataset"
940
+ # destination_table = dataset.table "my_destination_table",
941
+ # skip_lookup: true
831
942
  #
832
943
  # job = dataset.query_job "SELECT * FROM UNNEST(" \
833
944
  # "GENERATE_TIMESTAMP_ARRAY(" \
834
945
  # "'2018-10-01 00:00:00', " \
835
946
  # "'2018-10-10 00:00:00', " \
836
947
  # "INTERVAL 1 DAY)) AS dob" do |job|
948
+ # job.table = destination_table
837
949
  # job.time_partitioning_type = "DAY"
838
950
  # end
839
951
  #
@@ -871,12 +983,15 @@ module Google
871
983
  #
872
984
  # bigquery = Google::Cloud::Bigquery.new
873
985
  # dataset = bigquery.dataset "my_dataset"
986
+ # destination_table = dataset.table "my_destination_table",
987
+ # skip_lookup: true
874
988
  #
875
989
  # job = dataset.query_job "SELECT * FROM UNNEST(" \
876
990
  # "GENERATE_TIMESTAMP_ARRAY(" \
877
991
  # "'2018-10-01 00:00:00', " \
878
992
  # "'2018-10-10 00:00:00', " \
879
993
  # "INTERVAL 1 DAY)) AS dob" do |job|
994
+ # job.table = destination_table
880
995
  # job.time_partitioning_type = "DAY"
881
996
  # job.time_partitioning_field = "dob"
882
997
  # end
@@ -908,12 +1023,15 @@ module Google
908
1023
  #
909
1024
  # bigquery = Google::Cloud::Bigquery.new
910
1025
  # dataset = bigquery.dataset "my_dataset"
1026
+ # destination_table = dataset.table "my_destination_table",
1027
+ # skip_lookup: true
911
1028
  #
912
1029
  # job = dataset.query_job "SELECT * FROM UNNEST(" \
913
1030
  # "GENERATE_TIMESTAMP_ARRAY(" \
914
1031
  # "'2018-10-01 00:00:00', " \
915
1032
  # "'2018-10-10 00:00:00', " \
916
1033
  # "INTERVAL 1 DAY)) AS dob" do |job|
1034
+ # job.table = destination_table
917
1035
  # job.time_partitioning_type = "DAY"
918
1036
  # job.time_partitioning_expiration = 86_400
919
1037
  # end
@@ -948,6 +1066,54 @@ module Google
948
1066
  require_partition_filter: val
949
1067
  end
950
1068
 
1069
+ ##
1070
+ # Sets one or more fields on which the destination table should be
1071
+ # clustered. Must be specified with time-based partitioning, data in
1072
+ # the table will be first partitioned and subsequently clustered.
1073
+ #
1074
+ # Only top-level, non-repeated, simple-type fields are supported. When
1075
+ # you cluster a table using multiple columns, the order of columns you
1076
+ # specify is important. The order of the specified columns determines
1077
+ # the sort order of the data.
1078
+ #
1079
+ # See {QueryJob#clustering_fields}.
1080
+ #
1081
+ # @see https://cloud.google.com/bigquery/docs/partitioned-tables
1082
+ # Partitioned Tables
1083
+ # @see https://cloud.google.com/bigquery/docs/clustered-tables
1084
+ # Introduction to Clustered Tables
1085
+ # @see https://cloud.google.com/bigquery/docs/creating-clustered-tables
1086
+ # Creating and Using Clustered Tables
1087
+ #
1088
+ # @param [Array<String>] fields The clustering fields. Only top-level,
1089
+ # non-repeated, simple-type fields are supported.
1090
+ #
1091
+ # @example
1092
+ # require "google/cloud/bigquery"
1093
+ #
1094
+ # bigquery = Google::Cloud::Bigquery.new
1095
+ # dataset = bigquery.dataset "my_dataset"
1096
+ # destination_table = dataset.table "my_destination_table",
1097
+ # skip_lookup: true
1098
+ #
1099
+ # job = dataset.query_job "SELECT * FROM my_table" do |job|
1100
+ # job.table = destination_table
1101
+ # job.time_partitioning_type = "DAY"
1102
+ # job.time_partitioning_field = "dob"
1103
+ # job.clustering_fields = ["last_name", "first_name"]
1104
+ # end
1105
+ #
1106
+ # job.wait_until_done!
1107
+ # job.done? #=> true
1108
+ #
1109
+ # @!group Attributes
1110
+ #
1111
+ def clustering_fields= fields
1112
+ @gapi.configuration.query.clustering ||= \
1113
+ Google::Apis::BigqueryV2::Clustering.new
1114
+ @gapi.configuration.query.clustering.fields = fields
1115
+ end
1116
+
951
1117
  ##
952
1118
  # @private Returns the Google API client library version of this job.
953
1119
  #
@@ -192,10 +192,6 @@ module Google
192
192
  # the example below. BigQuery does not allow you to change partitioning
193
193
  # on an existing table.
194
194
  #
195
- # If the table is not a full resource representation (see
196
- # {#resource_full?}), the full representation will be retrieved before
197
- # the update to comply with ETag-based optimistic concurrency control.
198
- #
199
195
  # @param [String] type The partition type. Currently the only
200
196
  # supported value is "DAY".
201
197
  #
@@ -249,10 +245,6 @@ module Google
249
245
  # the example below. BigQuery does not allow you to change partitioning
250
246
  # on an existing table.
251
247
  #
252
- # If the table is not a full resource representation (see
253
- # {#resource_full?}), the full representation will be retrieved before
254
- # the update to comply with ETag-based optimistic concurrency control.
255
- #
256
248
  # @param [String] field The partition field. The field must be a
257
249
  # top-level TIMESTAMP or DATE field. Its mode must be NULLABLE or
258
250
  # REQUIRED.
@@ -332,6 +324,50 @@ module Google
332
324
  patch_gapi! :time_partitioning
333
325
  end
334
326
 
327
+ ###
328
+ # Checks if the table is clustered.
329
+ #
330
+ # @see https://cloud.google.com/bigquery/docs/clustered-tables
331
+ # Introduction to Clustered Tables
332
+ #
333
+ # @return [Boolean, nil] `true` when the table is clustered, or
334
+ # `false` otherwise, if the object is a resource (see {#resource?});
335
+ # `nil` if the object is a reference (see {#reference?}).
336
+ #
337
+ # @!group Attributes
338
+ #
339
+ def clustering?
340
+ return nil if reference?
341
+ !@gapi.clustering.nil?
342
+ end
343
+
344
+ ###
345
+ # One or more fields on which data should be clustered. Must be
346
+ # specified with time-based partitioning, data in the table will be
347
+ # first partitioned and subsequently clustered. The order of the
348
+ # returned fields determines the sort order of the data.
349
+ #
350
+ # See {Table::Updater#clustering_fields=}.
351
+ #
352
+ # @see https://cloud.google.com/bigquery/docs/partitioned-tables
353
+ # Partitioned Tables
354
+ # @see https://cloud.google.com/bigquery/docs/clustered-tables
355
+ # Introduction to Clustered Tables
356
+ # @see https://cloud.google.com/bigquery/docs/creating-clustered-tables
357
+ # Creating and Using Clustered Tables
358
+ #
359
+ # @return [Array<String>, nil] The clustering fields, or `nil` if the
360
+ # table is not clustered or if the table is a reference (see
361
+ # {#reference?}).
362
+ #
363
+ # @!group Attributes
364
+ #
365
+ def clustering_fields
366
+ return nil if reference?
367
+ ensure_full_data!
368
+ @gapi.clustering.fields if clustering?
369
+ end
370
+
335
371
  ##
336
372
  # The combined Project ID, Dataset ID, and Table ID for this table, in
337
373
  # the format specified by the [Legacy SQL Query
@@ -1137,7 +1173,7 @@ module Google
1137
1173
  options = { token: token, max: max, start: start }
1138
1174
  data_json = service.list_tabledata \
1139
1175
  dataset_id, table_id, options
1140
- Data.from_gapi_json data_json, gapi, service
1176
+ Data.from_gapi_json data_json, gapi, nil, service
1141
1177
  end
1142
1178
 
1143
1179
  ##
@@ -2184,7 +2220,8 @@ module Google
2184
2220
  ##
2185
2221
  # @private New lazy Table object without making an HTTP request.
2186
2222
  def self.new_reference project_id, dataset_id, table_id, service
2187
- # TODO: raise if dataset_id or table_id is nil?
2223
+ raise ArgumentError, "dataset_id is required" unless dataset_id
2224
+ raise ArgumentError, "table_id is required" unless table_id
2188
2225
  new.tap do |b|
2189
2226
  reference_gapi = Google::Apis::BigqueryV2::TableReference.new(
2190
2227
  project_id: project_id,
@@ -2447,6 +2484,56 @@ module Google
2447
2484
  @schema = nil
2448
2485
  end
2449
2486
 
2487
+ ##
2488
+ # Sets one or more fields on which data should be clustered. Must be
2489
+ # specified with time-based partitioning, data in the table will be
2490
+ # first partitioned and subsequently clustered.
2491
+ #
2492
+ # Only top-level, non-repeated, simple-type fields are supported. When
2493
+ # you cluster a table using multiple columns, the order of columns you
2494
+ # specify is important. The order of the specified columns determines
2495
+ # the sort order of the data.
2496
+ #
2497
+ # You can only set the clustering fields while creating a table as in
2498
+ # the example below. BigQuery does not allow you to change clustering
2499
+ # on an existing table.
2500
+ #
2501
+ # See {Table#clustering_fields}.
2502
+ #
2503
+ # @see https://cloud.google.com/bigquery/docs/partitioned-tables
2504
+ # Partitioned Tables
2505
+ # @see https://cloud.google.com/bigquery/docs/clustered-tables
2506
+ # Introduction to Clustered Tables
2507
+ # @see https://cloud.google.com/bigquery/docs/creating-clustered-tables
2508
+ # Creating and Using Clustered Tables
2509
+ #
2510
+ # @param [Array<String>] fields The clustering fields. Only top-level,
2511
+ # non-repeated, simple-type fields are supported.
2512
+ #
2513
+ # @example
2514
+ # require "google/cloud/bigquery"
2515
+ #
2516
+ # bigquery = Google::Cloud::Bigquery.new
2517
+ # dataset = bigquery.dataset "my_dataset"
2518
+ # table = dataset.create_table "my_table" do |table|
2519
+ # table.time_partitioning_type = "DAY"
2520
+ # table.time_partitioning_field = "dob"
2521
+ # table.schema do |schema|
2522
+ # schema.timestamp "dob", mode: :required
2523
+ # schema.string "first_name", mode: :required
2524
+ # schema.string "last_name", mode: :required
2525
+ # end
2526
+ # table.clustering_fields = ["last_name", "first_name"]
2527
+ # end
2528
+ #
2529
+ # @!group Attributes
2530
+ #
2531
+ def clustering_fields= fields
2532
+ @gapi.clustering ||= Google::Apis::BigqueryV2::Clustering.new
2533
+ @gapi.clustering.fields = fields
2534
+ patch_gapi! :clustering
2535
+ end
2536
+
2450
2537
  ##
2451
2538
  # Returns the table's schema. This method can also be used to set,
2452
2539
  # replace, or add to the schema by passing a block. See {Schema} for
@@ -2596,6 +2683,36 @@ module Google
2596
2683
  schema.float name, description: description, mode: mode
2597
2684
  end
2598
2685
 
2686
+ ##
2687
+ # Adds a numeric number field to the schema. Numeric is a
2688
+ # fixed-precision numeric type with 38 decimal digits, 9 that follow
2689
+ # the decimal point.
2690
+ #
2691
+ # See {Schema#numeric}
2692
+ #
2693
+ # @param [String] name The field name. The name must contain only
2694
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
2695
+ # start with a letter or underscore. The maximum length is 128
2696
+ # characters.
2697
+ # @param [String] description A description of the field.
2698
+ # @param [Symbol] mode The field's mode. The possible values are
2699
+ # `:nullable`, `:required`, and `:repeated`. The default value is
2700
+ # `:nullable`.
2701
+ #
2702
+ # @example
2703
+ # require "google/cloud/bigquery"
2704
+ #
2705
+ # bigquery = Google::Cloud::Bigquery.new
2706
+ # dataset = bigquery.dataset "my_dataset"
2707
+ # table = dataset.create_table "my_table" do |schema|
2708
+ # schema.numeric "total_cost", mode: :required
2709
+ # end
2710
+ #
2711
+ # @!group Schema
2712
+ def numeric name, description: nil, mode: :nullable
2713
+ schema.numeric name, description: description, mode: mode
2714
+ end
2715
+
2599
2716
  ##
2600
2717
  # Adds a boolean field to the schema.
2601
2718
  #
@@ -16,7 +16,7 @@
16
16
  module Google
17
17
  module Cloud
18
18
  module Bigquery
19
- VERSION = "1.8.2".freeze
19
+ VERSION = "1.9.0".freeze
20
20
  end
21
21
  end
22
22
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: google-cloud-bigquery
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.2
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Moore
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-09-20 00:00:00.000000000 Z
12
+ date: 2018-10-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: google-cloud-core
@@ -157,14 +157,14 @@ dependencies:
157
157
  requirements:
158
158
  - - "~>"
159
159
  - !ruby/object:Gem::Version
160
- version: 0.50.0
160
+ version: 0.59.2
161
161
  type: :development
162
162
  prerelease: false
163
163
  version_requirements: !ruby/object:Gem::Requirement
164
164
  requirements:
165
165
  - - "~>"
166
166
  - !ruby/object:Gem::Version
167
- version: 0.50.0
167
+ version: 0.59.2
168
168
  - !ruby/object:Gem::Dependency
169
169
  name: simplecov
170
170
  requirement: !ruby/object:Gem::Requirement