google-cloud-bigquery 1.8.2 → 1.9.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.
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