google-cloud-bigquery 1.21.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +16 -0
  3. data/AUTHENTICATION.md +158 -0
  4. data/CHANGELOG.md +397 -0
  5. data/CODE_OF_CONDUCT.md +40 -0
  6. data/CONTRIBUTING.md +188 -0
  7. data/LICENSE +201 -0
  8. data/LOGGING.md +27 -0
  9. data/OVERVIEW.md +463 -0
  10. data/TROUBLESHOOTING.md +31 -0
  11. data/lib/google-cloud-bigquery.rb +139 -0
  12. data/lib/google/cloud/bigquery.rb +145 -0
  13. data/lib/google/cloud/bigquery/argument.rb +197 -0
  14. data/lib/google/cloud/bigquery/convert.rb +383 -0
  15. data/lib/google/cloud/bigquery/copy_job.rb +316 -0
  16. data/lib/google/cloud/bigquery/credentials.rb +50 -0
  17. data/lib/google/cloud/bigquery/data.rb +526 -0
  18. data/lib/google/cloud/bigquery/dataset.rb +2845 -0
  19. data/lib/google/cloud/bigquery/dataset/access.rb +1021 -0
  20. data/lib/google/cloud/bigquery/dataset/list.rb +162 -0
  21. data/lib/google/cloud/bigquery/encryption_configuration.rb +123 -0
  22. data/lib/google/cloud/bigquery/external.rb +2432 -0
  23. data/lib/google/cloud/bigquery/extract_job.rb +368 -0
  24. data/lib/google/cloud/bigquery/insert_response.rb +180 -0
  25. data/lib/google/cloud/bigquery/job.rb +657 -0
  26. data/lib/google/cloud/bigquery/job/list.rb +162 -0
  27. data/lib/google/cloud/bigquery/load_job.rb +1704 -0
  28. data/lib/google/cloud/bigquery/model.rb +740 -0
  29. data/lib/google/cloud/bigquery/model/list.rb +164 -0
  30. data/lib/google/cloud/bigquery/project.rb +1655 -0
  31. data/lib/google/cloud/bigquery/project/list.rb +161 -0
  32. data/lib/google/cloud/bigquery/query_job.rb +1695 -0
  33. data/lib/google/cloud/bigquery/routine.rb +1108 -0
  34. data/lib/google/cloud/bigquery/routine/list.rb +165 -0
  35. data/lib/google/cloud/bigquery/schema.rb +564 -0
  36. data/lib/google/cloud/bigquery/schema/field.rb +668 -0
  37. data/lib/google/cloud/bigquery/service.rb +589 -0
  38. data/lib/google/cloud/bigquery/standard_sql.rb +495 -0
  39. data/lib/google/cloud/bigquery/table.rb +3340 -0
  40. data/lib/google/cloud/bigquery/table/async_inserter.rb +520 -0
  41. data/lib/google/cloud/bigquery/table/list.rb +172 -0
  42. data/lib/google/cloud/bigquery/time.rb +65 -0
  43. data/lib/google/cloud/bigquery/version.rb +22 -0
  44. metadata +297 -0
@@ -0,0 +1,668 @@
1
+ # Copyright 2017 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ module Google
17
+ module Cloud
18
+ module Bigquery
19
+ class Schema
20
+ ##
21
+ # # Schema Field
22
+ #
23
+ # The fields of a table schema.
24
+ #
25
+ # @see https://cloud.google.com/bigquery/docs/loading-data#loading_denormalized_nested_and_repeated_data
26
+ # Loading denormalized, nested, and repeated data
27
+ #
28
+ # @example
29
+ # require "google/cloud/bigquery"
30
+ #
31
+ # bigquery = Google::Cloud::Bigquery.new
32
+ # dataset = bigquery.dataset "my_dataset"
33
+ # table = dataset.table "my_table"
34
+ #
35
+ # field = table.schema.field "name"
36
+ # field.required? #=> true
37
+ #
38
+ class Field
39
+ # @private
40
+ MODES = ["NULLABLE", "REQUIRED", "REPEATED"].freeze
41
+
42
+ # @private
43
+ TYPES = ["STRING", "INTEGER", "INT64", "FLOAT", "FLOAT64", "NUMERIC", "BOOLEAN", "BOOL", "BYTES", "TIMESTAMP",
44
+ "TIME", "DATETIME", "DATE", "RECORD", "STRUCT"].freeze
45
+
46
+ ##
47
+ # The name of the field.
48
+ #
49
+ # @return [String] The field name. The name must contain only
50
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
51
+ # start with a letter or underscore. The maximum length is 128
52
+ # characters.
53
+ #
54
+ def name
55
+ @gapi.name
56
+ end
57
+
58
+ ##
59
+ # Updates the name of the field.
60
+ #
61
+ # @param [String] new_name The field name. The name must contain only
62
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
63
+ # start with a letter or underscore. The maximum length is 128
64
+ # characters.
65
+ #
66
+ def name= new_name
67
+ @gapi.update! name: String(new_name)
68
+ end
69
+
70
+ ##
71
+ # The data type of the field.
72
+ #
73
+ # @return [String] The field data type. Possible values include
74
+ # `STRING`, `BYTES`, `INTEGER`, `INT64` (same as `INTEGER`),
75
+ # `FLOAT`, `FLOAT64` (same as `FLOAT`), `NUMERIC`, `BOOLEAN`, `BOOL`
76
+ # (same as `BOOLEAN`), `TIMESTAMP`, `DATE`, `TIME`, `DATETIME`,
77
+ # `RECORD` (where `RECORD` indicates that the field contains a
78
+ # nested schema) or `STRUCT` (same as `RECORD`).
79
+ #
80
+ def type
81
+ @gapi.type
82
+ end
83
+
84
+ ##
85
+ # Updates the data type of the field.
86
+ #
87
+ # @param [String] new_type The data type. Possible values include
88
+ # `STRING`, `BYTES`, `INTEGER`, `INT64` (same as `INTEGER`),
89
+ # `FLOAT`, `FLOAT64` (same as `FLOAT`), `NUMERIC`, `BOOLEAN`, `BOOL`
90
+ # (same as `BOOLEAN`), `TIMESTAMP`, `DATE`, `TIME`, `DATETIME`,
91
+ # `RECORD` (where `RECORD` indicates that the field contains a
92
+ # nested schema) or `STRUCT` (same as `RECORD`).
93
+ #
94
+ def type= new_type
95
+ @gapi.update! type: verify_type(new_type)
96
+ end
97
+
98
+ ##
99
+ # Checks if the type of the field is `NULLABLE`.
100
+ #
101
+ # @return [Boolean] `true` when `NULLABLE`, `false` otherwise.
102
+ #
103
+ def nullable?
104
+ mode == "NULLABLE"
105
+ end
106
+
107
+ ##
108
+ # Checks if the type of the field is `REQUIRED`.
109
+ #
110
+ # @return [Boolean] `true` when `REQUIRED`, `false` otherwise.
111
+ #
112
+ def required?
113
+ mode == "REQUIRED"
114
+ end
115
+
116
+ ##
117
+ # Checks if the type of the field is `REPEATED`.
118
+ #
119
+ # @return [Boolean] `true` when `REPEATED`, `false` otherwise.
120
+ #
121
+ def repeated?
122
+ mode == "REPEATED"
123
+ end
124
+
125
+ ##
126
+ # The description of the field.
127
+ #
128
+ # @return [String] The field description. The maximum length is 1,024
129
+ # characters.
130
+ #
131
+ def description
132
+ @gapi.description
133
+ end
134
+
135
+ ##
136
+ # Updates the description of the field.
137
+ #
138
+ # @param [String] new_description The field description. The maximum
139
+ # length is 1,024 characters.
140
+ #
141
+ def description= new_description
142
+ @gapi.update! description: new_description
143
+ end
144
+
145
+ ##
146
+ # The mode of the field.
147
+ #
148
+ # @return [String] The field mode. Possible values include `NULLABLE`,
149
+ # `REQUIRED` and `REPEATED`. The default value is `NULLABLE`.
150
+ #
151
+ def mode
152
+ @gapi.mode
153
+ end
154
+
155
+ ##
156
+ # Updates the mode of the field.
157
+ #
158
+ # @param [String] new_mode The field mode. Possible values include
159
+ # `NULLABLE`, `REQUIRED` and `REPEATED`. The default value is
160
+ # `NULLABLE`.
161
+ #
162
+ def mode= new_mode
163
+ @gapi.update! mode: verify_mode(new_mode)
164
+ end
165
+
166
+ ##
167
+ # Checks if the type of the field is `STRING`.
168
+ #
169
+ # @return [Boolean] `true` when `STRING`, `false` otherwise.
170
+ #
171
+ def string?
172
+ type == "STRING"
173
+ end
174
+
175
+ ##
176
+ # Checks if the type of the field is `INTEGER`.
177
+ #
178
+ # @return [Boolean] `true` when `INTEGER`, `false` otherwise.
179
+ #
180
+ def integer?
181
+ type == "INTEGER" || type == "INT64"
182
+ end
183
+
184
+ ##
185
+ # Checks if the type of the field is `FLOAT`.
186
+ #
187
+ # @return [Boolean] `true` when `FLOAT`, `false` otherwise.
188
+ #
189
+ def float?
190
+ type == "FLOAT" || type == "FLOAT64"
191
+ end
192
+
193
+ ##
194
+ # Checks if the type of the field is `NUMERIC`.
195
+ #
196
+ # @return [Boolean] `true` when `NUMERIC`, `false` otherwise.
197
+ #
198
+ def numeric?
199
+ type == "NUMERIC"
200
+ end
201
+
202
+ ##
203
+ # Checks if the type of the field is `BOOLEAN`.
204
+ #
205
+ # @return [Boolean] `true` when `BOOLEAN`, `false` otherwise.
206
+ #
207
+ def boolean?
208
+ type == "BOOLEAN" || type == "BOOL"
209
+ end
210
+
211
+ ##
212
+ # Checks if the type of the field is `BYTES`.
213
+ #
214
+ # @return [Boolean] `true` when `BYTES`, `false` otherwise.
215
+ #
216
+ def bytes?
217
+ type == "BYTES"
218
+ end
219
+
220
+ ##
221
+ # Checks if the type of the field is `TIMESTAMP`.
222
+ #
223
+ # @return [Boolean] `true` when `TIMESTAMP`, `false` otherwise.
224
+ #
225
+ def timestamp?
226
+ type == "TIMESTAMP"
227
+ end
228
+
229
+ ##
230
+ # Checks if the type of the field is `TIME`.
231
+ #
232
+ # @return [Boolean] `true` when `TIME`, `false` otherwise.
233
+ #
234
+ def time?
235
+ type == "TIME"
236
+ end
237
+
238
+ ##
239
+ # Checks if the type of the field is `DATETIME`.
240
+ #
241
+ # @return [Boolean] `true` when `DATETIME`, `false` otherwise.
242
+ #
243
+ def datetime?
244
+ type == "DATETIME"
245
+ end
246
+
247
+ ##
248
+ # Checks if the type of the field is `DATE`.
249
+ #
250
+ # @return [Boolean] `true` when `DATE`, `false` otherwise.
251
+ #
252
+ def date?
253
+ type == "DATE"
254
+ end
255
+
256
+ ##
257
+ # Checks if the type of the field is `RECORD`.
258
+ #
259
+ # @return [Boolean] `true` when `RECORD`, `false` otherwise.
260
+ #
261
+ def record?
262
+ type == "RECORD" || type == "STRUCT"
263
+ end
264
+ alias struct? record?
265
+
266
+ ##
267
+ # The nested fields if the type property is set to `RECORD`. Will be
268
+ # empty otherwise.
269
+ #
270
+ # @return [Array<Field>, nil] The nested schema fields if the type
271
+ # is set to `RECORD`.
272
+ #
273
+ def fields
274
+ if frozen?
275
+ Array(@gapi.fields).map { |f| Field.from_gapi(f).freeze }.freeze
276
+ else
277
+ Array(@gapi.fields).map { |f| Field.from_gapi f }
278
+ end
279
+ end
280
+
281
+ ##
282
+ # The names of the nested fields as symbols if the type property is
283
+ # set to `RECORD`. Will be empty otherwise.
284
+ #
285
+ # @return [Array<Symbol>, nil] The names of the nested schema fields
286
+ # if the type is set to `RECORD`.
287
+ #
288
+ def headers
289
+ fields.map(&:name).map(&:to_sym)
290
+ end
291
+
292
+ ##
293
+ # The types of the field, using the same format as the optional query
294
+ # parameter types.
295
+ #
296
+ # The parameter types are one of the following BigQuery type codes:
297
+ #
298
+ # * `:BOOL`
299
+ # * `:INT64`
300
+ # * `:FLOAT64`
301
+ # * `:NUMERIC`
302
+ # * `:STRING`
303
+ # * `:DATETIME`
304
+ # * `:DATE`
305
+ # * `:TIMESTAMP`
306
+ # * `:TIME`
307
+ # * `:BYTES`
308
+ # * `Array` - Lists are specified by providing the type code in an array. For example, an array of integers
309
+ # are specified as `[:INT64]`.
310
+ # * `Hash` - Types for STRUCT values (`Hash` objects) are specified using a `Hash` object, where the keys
311
+ # are the nested field names, and the values are the the nested field types.
312
+ #
313
+ # @return [Symbol, Array, Hash] The type.
314
+ #
315
+ def param_type
316
+ param_type = type.to_sym
317
+ param_type = Hash[fields.map { |field| [field.name.to_sym, field.param_type] }] if record?
318
+ param_type = [param_type] if repeated?
319
+ param_type
320
+ end
321
+
322
+ ##
323
+ # Retrieve a nested field by name, if the type property is
324
+ # set to `RECORD`. Will return `nil` otherwise.
325
+ #
326
+ # @return [Field, nil] The nested schema field object, or `nil`.
327
+ #
328
+ def field name
329
+ f = fields.find { |fld| fld.name == name.to_s }
330
+ return nil if f.nil?
331
+ yield f if block_given?
332
+ f
333
+ end
334
+
335
+ ##
336
+ # Adds a string field to the nested schema of a record field.
337
+ #
338
+ # This can only be called on fields that are of type `RECORD`.
339
+ #
340
+ # @param [String] name The field name. The name must contain only
341
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
342
+ # start with a letter or underscore. The maximum length is 128
343
+ # characters.
344
+ # @param [String] description A description of the field.
345
+ # @param [Symbol] mode The field's mode. The possible values are
346
+ # `:nullable`, `:required`, and `:repeated`. The default value is
347
+ # `:nullable`.
348
+ #
349
+ def string name, description: nil, mode: :nullable
350
+ record_check!
351
+
352
+ add_field name, :string, description: description, mode: mode
353
+ end
354
+
355
+ ##
356
+ # Adds an integer field to the nested schema of a record field.
357
+ #
358
+ # This can only be called on fields that are of type `RECORD`.
359
+ #
360
+ # @param [String] name The field name. The name must contain only
361
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
362
+ # start with a letter or underscore. The maximum length is 128
363
+ # characters.
364
+ # @param [String] description A description of the field.
365
+ # @param [Symbol] mode The field's mode. The possible values are
366
+ # `:nullable`, `:required`, and `:repeated`. The default value is
367
+ # `:nullable`.
368
+ #
369
+ def integer name, description: nil, mode: :nullable
370
+ record_check!
371
+
372
+ add_field name, :integer, description: description, mode: mode
373
+ end
374
+
375
+ ##
376
+ # Adds a floating-point number field to the nested schema of a record
377
+ # field.
378
+ #
379
+ # This can only be called on fields that are of type `RECORD`.
380
+ #
381
+ # @param [String] name The field name. The name must contain only
382
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
383
+ # start with a letter or underscore. The maximum length is 128
384
+ # characters.
385
+ # @param [String] description A description of the field.
386
+ # @param [Symbol] mode The field's mode. The possible values are
387
+ # `:nullable`, `:required`, and `:repeated`. The default value is
388
+ # `:nullable`.
389
+ #
390
+ def float name, description: nil, mode: :nullable
391
+ record_check!
392
+
393
+ add_field name, :float, description: description, mode: mode
394
+ end
395
+
396
+ ##
397
+ # Adds a numeric number field to the schema. Numeric is a
398
+ # fixed-precision numeric type with 38 decimal digits, 9 that follow
399
+ # the decimal point.
400
+ #
401
+ # This can only be called on fields that are of type `RECORD`.
402
+ #
403
+ # @param [String] name The field name. The name must contain only
404
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
405
+ # start with a letter or underscore. The maximum length is 128
406
+ # characters.
407
+ # @param [String] description A description of the field.
408
+ # @param [Symbol] mode The field's mode. The possible values are
409
+ # `:nullable`, `:required`, and `:repeated`. The default value is
410
+ # `:nullable`.
411
+ #
412
+ def numeric name, description: nil, mode: :nullable
413
+ record_check!
414
+
415
+ add_field name, :numeric, description: description, mode: mode
416
+ end
417
+
418
+ ##
419
+ # Adds a boolean field to the nested schema of a record field.
420
+ #
421
+ # This can only be called on fields that are of type `RECORD`.
422
+ #
423
+ # @param [String] name The field name. The name must contain only
424
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
425
+ # start with a letter or underscore. The maximum length is 128
426
+ # characters.
427
+ # @param [String] description A description of the field.
428
+ # @param [Symbol] mode The field's mode. The possible values are
429
+ # `:nullable`, `:required`, and `:repeated`. The default value is
430
+ # `:nullable`.
431
+ #
432
+ def boolean name, description: nil, mode: :nullable
433
+ record_check!
434
+
435
+ add_field name, :boolean, description: description, mode: mode
436
+ end
437
+
438
+ ##
439
+ # Adds a bytes field to the nested schema of a record field.
440
+ #
441
+ # This can only be called on fields that are of type `RECORD`.
442
+ #
443
+ # @param [String] name The field name. The name must contain only
444
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
445
+ # start with a letter or underscore. The maximum length is 128
446
+ # characters.
447
+ # @param [String] description A description of the field.
448
+ # @param [Symbol] mode The field's mode. The possible values are
449
+ # `:nullable`, `:required`, and `:repeated`. The default value is
450
+ # `:nullable`.
451
+ #
452
+ def bytes name, description: nil, mode: :nullable
453
+ record_check!
454
+
455
+ add_field name, :bytes, description: description, mode: mode
456
+ end
457
+
458
+ ##
459
+ # Adds a timestamp field to the nested schema of a record field.
460
+ #
461
+ # This can only be called on fields that are of type `RECORD`.
462
+ #
463
+ # @param [String] name The field name. The name must contain only
464
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
465
+ # start with a letter or underscore. The maximum length is 128
466
+ # characters.
467
+ # @param [String] description A description of the field.
468
+ # @param [Symbol] mode The field's mode. The possible values are
469
+ # `:nullable`, `:required`, and `:repeated`. The default value is
470
+ # `:nullable`.
471
+ #
472
+ def timestamp name, description: nil, mode: :nullable
473
+ record_check!
474
+
475
+ add_field name, :timestamp, description: description, mode: mode
476
+ end
477
+
478
+ ##
479
+ # Adds a time field to the nested schema of a record field.
480
+ #
481
+ # This can only be called on fields that are of type `RECORD`.
482
+ #
483
+ # @param [String] name The field name. The name must contain only
484
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
485
+ # start with a letter or underscore. The maximum length is 128
486
+ # characters.
487
+ # @param [String] description A description of the field.
488
+ # @param [Symbol] mode The field's mode. The possible values are
489
+ # `:nullable`, `:required`, and `:repeated`. The default value is
490
+ # `:nullable`.
491
+ #
492
+ def time name, description: nil, mode: :nullable
493
+ record_check!
494
+
495
+ add_field name, :time, description: description, mode: mode
496
+ end
497
+
498
+ ##
499
+ # Adds a datetime field to the nested schema of a record field.
500
+ #
501
+ # This can only be called on fields that are of type `RECORD`.
502
+ #
503
+ # @param [String] name The field name. The name must contain only
504
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
505
+ # start with a letter or underscore. The maximum length is 128
506
+ # characters.
507
+ # @param [String] description A description of the field.
508
+ # @param [Symbol] mode The field's mode. The possible values are
509
+ # `:nullable`, `:required`, and `:repeated`. The default value is
510
+ # `:nullable`.
511
+ #
512
+ def datetime name, description: nil, mode: :nullable
513
+ record_check!
514
+
515
+ add_field name, :datetime, description: description, mode: mode
516
+ end
517
+
518
+ ##
519
+ # Adds a date field to the nested schema of a record field.
520
+ #
521
+ # This can only be called on fields that are of type `RECORD`.
522
+ #
523
+ # @param [String] name The field name. The name must contain only
524
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
525
+ # start with a letter or underscore. The maximum length is 128
526
+ # characters.
527
+ # @param [String] description A description of the field.
528
+ # @param [Symbol] mode The field's mode. The possible values are
529
+ # `:nullable`, `:required`, and `:repeated`. The default value is
530
+ # `:nullable`.
531
+ #
532
+ def date name, description: nil, mode: :nullable
533
+ record_check!
534
+
535
+ add_field name, :date, description: description, mode: mode
536
+ end
537
+
538
+ ##
539
+ # Adds a record field to the nested schema of a record field. A block
540
+ # must be passed describing the nested fields of the record. For more
541
+ # information about nested and repeated records, see [Preparing Data
542
+ # for BigQuery](https://cloud.google.com/bigquery/docs/loading-data#loading_denormalized_nested_and_repeated_data).
543
+ #
544
+ # This can only be called on fields that are of type `RECORD`.
545
+ #
546
+ # @param [String] name The field name. The name must contain only
547
+ # letters (a-z, A-Z), numbers (0-9), or underscores (_), and must
548
+ # start with a letter or underscore. The maximum length is 128
549
+ # characters.
550
+ # @param [String] description A description of the field.
551
+ # @param [Symbol] mode The field's mode. The possible values are
552
+ # `:nullable`, `:required`, and `:repeated`. The default value is
553
+ # `:nullable`.
554
+ # @yield [nested_schema] a block for setting the nested schema
555
+ # @yieldparam [Schema] nested_schema the object accepting the
556
+ # nested schema
557
+ #
558
+ # @example
559
+ # require "google/cloud/bigquery"
560
+ #
561
+ # bigquery = Google::Cloud::Bigquery.new
562
+ # dataset = bigquery.dataset "my_dataset"
563
+ # table = dataset.create_table "my_table"
564
+ #
565
+ # table.schema do |schema|
566
+ # schema.string "first_name", mode: :required
567
+ # schema.record "cities_lived", mode: :repeated do |cities_lived|
568
+ # cities_lived.record "city", mode: :required do |city|
569
+ # city.string "name", mode: :required
570
+ # city.string "country", mode: :required
571
+ # end
572
+ # cities_lived.integer "number_of_years", mode: :required
573
+ # end
574
+ # end
575
+ #
576
+ def record name, description: nil, mode: nil
577
+ record_check!
578
+
579
+ # TODO: do we need to raise if no block was given?
580
+ raise ArgumentError, "a block is required" unless block_given?
581
+
582
+ nested_field = add_field name, :record, description: description, mode: mode
583
+ yield nested_field
584
+ nested_field
585
+ end
586
+
587
+ # @private
588
+ def self.from_gapi gapi
589
+ new.tap do |f|
590
+ f.instance_variable_set :@gapi, gapi
591
+ f.instance_variable_set :@original_json, gapi.to_json
592
+ end
593
+ end
594
+
595
+ # @private
596
+ def to_gapi
597
+ @gapi
598
+ end
599
+
600
+ # @private
601
+ def == other
602
+ return false unless other.is_a? Field
603
+ to_gapi.to_h == other.to_gapi.to_h
604
+ end
605
+
606
+ # @private
607
+ def to_hash
608
+ h = {
609
+ name: name,
610
+ type: type,
611
+ mode: mode
612
+ }
613
+ h[:description] = description if description
614
+ h[:fields] = fields.map(&:to_hash) if record?
615
+ h
616
+ end
617
+
618
+ protected
619
+
620
+ def frozen_check!
621
+ return unless frozen?
622
+ raise ArgumentError, "Cannot modify a frozen field"
623
+ end
624
+
625
+ def record_check!
626
+ return unless type != "RECORD"
627
+ raise ArgumentError,
628
+ "Cannot add fields to a non-RECORD field (#{type})"
629
+ end
630
+
631
+ def add_field name, type, description: nil, mode: :nullable
632
+ frozen_check!
633
+
634
+ new_gapi = Google::Apis::BigqueryV2::TableFieldSchema.new(
635
+ name: String(name),
636
+ type: verify_type(type),
637
+ description: description,
638
+ mode: verify_mode(mode),
639
+ fields: []
640
+ )
641
+
642
+ # Remove any existing field of this name
643
+ @gapi.fields ||= []
644
+ @gapi.fields.reject! { |f| f.name == new_gapi.name }
645
+ # Add to the nested fields
646
+ @gapi.fields << new_gapi
647
+
648
+ # return the public API object
649
+ Field.from_gapi new_gapi
650
+ end
651
+
652
+ def verify_type type
653
+ type = type.to_s.upcase
654
+ raise ArgumentError, "Type '#{type}' not found" unless TYPES.include? type
655
+ type
656
+ end
657
+
658
+ def verify_mode mode
659
+ mode = :nullable if mode.nil?
660
+ mode = mode.to_s.upcase
661
+ raise ArgumentError "Unable to determine mode for '#{mode}'" unless MODES.include? mode
662
+ mode
663
+ end
664
+ end
665
+ end
666
+ end
667
+ end
668
+ end