google-cloud-bigquery 1.18.0 → 1.21.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1108 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ # require "google/cloud/errors"
17
+ require "google/cloud/bigquery/convert"
18
+ require "google/cloud/bigquery/service"
19
+ require "google/cloud/bigquery/routine/list"
20
+ require "google/cloud/bigquery/argument"
21
+
22
+ module Google
23
+ module Cloud
24
+ module Bigquery
25
+ ##
26
+ # # Routine
27
+ #
28
+ # A user-defined function or a stored procedure.
29
+ #
30
+ # @example Creating a new routine:
31
+ # require "google/cloud/bigquery"
32
+ #
33
+ # bigquery = Google::Cloud::Bigquery.new
34
+ # dataset = bigquery.dataset "my_dataset"
35
+ #
36
+ # routine = dataset.create_routine "my_routine" do |r|
37
+ # r.routine_type = "SCALAR_FUNCTION"
38
+ # r.language = "SQL"
39
+ # r.arguments = [
40
+ # Google::Cloud::Bigquery::Argument.new(name: "x", data_type: "INT64")
41
+ # ]
42
+ # r.body = "x * 3"
43
+ # r.description = "My routine description"
44
+ # end
45
+ #
46
+ # puts routine.routine_id
47
+ #
48
+ # @example Extended example:
49
+ # require "google/cloud/bigquery"
50
+ #
51
+ # bigquery = Google::Cloud::Bigquery.new
52
+ # dataset = bigquery.dataset "my_dataset"
53
+ # routine = dataset.create_routine "my_routine" do |r|
54
+ # r.routine_type = "SCALAR_FUNCTION"
55
+ # r.language = :SQL
56
+ # r.body = "(SELECT SUM(IF(elem.name = \"foo\",elem.val,null)) FROM UNNEST(arr) AS elem)"
57
+ # r.arguments = [
58
+ # Google::Cloud::Bigquery::Argument.new(
59
+ # name: "arr",
60
+ # argument_kind: "FIXED_TYPE",
61
+ # data_type: Google::Cloud::Bigquery::StandardSql::DataType.new(
62
+ # type_kind: "ARRAY",
63
+ # array_element_type: Google::Cloud::Bigquery::StandardSql::DataType.new(
64
+ # type_kind: "STRUCT",
65
+ # struct_type: Google::Cloud::Bigquery::StandardSql::StructType.new(
66
+ # fields: [
67
+ # Google::Cloud::Bigquery::StandardSql::Field.new(
68
+ # name: "name",
69
+ # type: Google::Cloud::Bigquery::StandardSql::DataType.new(type_kind: "STRING")
70
+ # ),
71
+ # Google::Cloud::Bigquery::StandardSql::Field.new(
72
+ # name: "val",
73
+ # type: Google::Cloud::Bigquery::StandardSql::DataType.new(type_kind: "INT64")
74
+ # )
75
+ # ]
76
+ # )
77
+ # )
78
+ # )
79
+ # )
80
+ # ]
81
+ # end
82
+ #
83
+ # @example Retrieving and updating an existing routine:
84
+ # require "google/cloud/bigquery"
85
+ #
86
+ # bigquery = Google::Cloud::Bigquery.new
87
+ # dataset = bigquery.dataset "my_dataset"
88
+ # routine = dataset.routine "my_routine"
89
+ #
90
+ # routine.update do |r|
91
+ # r.body = "x * 4"
92
+ # r.description = "My new routine description"
93
+ # end
94
+ #
95
+ class Routine
96
+ ##
97
+ # @private The Service object.
98
+ attr_accessor :service
99
+
100
+ ##
101
+ # @private The Google API Client object.
102
+ attr_accessor :gapi
103
+
104
+ ##
105
+ # @private A Google API Client Dataset Reference object.
106
+ attr_reader :reference
107
+
108
+ ##
109
+ # @private Creates an empty Routine object.
110
+ def initialize
111
+ @service = nil
112
+ @gapi = nil
113
+ @reference = nil
114
+ end
115
+
116
+ ##
117
+ # A unique ID for this routine, without the project name.
118
+ #
119
+ # @return [String] The ID must contain only letters (a-z, A-Z), numbers (0-9), or underscores (_). The maximum
120
+ # length is 256 characters.
121
+ #
122
+ # @!group Attributes
123
+ #
124
+ def routine_id
125
+ return reference.routine_id if reference?
126
+ @gapi.routine_reference.routine_id
127
+ end
128
+
129
+ ##
130
+ # The ID of the dataset containing this routine.
131
+ #
132
+ # @return [String] The dataset ID.
133
+ #
134
+ # @!group Attributes
135
+ #
136
+ def dataset_id
137
+ return reference.dataset_id if reference?
138
+ @gapi.routine_reference.dataset_id
139
+ end
140
+
141
+ ##
142
+ # The ID of the project containing this routine.
143
+ #
144
+ # @return [String] The project ID.
145
+ #
146
+ # @!group Attributes
147
+ #
148
+ def project_id
149
+ return reference.project_id if reference?
150
+ @gapi.routine_reference.project_id
151
+ end
152
+
153
+ ##
154
+ # @private The gapi fragment containing the Project ID, Dataset ID, and Routine ID.
155
+ #
156
+ # @return [Google::Apis::BigqueryV2::RoutineReference]
157
+ #
158
+ def routine_ref
159
+ reference? ? reference : @gapi.routine_reference
160
+ end
161
+
162
+ ##
163
+ # The ETag hash of the routine.
164
+ #
165
+ # @return [String, nil] The ETag hash, or `nil` if the object is a reference (see {#reference?}).
166
+ #
167
+ # @example
168
+ # require "google/cloud/bigquery"
169
+ #
170
+ # bigquery = Google::Cloud::Bigquery.new
171
+ # dataset = bigquery.dataset "my_dataset"
172
+ # routine = dataset.routine "my_routine"
173
+ #
174
+ # routine.etag # "etag123456789"
175
+ #
176
+ # @!group Attributes
177
+ #
178
+ def etag
179
+ return nil if reference?
180
+ @gapi.etag
181
+ end
182
+
183
+ ##
184
+ # The type of routine. Required.
185
+ #
186
+ # * `SCALAR_FUNCTION` - Non-builtin permanent scalar function.
187
+ # * `PROCEDURE` - Stored procedure.
188
+ #
189
+ # @return [String, nil] The type of routine in upper case, or `nil` if the object is a reference (see
190
+ # {#reference?}).
191
+ #
192
+ # @!group Attributes
193
+ #
194
+ def routine_type
195
+ return nil if reference?
196
+ @gapi.routine_type
197
+ end
198
+
199
+ ##
200
+ # Updates the type of routine. Required.
201
+ #
202
+ # * `SCALAR_FUNCTION` - Non-builtin permanent scalar function.
203
+ # * `PROCEDURE` - Stored procedure.
204
+ #
205
+ # @param [String] new_routine_type The new type of the routine in upper case.
206
+ #
207
+ # @!group Attributes
208
+ #
209
+ def routine_type= new_routine_type
210
+ ensure_full_data!
211
+ @gapi.routine_type = new_routine_type
212
+ update_gapi!
213
+ end
214
+
215
+ ##
216
+ # Checks if the value of {#routine_type} is `PROCEDURE`. The default is `false`.
217
+ #
218
+ # @return [Boolean] `true` when `PROCEDURE` and the object is not a reference (see {#reference?}), `false`
219
+ # otherwise.
220
+ #
221
+ # @!group Attributes
222
+ #
223
+ def procedure?
224
+ @gapi.routine_type == "PROCEDURE"
225
+ end
226
+
227
+ ##
228
+ # Checks if the value of {#routine_type} is `SCALAR_FUNCTION`. The default is `true`.
229
+ #
230
+ # @return [Boolean] `true` when `SCALAR_FUNCTION` and the object is not a reference (see {#reference?}), `false`
231
+ # otherwise.
232
+ #
233
+ # @!group Attributes
234
+ #
235
+ def scalar_function?
236
+ @gapi.routine_type == "SCALAR_FUNCTION"
237
+ end
238
+
239
+ ##
240
+ # The time when this routine was created.
241
+ #
242
+ # @return [Time, nil] The creation time, or `nil` if the object is a reference (see {#reference?}).
243
+ #
244
+ # @!group Attributes
245
+ #
246
+ def created_at
247
+ return nil if reference?
248
+ Convert.millis_to_time @gapi.creation_time
249
+ end
250
+
251
+ ##
252
+ # The time when this routine was last modified.
253
+ #
254
+ # @return [Time, nil] The last modified time, or `nil` if the object is a reference (see {#reference?}).
255
+ #
256
+ # @!group Attributes
257
+ #
258
+ def modified_at
259
+ return nil if reference?
260
+ Convert.millis_to_time @gapi.last_modified_time
261
+ end
262
+
263
+ ##
264
+ # The programming language of routine. Optional. Defaults to "SQL".
265
+ #
266
+ # * `SQL` - SQL language.
267
+ # * `JAVASCRIPT` - JavaScript language.
268
+ #
269
+ # @return [String, nil] The language in upper case, or `nil` if the object is a reference (see {#reference?}).
270
+ #
271
+ # @!group Attributes
272
+ #
273
+ def language
274
+ return nil if reference?
275
+ @gapi.language
276
+ end
277
+
278
+ ##
279
+ # Updates the programming language of routine. Optional. Defaults to "SQL".
280
+ #
281
+ # * `SQL` - SQL language.
282
+ # * `JAVASCRIPT` - JavaScript language.
283
+ #
284
+ # @param [String] new_language The new language in upper case.
285
+ #
286
+ # @!group Attributes
287
+ #
288
+ def language= new_language
289
+ ensure_full_data!
290
+ @gapi.language = new_language
291
+ update_gapi!
292
+ end
293
+
294
+ ##
295
+ # Checks if the value of {#language} is `JAVASCRIPT`. The default is `false`.
296
+ #
297
+ # @return [Boolean] `true` when `JAVASCRIPT` and the object is not a reference (see {#reference?}), `false`
298
+ # otherwise.
299
+ #
300
+ # @!group Attributes
301
+ #
302
+ def javascript?
303
+ @gapi.language == "JAVASCRIPT"
304
+ end
305
+
306
+ ##
307
+ # Checks if the value of {#language} is `SQL`. The default is `true`.
308
+ #
309
+ # @return [Boolean] `true` when `SQL` and the object is not a reference (see {#reference?}), `false`
310
+ # otherwise.
311
+ #
312
+ # @!group Attributes
313
+ #
314
+ def sql?
315
+ return true if @gapi.language.nil?
316
+ @gapi.language == "SQL"
317
+ end
318
+
319
+ ##
320
+ # The input/output arguments of the routine. Optional.
321
+ #
322
+ # @return [Array<Argument>, nil] An array of argument objects, or `nil` if the object is a reference (see
323
+ # {#reference?}).
324
+ #
325
+ # @example
326
+ # require "google/cloud/bigquery"
327
+ #
328
+ # bigquery = Google::Cloud::Bigquery.new
329
+ # dataset = bigquery.dataset "my_dataset"
330
+ # routine = dataset.routine "my_routine"
331
+ #
332
+ # puts "#{routine.routine_id} arguments:"
333
+ # routine.arguments.each do |arguments|
334
+ # puts "* #{arguments.name}"
335
+ # end
336
+ #
337
+ # @!group Attributes
338
+ #
339
+ def arguments
340
+ return nil if reference?
341
+ ensure_full_data!
342
+ # always return frozen arguments
343
+ Array(@gapi.arguments).map { |a| Argument.from_gapi a }.freeze
344
+ end
345
+
346
+ ##
347
+ # Updates the input/output arguments of the routine. Optional.
348
+ #
349
+ # @param [Array<Argument>] new_arguments The new arguments.
350
+ #
351
+ # @example
352
+ # require "google/cloud/bigquery"
353
+ #
354
+ # bigquery = Google::Cloud::Bigquery.new
355
+ # dataset = bigquery.dataset "my_dataset"
356
+ # routine = dataset.routine "my_routine"
357
+ #
358
+ # routine.arguments = [
359
+ # Google::Cloud::Bigquery::Argument.new(name: "x", data_type: "INT64")
360
+ # ]
361
+ #
362
+ # @!group Attributes
363
+ #
364
+ def arguments= new_arguments
365
+ ensure_full_data!
366
+ @gapi.update! arguments: new_arguments.map(&:to_gapi)
367
+ update_gapi!
368
+ end
369
+
370
+ ##
371
+ # The return type of the routine. Optional if the routine is a SQL function ({#sql?}); required otherwise.
372
+ #
373
+ # If absent, the return type is inferred from {#body} at query time in each query that references this routine.
374
+ # If present, then the evaluated result will be cast to the specified returned type at query time.
375
+ #
376
+ # For example, for the functions created with the following statements:
377
+ #
378
+ # * `CREATE FUNCTION Add(x FLOAT64, y FLOAT64) RETURNS FLOAT64 AS (x + y);`
379
+ # * `CREATE FUNCTION Increment(x FLOAT64) AS (Add(x, 1));`
380
+ # * `CREATE FUNCTION Decrement(x FLOAT64) RETURNS FLOAT64 AS (Add(x, -1));`
381
+ #
382
+ # The returnType is `{typeKind: "FLOAT64"}` for Add and Decrement, and is absent for Increment (inferred as
383
+ # `FLOAT64` at query time).
384
+ #
385
+ # Suppose the function Add is replaced by `CREATE OR REPLACE FUNCTION Add(x INT64, y INT64) AS (x + y);`
386
+ #
387
+ # Then the inferred return type of Increment is automatically changed to `INT64` at query time, while the return
388
+ # type of Decrement remains `FLOAT64`.
389
+ #
390
+ # @return [Google::Cloud::Bigquery::StandardSql::DataType, nil] The return type in upper case, or `nil` if the
391
+ # object is a reference (see {#reference?}).
392
+ #
393
+ # @example
394
+ # require "google/cloud/bigquery"
395
+ #
396
+ # bigquery = Google::Cloud::Bigquery.new
397
+ # dataset = bigquery.dataset "my_dataset"
398
+ # routine = dataset.routine "my_routine"
399
+ #
400
+ # routine.return_type.type_kind #=> "INT64"
401
+ #
402
+ # @!group Attributes
403
+ #
404
+ def return_type
405
+ return nil if reference?
406
+ ensure_full_data!
407
+ return nil unless @gapi.return_type
408
+ StandardSql::DataType.from_gapi @gapi.return_type
409
+ end
410
+
411
+ ##
412
+ # Updates the return type of the routine. Optional if the routine is a SQL function ({#sql?}); required
413
+ # otherwise.
414
+ #
415
+ # If absent, the return type is inferred from {#body} at query time in each query that references this routine.
416
+ # If present, then the evaluated result will be cast to the specified returned type at query time.
417
+ #
418
+ # For example, for the functions created with the following statements:
419
+ #
420
+ # * `CREATE FUNCTION Add(x FLOAT64, y FLOAT64) RETURNS FLOAT64 AS (x + y);`
421
+ # * `CREATE FUNCTION Increment(x FLOAT64) AS (Add(x, 1));`
422
+ # * `CREATE FUNCTION Decrement(x FLOAT64) RETURNS FLOAT64 AS (Add(x, -1));`
423
+ #
424
+ # The returnType is `{typeKind: "FLOAT64"}` for Add and Decrement, and is absent for Increment (inferred as
425
+ # `FLOAT64` at query time).
426
+ #
427
+ # Suppose the function Add is replaced by `CREATE OR REPLACE FUNCTION Add(x INT64, y INT64) AS (x + y);`
428
+ #
429
+ # Then the inferred return type of Increment is automatically changed to `INT64` at query time, while the return
430
+ # type of Decrement remains `FLOAT64`.
431
+ #
432
+ # @param [Google::Cloud::Bigquery::StandardSql::DataType, String] new_return_type The new return type for the
433
+ # routine.
434
+ #
435
+ # @example
436
+ # require "google/cloud/bigquery"
437
+ #
438
+ # bigquery = Google::Cloud::Bigquery.new
439
+ # dataset = bigquery.dataset "my_dataset"
440
+ # routine = dataset.routine "my_routine"
441
+ #
442
+ # routine.return_type.type_kind #=> "INT64"
443
+ # routine.return_type = "STRING"
444
+ #
445
+ # @!group Attributes
446
+ #
447
+ def return_type= new_return_type
448
+ ensure_full_data!
449
+ @gapi.return_type = StandardSql::DataType.gapi_from_string_or_data_type new_return_type
450
+ update_gapi!
451
+ end
452
+
453
+ ##
454
+ # The list of the Google Cloud Storage URIs of imported JavaScript libraries. Optional. Only used if
455
+ # {#language} is `JAVASCRIPT` ({#javascript?}).
456
+ #
457
+ # @return [Array<String>, nil] A frozen array of Google Cloud Storage URIs, e.g.
458
+ # `["gs://cloud-samples-data/bigquery/udfs/max-value.js"]`, or `nil` if the object is a reference (see
459
+ # {#reference?}).
460
+ #
461
+ # @!group Attributes
462
+ #
463
+ def imported_libraries
464
+ return nil if reference?
465
+ ensure_full_data!
466
+ @gapi.imported_libraries.freeze
467
+ end
468
+
469
+ ##
470
+ # Updates the list of the Google Cloud Storage URIs of imported JavaScript libraries. Optional. Only used if
471
+ # {#language} is `JAVASCRIPT` ({#javascript?}).
472
+ #
473
+ # @param [Array<String>, nil] new_imported_libraries An array of Google Cloud Storage URIs, e.g.
474
+ # `["gs://cloud-samples-data/bigquery/udfs/max-value.js"]`.
475
+ #
476
+ # @example
477
+ # require "google/cloud/bigquery"
478
+ #
479
+ # bigquery = Google::Cloud::Bigquery.new
480
+ # dataset = bigquery.dataset "my_dataset"
481
+ # routine = dataset.routine "my_routine"
482
+ #
483
+ # routine.imported_libraries = [
484
+ # "gs://cloud-samples-data/bigquery/udfs/max-value.js"
485
+ # ]
486
+ #
487
+ # @!group Attributes
488
+ #
489
+ def imported_libraries= new_imported_libraries
490
+ ensure_full_data!
491
+ @gapi.imported_libraries = new_imported_libraries
492
+ update_gapi!
493
+ end
494
+
495
+ ##
496
+ # The body of the routine. Required.
497
+ #
498
+ # For functions ({#scalar_function?}), this is the expression in the `AS` clause.
499
+ #
500
+ # When the routine is a SQL function ({#sql?}), it is the substring inside (but excluding) the parentheses. For
501
+ # example, for the function created with the following statement:
502
+ # ```
503
+ # CREATE FUNCTION JoinLines(x string, y string) as (concat(x, "\n", y))
504
+ # ```
505
+ # The definition_body is `concat(x, "\n", y)` (`\n` is not replaced with linebreak).
506
+ #
507
+ # When the routine is a JavaScript function ({#javascript?}), it is the evaluated string in the `AS` clause. For
508
+ # example, for the function created with the following statement:
509
+ # ```
510
+ # CREATE FUNCTION f() RETURNS STRING LANGUAGE js AS 'return "\n";\n'
511
+ # ```
512
+ # The definition_body is
513
+ # ```
514
+ # "return \"\n\";\n"`
515
+ # ```
516
+ # Note that both `\n` are replaced with linebreaks.
517
+ #
518
+ # @return [String, nil] The body of the routine, or `nil` if the object is a reference (see {#reference?}).
519
+ #
520
+ # @!group Attributes
521
+ #
522
+ def body
523
+ return nil if reference?
524
+ ensure_full_data!
525
+ @gapi.definition_body
526
+ end
527
+
528
+ ##
529
+ # Updates the body of the routine. Required.
530
+ #
531
+ # For functions ({#scalar_function?}), this is the expression in the `AS` clause.
532
+ #
533
+ # When the routine is a SQL function ({#sql?}), it is the substring inside (but excluding) the parentheses. For
534
+ # example, for the function created with the following statement:
535
+ # ```
536
+ # CREATE FUNCTION JoinLines(x string, y string) as (concat(x, "\n", y))
537
+ # ```
538
+ # The definition_body is `concat(x, "\n", y)` (`\n` is not replaced with linebreak).
539
+ #
540
+ # When the routine is a JavaScript function ({#javascript?}), it is the evaluated string in the `AS` clause. For
541
+ # example, for the function created with the following statement:
542
+ # ```
543
+ # CREATE FUNCTION f() RETURNS STRING LANGUAGE js AS 'return "\n";\n'
544
+ # ```
545
+ # The definition_body is
546
+ # ```
547
+ # "return \"\n\";\n"`
548
+ # ```
549
+ # Note that both `\n` are replaced with linebreaks.
550
+ #
551
+ # @param [String] new_body The new body of the routine.
552
+ #
553
+ # @!group Attributes
554
+ #
555
+ def body= new_body
556
+ ensure_full_data!
557
+ @gapi.definition_body = new_body
558
+ update_gapi!
559
+ end
560
+
561
+ ###
562
+ # The description of the routine if defined. Optional. [Experimental]
563
+ #
564
+ # @return [String, nil] The routine description, or `nil` if the object is a reference (see {#reference?}).
565
+ #
566
+ # @example
567
+ # require "google/cloud/bigquery"
568
+ #
569
+ # bigquery = Google::Cloud::Bigquery.new
570
+ # dataset = bigquery.dataset "my_dataset"
571
+ # routine = dataset.routine "my_routine"
572
+ #
573
+ # routine.description #=> "My routine description"
574
+ #
575
+ # @!group Attributes
576
+ #
577
+ def description
578
+ return nil if reference?
579
+ ensure_full_data!
580
+ @gapi.description
581
+ end
582
+
583
+ ##
584
+ # Updates the description of the routine. Optional. [Experimental]
585
+ #
586
+ # @param [String] new_description The new routine description.
587
+ #
588
+ # @example
589
+ # require "google/cloud/bigquery"
590
+ #
591
+ # bigquery = Google::Cloud::Bigquery.new
592
+ # dataset = bigquery.dataset "my_dataset"
593
+ # routine = dataset.routine "my_routine"
594
+ #
595
+ # routine.description #=> "My routine description"
596
+ # routine.description = "My updated routine description"
597
+ #
598
+ # @!group Attributes
599
+ #
600
+ def description= new_description
601
+ ensure_full_data!
602
+ @gapi.description = new_description
603
+ update_gapi!
604
+ end
605
+
606
+ ##
607
+ # Updates the routine with changes made in the given block in a single update request. The following attributes
608
+ # may be set: {Updater#routine_type=}, {Updater#language=}, {Updater#arguments=}, {Updater#return_type=},
609
+ # {Updater#imported_libraries=}, {Updater#body=}, and {Updater#description=}.
610
+ #
611
+ # @yield [routine] A block for setting properties on the routine.
612
+ # @yieldparam [Google::Cloud::Bigquery::Routine::Updater] routine An updater to set additional properties on the
613
+ # routine.
614
+ #
615
+ # @example
616
+ # require "google/cloud/bigquery"
617
+ #
618
+ # bigquery = Google::Cloud::Bigquery.new
619
+ # dataset = bigquery.dataset "my_dataset"
620
+ # routine = dataset.routine "my_routine"
621
+ #
622
+ # routine.update do |r|
623
+ # r.routine_type = "SCALAR_FUNCTION"
624
+ # r.language = "SQL"
625
+ # r.arguments = [
626
+ # Google::Cloud::Bigquery::Argument.new(name: "x", data_type: "INT64")
627
+ # ]
628
+ # r.body = "x * 3"
629
+ # r.description = "My new routine description"
630
+ # end
631
+ #
632
+ # @!group Lifecycle
633
+ #
634
+ def update
635
+ ensure_full_data!
636
+ updater = Updater.new @gapi
637
+ yield updater
638
+ update_gapi! updater.to_gapi if updater.updates?
639
+ end
640
+
641
+ ##
642
+ # Permanently deletes the routine.
643
+ #
644
+ # @return [Boolean] Returns `true` if the routine was deleted.
645
+ #
646
+ # @example
647
+ # require "google/cloud/bigquery"
648
+ #
649
+ # bigquery = Google::Cloud::Bigquery.new
650
+ # dataset = bigquery.dataset "my_dataset"
651
+ # routine = dataset.routine "my_routine"
652
+ #
653
+ # routine.delete
654
+ #
655
+ # @!group Lifecycle
656
+ #
657
+ def delete
658
+ ensure_service!
659
+ service.delete_routine dataset_id, routine_id
660
+ # Set flag for #exists?
661
+ @exists = false
662
+ true
663
+ end
664
+
665
+ ##
666
+ # Reloads the routine with current data from the BigQuery service.
667
+ #
668
+ # @return [Google::Cloud::Bigquery::Routine] Returns the reloaded
669
+ # routine.
670
+ #
671
+ # @example Skip retrieving the routine from the service, then load it:
672
+ # require "google/cloud/bigquery"
673
+ #
674
+ # bigquery = Google::Cloud::Bigquery.new
675
+ #
676
+ # dataset = bigquery.dataset "my_dataset"
677
+ # routine = dataset.routine "my_routine", skip_lookup: true
678
+ #
679
+ # routine.reload!
680
+ #
681
+ # @!group Lifecycle
682
+ #
683
+ def reload!
684
+ ensure_service!
685
+ @gapi = service.get_routine dataset_id, routine_id
686
+ @reference = nil
687
+ @exists = nil
688
+ self
689
+ end
690
+ alias refresh! reload!
691
+
692
+ ##
693
+ # Determines whether the routine exists in the BigQuery service. The
694
+ # result is cached locally. To refresh state, set `force` to `true`.
695
+ #
696
+ # @param [Boolean] force Force the latest resource representation to be
697
+ # retrieved from the BigQuery service when `true`. Otherwise the
698
+ # return value of this method will be memoized to reduce the number of
699
+ # API calls made to the BigQuery service. The default is `false`.
700
+ #
701
+ # @return [Boolean] `true` when the routine exists in the BigQuery
702
+ # service, `false` otherwise.
703
+ #
704
+ # @example
705
+ # require "google/cloud/bigquery"
706
+ #
707
+ # bigquery = Google::Cloud::Bigquery.new
708
+ #
709
+ # dataset = bigquery.dataset "my_dataset"
710
+ # routine = dataset.routine "my_routine", skip_lookup: true
711
+ # routine.exists? #=> true
712
+ #
713
+ def exists? force: false
714
+ return resource_exists? if force
715
+ # If we have a value, return it
716
+ return @exists unless @exists.nil?
717
+ # Always true if we have a gapi object
718
+ return true if resource?
719
+ resource_exists?
720
+ end
721
+
722
+ ##
723
+ # Whether the routine was created without retrieving the resource
724
+ # representation from the BigQuery service.
725
+ #
726
+ # @return [Boolean] `true` when the routine is just a local reference
727
+ # object, `false` otherwise.
728
+ #
729
+ # @example
730
+ # require "google/cloud/bigquery"
731
+ #
732
+ # bigquery = Google::Cloud::Bigquery.new
733
+ #
734
+ # dataset = bigquery.dataset "my_dataset"
735
+ # routine = dataset.routine "my_routine", skip_lookup: true
736
+ #
737
+ # routine.reference? #=> true
738
+ # routine.reload!
739
+ # routine.reference? #=> false
740
+ #
741
+ def reference?
742
+ @gapi.nil?
743
+ end
744
+
745
+ ##
746
+ # Whether the routine was created with a resource representation from
747
+ # the BigQuery service.
748
+ #
749
+ # @return [Boolean] `true` when the routine was created with a resource
750
+ # representation, `false` otherwise.
751
+ #
752
+ # @example
753
+ # require "google/cloud/bigquery"
754
+ #
755
+ # bigquery = Google::Cloud::Bigquery.new
756
+ #
757
+ # dataset = bigquery.dataset "my_dataset"
758
+ # routine = dataset.routine "my_routine", skip_lookup: true
759
+ #
760
+ # routine.resource? #=> false
761
+ # routine.reload!
762
+ # routine.resource? #=> true
763
+ #
764
+ def resource?
765
+ !@gapi.nil?
766
+ end
767
+
768
+ ##
769
+ # Whether the routine was created with a partial resource representation
770
+ # from the BigQuery service by retrieval through {Dataset#routines}.
771
+ # See [Models: list
772
+ # response](https://cloud.google.com/bigquery/docs/reference/rest/v2/routines/list#response)
773
+ # for the contents of the partial representation. Accessing any
774
+ # attribute outside of the partial representation will result in loading
775
+ # the full representation.
776
+ #
777
+ # @return [Boolean] `true` when the routine was created with a partial
778
+ # resource representation, `false` otherwise.
779
+ #
780
+ # @example
781
+ # require "google/cloud/bigquery"
782
+ #
783
+ # bigquery = Google::Cloud::Bigquery.new
784
+ #
785
+ # dataset = bigquery.dataset "my_dataset"
786
+ # routine = dataset.routines.first
787
+ #
788
+ # routine.resource_partial? #=> true
789
+ # routine.description # Loads the full resource.
790
+ # routine.resource_partial? #=> false
791
+ #
792
+ def resource_partial?
793
+ resource? && !resource_full?
794
+ end
795
+
796
+ ##
797
+ # Whether the routine was created with a full resource representation
798
+ # from the BigQuery service.
799
+ #
800
+ # @return [Boolean] `true` when the routine was created with a full
801
+ # resource representation, `false` otherwise.
802
+ #
803
+ # @example
804
+ # require "google/cloud/bigquery"
805
+ #
806
+ # bigquery = Google::Cloud::Bigquery.new
807
+ #
808
+ # dataset = bigquery.dataset "my_dataset"
809
+ # routine = dataset.routine "my_routine"
810
+ #
811
+ # routine.resource_full? #=> true
812
+ #
813
+ def resource_full?
814
+ resource? && !@gapi.definition_body.nil?
815
+ end
816
+
817
+ ##
818
+ # @private New Routine from a Google API Client object.
819
+ def self.from_gapi gapi, service
820
+ new.tap do |r|
821
+ r.instance_variable_set :@gapi, gapi
822
+ r.instance_variable_set :@service, service
823
+ end
824
+ end
825
+
826
+ ##
827
+ # @private New lazy Routine object without making an HTTP request, for use with the skip_lookup option.
828
+ def self.new_reference project_id, dataset_id, routine_id, service
829
+ raise ArgumentError, "project_id is required" unless project_id
830
+ raise ArgumentError, "dataset_id is required" unless dataset_id
831
+ raise ArgumentError, "routine_id is required" unless routine_id
832
+ raise ArgumentError, "service is required" unless service
833
+
834
+ gapi = Google::Apis::BigqueryV2::RoutineReference.new(
835
+ project_id: project_id,
836
+ dataset_id: dataset_id,
837
+ routine_id: routine_id
838
+ )
839
+ new.tap do |r|
840
+ r.service = service
841
+ r.instance_variable_set :@reference, gapi
842
+ end
843
+ end
844
+
845
+ ##
846
+ # @private New lazy Routine object from a Google API Client object.
847
+ def self.new_reference_from_gapi gapi, service
848
+ new.tap do |b|
849
+ b.service = service
850
+ b.instance_variable_set :@reference, gapi
851
+ end
852
+ end
853
+
854
+ protected
855
+
856
+ ##
857
+ # Raise an error unless an active service is available.
858
+ def ensure_service!
859
+ raise "Must have active connection" unless service
860
+ end
861
+
862
+ ##
863
+ # Fetch gapi and memoize whether resource exists.
864
+ def resource_exists?
865
+ reload!
866
+ @exists = true
867
+ rescue Google::Cloud::NotFoundError
868
+ @exists = false
869
+ end
870
+
871
+ ##
872
+ # Load the complete representation of the routine if it has been
873
+ # only partially loaded by a request to the API list method.
874
+ def ensure_full_data!
875
+ reload! unless resource_full?
876
+ end
877
+
878
+ def update_gapi! update_gapi = nil
879
+ update_gapi ||= @gapi
880
+ ensure_service!
881
+ @gapi = service.update_routine dataset_id, routine_id, update_gapi
882
+ self
883
+ end
884
+
885
+ ##
886
+ # Yielded to a block to accumulate changes. See {Dataset#create_routine} and {Routine#update}.
887
+ #
888
+ # @example Creating a new routine:
889
+ # require "google/cloud/bigquery"
890
+ #
891
+ # bigquery = Google::Cloud::Bigquery.new
892
+ # dataset = bigquery.dataset "my_dataset"
893
+ #
894
+ # routine = dataset.create_routine "my_routine" do |r|
895
+ # r.routine_type = "SCALAR_FUNCTION"
896
+ # r.language = "SQL"
897
+ # r.arguments = [
898
+ # Google::Cloud::Bigquery::Argument.new(name: "x", data_type: "INT64")
899
+ # ]
900
+ # r.body = "x * 3"
901
+ # r.description = "My routine description"
902
+ # end
903
+ #
904
+ # puts routine.routine_id
905
+ #
906
+ # @example Updating an existing routine:
907
+ # require "google/cloud/bigquery"
908
+ #
909
+ # bigquery = Google::Cloud::Bigquery.new
910
+ # dataset = bigquery.dataset "my_dataset"
911
+ # routine = dataset.routine "my_routine"
912
+ #
913
+ # routine.update do |r|
914
+ # r.body = "x * 4"
915
+ # r.description = "My new routine description"
916
+ # end
917
+ #
918
+ class Updater < Routine
919
+ ##
920
+ # @private Create an Updater object.
921
+ def initialize gapi
922
+ @original_gapi = gapi
923
+ @gapi = gapi.dup
924
+ end
925
+
926
+ ##
927
+ # Updates the type of routine. Required.
928
+ #
929
+ # * `SCALAR_FUNCTION` - Non-builtin permanent scalar function.
930
+ # * `PROCEDURE` - Stored procedure.
931
+ #
932
+ # @param [String] new_routine_type The new type of the routine.
933
+ #
934
+ def routine_type= new_routine_type
935
+ @gapi.routine_type = new_routine_type
936
+ end
937
+
938
+ ##
939
+ # Updates the programming language of routine. Optional. Defaults to "SQL".
940
+ #
941
+ # * `SQL` - SQL language.
942
+ # * `JAVASCRIPT` - JavaScript language.
943
+ #
944
+ # @param [String] new_language The new language in upper case.
945
+ #
946
+ def language= new_language
947
+ @gapi.language = new_language
948
+ end
949
+
950
+ ##
951
+ # Updates the input/output arguments of the routine. Optional.
952
+ #
953
+ # @param [Array<Argument>] new_arguments The new arguments.
954
+ #
955
+ # @example
956
+ # require "google/cloud/bigquery"
957
+ #
958
+ # bigquery = Google::Cloud::Bigquery.new
959
+ # dataset = bigquery.dataset "my_dataset"
960
+ # routine = dataset.routine "my_routine"
961
+ #
962
+ # routine.arguments = [
963
+ # Google::Cloud::Bigquery::Argument.new(name: "x", data_type: "INT64")
964
+ # ]
965
+ #
966
+ def arguments= new_arguments
967
+ @gapi.arguments = new_arguments.map(&:to_gapi)
968
+ end
969
+
970
+ ##
971
+ # Updates the return type of the routine. Optional if the routine is a SQL function ({#sql?}); required
972
+ # otherwise.
973
+ #
974
+ # If absent, the return type is inferred from {#body} at query time in each query that references this
975
+ # routine. If present, then the evaluated result will be cast to the specified returned type at query time.
976
+ #
977
+ # For example, for the functions created with the following statements:
978
+ #
979
+ # * `CREATE FUNCTION Add(x FLOAT64, y FLOAT64) RETURNS FLOAT64 AS (x + y);`
980
+ # * `CREATE FUNCTION Increment(x FLOAT64) AS (Add(x, 1));`
981
+ # * `CREATE FUNCTION Decrement(x FLOAT64) RETURNS FLOAT64 AS (Add(x, -1));`
982
+ #
983
+ # The returnType is `{typeKind: "FLOAT64"}` for Add and Decrement, and is absent for Increment (inferred as
984
+ # `FLOAT64` at query time).
985
+ #
986
+ # Suppose the function Add is replaced by `CREATE OR REPLACE FUNCTION Add(x INT64, y INT64) AS (x + y);`
987
+ #
988
+ # Then the inferred return type of Increment is automatically changed to `INT64` at query time, while the
989
+ # return type of Decrement remains `FLOAT64`.
990
+ #
991
+ # @param [Google::Cloud::Bigquery::StandardSql::DataType, String] new_return_type The new return type for the
992
+ # routine.
993
+ #
994
+ # @example
995
+ # require "google/cloud/bigquery"
996
+ #
997
+ # bigquery = Google::Cloud::Bigquery.new
998
+ # dataset = bigquery.dataset "my_dataset"
999
+ # routine = dataset.routine "my_routine"
1000
+ #
1001
+ # routine.return_type.type_kind #=> "INT64"
1002
+ # routine.return_type = "STRING"
1003
+ #
1004
+ def return_type= new_return_type
1005
+ @gapi.return_type = StandardSql::DataType.gapi_from_string_or_data_type new_return_type
1006
+ end
1007
+
1008
+ ##
1009
+ # Updates the list of the Google Cloud Storage URIs of imported JavaScript libraries. Optional. Only used if
1010
+ # {#language} is `JAVASCRIPT` ({#javascript?}).
1011
+ #
1012
+ # @param [Array<String>, nil] new_imported_libraries An array of Google Cloud Storage URIs, e.g.
1013
+ # `["gs://cloud-samples-data/bigquery/udfs/max-value.js"]`.
1014
+ #
1015
+ # @example
1016
+ # require "google/cloud/bigquery"
1017
+ #
1018
+ # bigquery = Google::Cloud::Bigquery.new
1019
+ # dataset = bigquery.dataset "my_dataset"
1020
+ # routine = dataset.routine "my_routine"
1021
+ #
1022
+ # routine.imported_libraries = [
1023
+ # "gs://cloud-samples-data/bigquery/udfs/max-value.js"
1024
+ # ]
1025
+ #
1026
+ def imported_libraries= new_imported_libraries
1027
+ @gapi.imported_libraries = new_imported_libraries
1028
+ end
1029
+
1030
+ ##
1031
+ # Updates the body of the routine. Required.
1032
+ #
1033
+ # For functions ({#scalar_function?}), this is the expression in the `AS` clause.
1034
+ #
1035
+ # When the routine is a SQL function ({#sql?}), it is the substring inside (but excluding) the parentheses.
1036
+ # For example, for the function created with the following statement:
1037
+ # ```
1038
+ # CREATE FUNCTION JoinLines(x string, y string) as (concat(x, "\n", y))
1039
+ # ```
1040
+ # The definition_body is `concat(x, "\n", y)` (`\n` is not replaced with linebreak).
1041
+ #
1042
+ # When the routine is a JavaScript function ({#javascript?}), it is the evaluated string in the `AS` clause.
1043
+ # For example, for the function created with the following statement:
1044
+ # ```
1045
+ # CREATE FUNCTION f() RETURNS STRING LANGUAGE js AS 'return "\n";\n'
1046
+ # ```
1047
+ # The definition_body is
1048
+ # ```
1049
+ # "return \"\n\";\n"`
1050
+ # ```
1051
+ # Note that both `\n` are replaced with linebreaks.
1052
+ #
1053
+ # @param [String] new_body The new body of the routine.
1054
+ #
1055
+ def body= new_body
1056
+ @gapi.definition_body = new_body
1057
+ end
1058
+
1059
+ ##
1060
+ # Updates the description of the routine. Optional. [Experimental]
1061
+ #
1062
+ # @param [String] new_description The new routine description.
1063
+ #
1064
+ # @example
1065
+ # require "google/cloud/bigquery"
1066
+ #
1067
+ # bigquery = Google::Cloud::Bigquery.new
1068
+ # dataset = bigquery.dataset "my_dataset"
1069
+ # routine = dataset.routine "my_routine"
1070
+ #
1071
+ # routine.description #=> "My routine description"
1072
+ # routine.description = "My updated routine description"
1073
+ #
1074
+ def description= new_description
1075
+ @gapi.description = new_description
1076
+ end
1077
+
1078
+ def update
1079
+ raise "not implemented in #{self.class}"
1080
+ end
1081
+
1082
+ def delete
1083
+ raise "not implemented in #{self.class}"
1084
+ end
1085
+
1086
+ def reload!
1087
+ raise "not implemented in #{self.class}"
1088
+ end
1089
+ alias refresh! reload!
1090
+
1091
+ # rubocop:disable Style/CaseEquality
1092
+
1093
+ # @private
1094
+ def updates?
1095
+ !(@gapi === @original_gapi)
1096
+ end
1097
+
1098
+ # rubocop:enable Style/CaseEquality
1099
+
1100
+ # @private
1101
+ def to_gapi
1102
+ @gapi
1103
+ end
1104
+ end
1105
+ end
1106
+ end
1107
+ end
1108
+ end