-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Expand file tree
/
Copy pathBigQueryJdbcUrlUtility.java
More file actions
869 lines (840 loc) · 44.2 KB
/
BigQueryJdbcUrlUtility.java
File metadata and controls
869 lines (840 loc) · 44.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.cloud.bigquery.jdbc;
import com.google.api.client.util.escape.CharEscapers;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.exception.BigQueryJdbcRuntimeException;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.net.UrlEscapers;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class implements all the methods that parse Connection property values from the Connection
* String.
*/
final class BigQueryJdbcUrlUtility {
private static final Map<String, Map<String, String>> PARSE_CACHE =
Collections.synchronizedMap(
new LinkedHashMap<String, Map<String, String>>(50, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry<String, Map<String, String>> eldest) {
return size() > 50; // bound cache size
}
});
// TODO: Add all Connection options
static final String ALLOW_LARGE_RESULTS_PROPERTY_NAME = "AllowLargeResults";
static final String LARGE_RESULTS_TABLE_PROPERTY_NAME = "LargeResultTable";
static final String LARGE_RESULTS_DATASET_PROPERTY_NAME = "LargeResultDataset";
static final String UNSUPPORTED_HTAPI_FALLBACK_PROPERTY_NAME = "UnsupportedHTAPIFallback";
static final boolean DEFAULT_UNSUPPORTED_HTAPI_FALLBACK_VALUE = true;
static final String DESTINATION_DATASET_EXPIRATION_TIME_PROPERTY_NAME =
"LargeResultsDatasetExpirationTime";
static final long DEFAULT_DESTINATION_DATASET_EXPIRATION_TIME_VALUE = 3600000L;
static final boolean DEFAULT_ALLOW_LARGE_RESULTS = true;
static final String QUERY_DIALECT_PROPERTY_NAME = "QueryDialect";
static final String DEFAULT_QUERY_DIALECT_VALUE = "SQL";
static final String UNIVERSE_DOMAIN_OVERRIDE_PROPERTY_NAME = "universeDomain";
static final String DEFAULT_UNIVERSE_DOMAIN_VALUE = "googleapis.com";
static final String PROJECT_ID_PROPERTY_NAME = "ProjectId";
static final String DEFAULT_DATASET_PROPERTY_NAME = "DefaultDataset";
static final String OAUTH_TYPE_PROPERTY_NAME = "OAuthType";
static final String HTAPI_ACTIVATION_RATIO_PROPERTY_NAME = "HighThroughputActivationRatio";
static final String KMS_KEY_NAME_PROPERTY_NAME = "KMSKeyName";
static final String QUERY_PROPERTIES_NAME = "QueryProperties";
static final int DEFAULT_HTAPI_ACTIVATION_RATIO_VALUE = 2;
static final String HTAPI_MIN_TABLE_SIZE_PROPERTY_NAME = "HighThroughputMinTableSize";
static final int DEFAULT_HTAPI_MIN_TABLE_SIZE_VALUE = 10000;
static final int DEFAULT_OAUTH_TYPE_VALUE = -1;
static final String LOCATION_PROPERTY_NAME = "Location";
static final String ENDPOINT_OVERRIDES_PROPERTY_NAME = "EndpointOverrides";
static final String PRIVATE_SERVICE_CONNECT_PROPERTY_NAME = "PrivateServiceConnectUris";
static final String OAUTH_SA_IMPERSONATION_EMAIL_PROPERTY_NAME =
"ServiceAccountImpersonationEmail";
static final String DEFAULT_OAUTH_SA_IMPERSONATION_EMAIL_VALUE = null;
static final String OAUTH_SA_IMPERSONATION_CHAIN_PROPERTY_NAME =
"ServiceAccountImpersonationChain";
static final String DEFAULT_OAUTH_SA_IMPERSONATION_CHAIN_VALUE = null;
static final String OAUTH_SA_IMPERSONATION_SCOPES_PROPERTY_NAME =
"ServiceAccountImpersonationScopes";
static final String OAUTH_SA_IMPERSONATION_TOKEN_LIFETIME_PROPERTY_NAME =
"ServiceAccountImpersonationTokenLifetime";
static final String DEFAULT_OAUTH_SA_IMPERSONATION_TOKEN_LIFETIME_VALUE = "3600";
static final String OAUTH_SA_EMAIL_PROPERTY_NAME = "OAuthServiceAcctEmail";
static final String OAUTH_PVT_KEY_PATH_PROPERTY_NAME = "OAuthPvtKeyPath";
static final String OAUTH_P12_PASSWORD_PROPERTY_NAME = "OAuthP12Password";
static final String DEFAULT_OAUTH_P12_PASSWORD_VALUE = "notasecret";
static final String OAUTH_PVT_KEY_PROPERTY_NAME = "OAuthPvtKey";
static final String OAUTH2_TOKEN_URI_PROPERTY_NAME = "OAUTH2";
static final String HTAPI_ENDPOINT_OVERRIDE_PROPERTY_NAME = "READ_API";
static final String BIGQUERY_ENDPOINT_OVERRIDE_PROPERTY_NAME = "BIGQUERY";
static final String STS_ENDPOINT_OVERRIDE_PROPERTY_NAME = "STS";
static final String OAUTH_ACCESS_TOKEN_PROPERTY_NAME = "OAuthAccessToken";
static final String OAUTH_ACCESS_TOKEN_READONLY_PROPERTY_NAME = "OAuthAccessTokenReadonly";
static final String OAUTH_REFRESH_TOKEN_PROPERTY_NAME = "OAuthRefreshToken";
static final String OAUTH_CLIENT_ID_PROPERTY_NAME = "OAuthClientId";
static final String OAUTH_CLIENT_SECRET_PROPERTY_NAME = "OAuthClientSecret";
static final String DEFAULT_OAUTH_CLIENT_ID = "977385342095.apps.googleusercontent.com";
static final String DEFAULT_OAUTH_CLIENT_SECRET = "wbER7576mc_1YOII0dGk7jEE";
static final String ENABLE_HTAPI_PROPERTY_NAME = "EnableHighThroughputAPI";
static final String PROXY_HOST_PROPERTY_NAME = "ProxyHost";
static final String PROXY_PORT_PROPERTY_NAME = "ProxyPort";
static final String PROXY_USER_ID_PROPERTY_NAME = "ProxyUid";
static final String PROXY_PASSWORD_PROPERTY_NAME = "ProxyPwd";
static final String HTTP_CONNECT_TIMEOUT_PROPERTY_NAME = "HttpConnectTimeout";
static final String HTTP_READ_TIMEOUT_PROPERTY_NAME = "HttpReadTimeout";
static final boolean DEFAULT_ENABLE_HTAPI_VALUE = false;
static final boolean DEFAULT_ENABLE_SESSION_VALUE = false;
static final int DEFAULT_LOG_LEVEL = 0;
static final String LOG_LEVEL_PROPERTY_NAME = "LogLevel";
static final String LOG_PATH_PROPERTY_NAME = "LogPath";
static final String LOG_LEVEL_ENV_VAR = "BIGQUERY_JDBC_LOG_LEVEL";
static final String LOG_PATH_ENV_VAR = "BIGQUERY_JDBC_LOG_PATH";
static final String ENABLE_SESSION_PROPERTY_NAME = "EnableSession";
static final String DEFAULT_LOG_PATH = "";
static final String USE_QUERY_CACHE_PROPERTY_NAME = "UseQueryCache";
static final boolean DEFAULT_USE_QUERY_CACHE = true;
static final String JOB_CREATION_MODE_PROPERTY_NAME = "JobCreationMode";
static final int DEFAULT_JOB_CREATION_MODE = 2;
static final String MAX_RESULTS_PROPERTY_NAME = "MaxResults";
static final long DEFAULT_MAX_RESULTS_VALUE = 10000;
static final String BYOID_AUDIENCE_URI_PROPERTY_NAME = "BYOID_AudienceUri";
static final String BYOID_CREDENTIAL_SOURCE_PROPERTY_NAME = "BYOID_CredentialSource";
static final String BYOID_POOL_USER_PROJECT_PROPERTY_NAME = "BYOID_PoolUserProject";
static final String BYOID_SA_IMPERSONATION_URI_PROPERTY_NAME = "BYOID_SA_Impersonation_Uri";
static final String BYOID_SUBJECT_TOKEN_TYPE_PROPERTY_NAME = "BYOID_SubjectTokenType";
static final String DEFAULT_BYOID_SUBJECT_TOKEN_TYPE_VALUE =
"urn:ietf:params:oauth:tokentype:id_token";
static final String BYOID_TOKEN_URI_PROPERTY_NAME = "BYOID_TokenUri";
static final String DEFAULT_BYOID_TOKEN_URI_VALUE = "https://sts.googleapis.com/v1/token";
static final String PARTNER_TOKEN_PROPERTY_NAME = "PartnerToken";
private static final Pattern PARTNER_TOKEN_PATTERN =
Pattern.compile(
"(?:^|(?<=;))" + PARTNER_TOKEN_PROPERTY_NAME + "=\\s*((?:\\([^)]*\\)|[^;])*?)(?=(?:;|$))",
Pattern.CASE_INSENSITIVE);
static final String METADATA_FETCH_THREAD_COUNT_PROPERTY_NAME = "MetaDataFetchThreadCount";
static final int DEFAULT_METADATA_FETCH_THREAD_COUNT_VALUE = 32;
static final String RETRY_TIMEOUT_IN_SECS_PROPERTY_NAME = "Timeout";
static final long DEFAULT_RETRY_TIMEOUT_IN_SECS_VALUE = 0L;
static final String JOB_TIMEOUT_PROPERTY_NAME = "JobTimeout";
static final long DEFAULT_JOB_TIMEOUT_VALUE = 0L;
static final String RETRY_INITIAL_DELAY_PROPERTY_NAME = "RetryInitialDelay";
static final long DEFAULT_RETRY_INITIAL_DELAY_VALUE = 0L;
static final String RETRY_MAX_DELAY_PROPERTY_NAME = "RetryMaxDelay";
static final long DEFAULT_RETRY_MAX_DELAY_VALUE = 0L;
static final String ADDITIONAL_PROJECTS_PROPERTY_NAME = "AdditionalProjects";
// Applicable only for connection pooling.
static final String CONNECTION_POOL_SIZE_PROPERTY_NAME = "ConnectionPoolSize";
static final long DEFAULT_CONNECTION_POOL_SIZE_VALUE = 10L;
static final String LISTENER_POOL_SIZE_PROPERTY_NAME = "ListenerPoolSize";
static final long DEFAULT_LISTENER_POOL_SIZE_VALUE = 10L;
static final String ENABLE_WRITE_API_PROPERTY_NAME = "EnableWriteAPI";
static final boolean DEFAULT_ENABLE_WRITE_API_VALUE = false;
static final String SWA_APPEND_ROW_COUNT_PROPERTY_NAME = "SWA_AppendRowCount";
static final int DEFAULT_SWA_APPEND_ROW_COUNT_VALUE = 1000;
static final String SWA_ACTIVATION_ROW_COUNT_PROPERTY_NAME = "SWA_ActivationRowCount";
static final int DEFAULT_SWA_ACTIVATION_ROW_COUNT_VALUE = 3;
private static final BigQueryJdbcCustomLogger LOG =
new BigQueryJdbcCustomLogger(BigQueryJdbcUrlUtility.class.getName());
static final String FILTER_TABLES_ON_DEFAULT_DATASET_PROPERTY_NAME =
"FilterTablesOnDefaultDataset";
static final boolean DEFAULT_FILTER_TABLES_ON_DEFAULT_DATASET_VALUE = false;
static final String REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME = "RequestGoogleDriveScope";
static final String SSL_TRUST_STORE_PROPERTY_NAME = "SSLTrustStore";
static final String SSL_TRUST_STORE_PWD_PROPERTY_NAME = "SSLTrustStorePwd";
static final int DEFAULT_REQUEST_GOOGLE_DRIVE_SCOPE_VALUE = 0;
static final String MAX_BYTES_BILLED_PROPERTY_NAME = "MaximumBytesBilled";
static final Long DEFAULT_MAX_BYTES_BILLED_VALUE = 0L;
static final String LABELS_PROPERTY_NAME = "Labels";
static final List<String> OVERRIDE_PROPERTIES =
Arrays.asList(
BIGQUERY_ENDPOINT_OVERRIDE_PROPERTY_NAME,
OAUTH2_TOKEN_URI_PROPERTY_NAME,
HTAPI_ENDPOINT_OVERRIDE_PROPERTY_NAME,
STS_ENDPOINT_OVERRIDE_PROPERTY_NAME);
static final String REQUEST_REASON_PROPERTY_NAME = "RequestReason";
static final List<String> BYOID_PROPERTIES =
Arrays.asList(
BYOID_AUDIENCE_URI_PROPERTY_NAME,
BYOID_CREDENTIAL_SOURCE_PROPERTY_NAME,
BYOID_POOL_USER_PROJECT_PROPERTY_NAME,
BYOID_SA_IMPERSONATION_URI_PROPERTY_NAME,
BYOID_SUBJECT_TOKEN_TYPE_PROPERTY_NAME,
BYOID_TOKEN_URI_PROPERTY_NAME);
static Set<BigQueryConnectionProperty> PROXY_PROPERTIES =
Collections.unmodifiableSet(
new HashSet<>(
Arrays.asList(
BigQueryConnectionProperty.newBuilder()
.setName(PROXY_HOST_PROPERTY_NAME)
.setDescription("The host name of the proxy server.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(PROXY_PORT_PROPERTY_NAME)
.setDescription(
"The port number of the proxy server to connect to. No defaulting"
+ " behavior happens.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(PROXY_USER_ID_PROPERTY_NAME)
.setDescription("The user name for an authenticated proxy server.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(PROXY_PASSWORD_PROPERTY_NAME)
.setDescription("The password for an authenticated proxy server.")
.build())));
static Set<BigQueryConnectionProperty> AUTH_PROPERTIES =
Collections.unmodifiableSet(
new HashSet<>(
Arrays.asList(
BigQueryConnectionProperty.newBuilder()
.setName(OAUTH_TYPE_PROPERTY_NAME)
.setDescription(
"This option specifies how the connector obtains or provides the"
+ " credentials for OAuth\n"
+ "2.0 authentication")
.setDefaultValue(String.valueOf(DEFAULT_OAUTH_TYPE_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(OAUTH_SA_EMAIL_PROPERTY_NAME)
.setDescription(
"The Service Account email use for Service Account Authentication.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(OAUTH_PVT_KEY_PATH_PROPERTY_NAME)
.setDescription(
"The location of the credentials file used for this connection.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(OAUTH_PVT_KEY_PROPERTY_NAME)
.setDescription("The OAuth private key used for this connection.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(OAUTH_REFRESH_TOKEN_PROPERTY_NAME)
.setDescription(
"The pre-generated refresh token to be used with BigQuery for"
+ " authentication.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(OAUTH_ACCESS_TOKEN_PROPERTY_NAME)
.setDescription(
"The pre-generated access token to be used with BigQuery for"
+ " authentication.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(OAUTH_ACCESS_TOKEN_READONLY_PROPERTY_NAME)
.setDescription(
"Set to true if the pre-generated access token has a read-only scope.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(OAUTH_CLIENT_ID_PROPERTY_NAME)
.setDescription(
"The client ID to be used for user authentication or to refresh"
+ " pre-generated tokens.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(OAUTH_CLIENT_SECRET_PROPERTY_NAME)
.setDescription(
"The client secret to be used for user authentication or to refresh"
+ " pre-generated tokens.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(OAUTH_SA_IMPERSONATION_EMAIL_PROPERTY_NAME)
.setDescription("The service account email to be impersonated.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(OAUTH_SA_IMPERSONATION_CHAIN_PROPERTY_NAME)
.setDescription(
"Comma separated list of service account emails in the impersonation"
+ " chain.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(OAUTH_SA_IMPERSONATION_SCOPES_PROPERTY_NAME)
.setDescription(
"Comma separated list of OAuth2 scopes to use with impersonated account.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(OAUTH_SA_IMPERSONATION_TOKEN_LIFETIME_PROPERTY_NAME)
.setDescription("Impersonated account token lifetime.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(OAUTH_P12_PASSWORD_PROPERTY_NAME)
.setDescription("Password for p12 secret file.")
.build())));
static Set<BigQueryConnectionProperty> VALID_PROPERTIES =
Collections.unmodifiableSet(
new HashSet<>(
Arrays.asList(
BigQueryConnectionProperty.newBuilder()
.setName(MAX_BYTES_BILLED_PROPERTY_NAME)
.setDescription(
" Limits the bytes billed for this query. Queries with bytes billed above"
+ " this limit will fail (without incurring a charge). If"
+ " unspecified, the project default is used.")
.setDefaultValue(String.valueOf(DEFAULT_MAX_BYTES_BILLED_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(CONNECTION_POOL_SIZE_PROPERTY_NAME)
.setDescription("Connection pool size if connection pooling is enabled.")
.setDefaultValue(String.valueOf(DEFAULT_CONNECTION_POOL_SIZE_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(LISTENER_POOL_SIZE_PROPERTY_NAME)
.setDescription("Listener pool size if connection pooling is enabled.")
.setDefaultValue(String.valueOf(DEFAULT_LISTENER_POOL_SIZE_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(RETRY_INITIAL_DELAY_PROPERTY_NAME)
.setDescription("Initial delay, in seconds, before the first retry.")
.setDefaultValue(String.valueOf(DEFAULT_RETRY_INITIAL_DELAY_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(RETRY_MAX_DELAY_PROPERTY_NAME)
.setDescription("Max limit for the retry delay, in seconds.")
.setDefaultValue(String.valueOf(DEFAULT_RETRY_MAX_DELAY_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(RETRY_TIMEOUT_IN_SECS_PROPERTY_NAME)
.setDescription(
"The length of time, in seconds, for which the connector retries a failed"
+ " API call before timing out.")
.setDefaultValue(String.valueOf(DEFAULT_RETRY_TIMEOUT_IN_SECS_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(JOB_TIMEOUT_PROPERTY_NAME)
.setDescription(
"Job timeout (in seconds) after which the job is cancelled on the server")
.setDefaultValue(String.valueOf(DEFAULT_JOB_TIMEOUT_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(UNSUPPORTED_HTAPI_FALLBACK_PROPERTY_NAME)
.setDescription(
"This option determines whether the connector uses the REST API or"
+ " returns an error when encountering fetch workflows unsupported by"
+ " the High-Throughput API.")
.setDefaultValue(String.valueOf(DEFAULT_UNSUPPORTED_HTAPI_FALLBACK_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(DESTINATION_DATASET_EXPIRATION_TIME_PROPERTY_NAME)
.setDescription(
"The expiration time (in milliseconds) for tables in a user-specified"
+ " large result dataset.")
.setDefaultValue(
String.valueOf(DEFAULT_DESTINATION_DATASET_EXPIRATION_TIME_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(UNIVERSE_DOMAIN_OVERRIDE_PROPERTY_NAME)
.setDescription(
"The name of the partner-operated cloud which is a new instance of Google"
+ " production, known as a Trusted Partner Cloud universe.")
.setDefaultValue(DEFAULT_UNIVERSE_DOMAIN_VALUE)
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(PROJECT_ID_PROPERTY_NAME)
.setDescription("A globally unique identifier for your project.")
.setLazyDefaultValue(() -> BigQueryOptions.getDefaultProjectId())
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(LOG_PATH_PROPERTY_NAME)
.setDescription(
"The directory where the connector saves log files (when logging is"
+ " enabled).")
.setDefaultValue(DEFAULT_LOG_PATH)
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(DEFAULT_DATASET_PROPERTY_NAME)
.setDescription(
"This default dataset for query execution. If this option is set, queries"
+ " with unqualified \n"
+ "table names will run against this dataset.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(LOCATION_PROPERTY_NAME)
.setDescription(
"The location where datasets are created/queried. The location will be"
+ " determined\n"
+ " automatically by BigQuery if not specified.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(ENABLE_HTAPI_PROPERTY_NAME)
.setDescription(
"Enables or disables Read API usage in the Driver. Disabled by default.")
.setDefaultValue(String.valueOf(DEFAULT_ENABLE_HTAPI_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(HTAPI_ACTIVATION_RATIO_PROPERTY_NAME)
.setDescription(
"Connector switches to BigQuery Storage API when the number of pages"
+ " exceed this value.")
.setDefaultValue(String.valueOf(DEFAULT_HTAPI_ACTIVATION_RATIO_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(KMS_KEY_NAME_PROPERTY_NAME)
.setDescription(
"The KMS key name tells BigQuery which key to use when encrypting or"
+ " decrypting your data.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(QUERY_PROPERTIES_NAME)
.setDescription(
"Connection-level properties to customize query behavior.") // TODO:
// Figure out
// a clean way
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(LABELS_PROPERTY_NAME)
.setDescription(
"Labels associated with the query to organize and group query jobs.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(HTAPI_MIN_TABLE_SIZE_PROPERTY_NAME)
.setDescription(
"If the number of total rows exceeds this value, the connector switches"
+ " to the BigQuery Storage API for faster processing.")
.setDefaultValue(String.valueOf(DEFAULT_HTAPI_MIN_TABLE_SIZE_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(ENABLE_SESSION_PROPERTY_NAME)
.setDescription(
"Enable to capture your SQL activities or enable multi statement"
+ " transactions. Disabled by default.")
.setDefaultValue(String.valueOf(DEFAULT_ENABLE_SESSION_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(LOG_LEVEL_PROPERTY_NAME)
.setDescription(
"Sets the Log Level for the Driver. Set to Level.OFF by default.")
.setDefaultValue(String.valueOf(DEFAULT_LOG_LEVEL))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(USE_QUERY_CACHE_PROPERTY_NAME)
.setDescription("Enables or disables Query caching. Set to true by default.")
.setDefaultValue(String.valueOf(DEFAULT_USE_QUERY_CACHE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(QUERY_DIALECT_PROPERTY_NAME)
.setDescription(
"Parameter for selecting if the queries should use standard or legacy SQL"
+ " syntax.")
.setDefaultValue(DEFAULT_QUERY_DIALECT_VALUE)
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(ALLOW_LARGE_RESULTS_PROPERTY_NAME)
.setDescription(
"Enabled by default, must be used with legacy SQL. Used for setting"
+ " destination table & dataset.")
.setDefaultValue(String.valueOf(DEFAULT_ALLOW_LARGE_RESULTS))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(LARGE_RESULTS_TABLE_PROPERTY_NAME)
.setDescription("The destination table where queries are saved.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(LARGE_RESULTS_DATASET_PROPERTY_NAME)
.setDescription("The destination dataset where queries are saved.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(JOB_CREATION_MODE_PROPERTY_NAME)
.setDescription(
"Enables or disables Stateless Query mode. Set to false by default.")
.setDefaultValue(String.valueOf(DEFAULT_JOB_CREATION_MODE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(MAX_RESULTS_PROPERTY_NAME)
.setDescription("Maximum number of results per page")
.setDefaultValue(String.valueOf(DEFAULT_MAX_RESULTS_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(BYOID_AUDIENCE_URI_PROPERTY_NAME)
.setDescription(
"Used for External Account Authentication. Corresponds to the audience"
+ " property\n"
+ " in the external account configuration file.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(BYOID_CREDENTIAL_SOURCE_PROPERTY_NAME)
.setDescription(
"Used for External Account Authentication. The file location or the URI"
+ " of\n"
+ " the subject token. Corresponds to the credential_source property"
+ " in\n"
+ " the external account configuration file.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(BYOID_POOL_USER_PROJECT_PROPERTY_NAME)
.setDescription(
"Used for External Account Authentication. The project number associated"
+ " with\n"
+ " the workforce pool. Corresponds to the"
+ " workforce_pool_user_project\n"
+ " property in the external account configuration file.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(BYOID_SA_IMPERSONATION_URI_PROPERTY_NAME)
.setDescription(
"Used for External Account Authentication. The service account email."
+ " Only\n"
+ " present when service account impersonation is used. Corresponds"
+ " to\n"
+ " the service_account_impersonation_url property in the external"
+ " account\n"
+ " configuration file.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(BYOID_SUBJECT_TOKEN_TYPE_PROPERTY_NAME)
.setDescription(
"Used for External Account Authentication. The subject token type."
+ " Corresponds\n"
+ " to the subject_token_type property in the external account"
+ " configuration file.")
.setDefaultValue(DEFAULT_BYOID_SUBJECT_TOKEN_TYPE_VALUE)
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(BYOID_TOKEN_URI_PROPERTY_NAME)
.setDescription(
"Used for External Account Authentication. The URI used to generate"
+ " authentication\n"
+ " tokens. Corresponds to the token_url property in the external"
+ " account\n"
+ " configuration file.")
.setDefaultValue(DEFAULT_BYOID_TOKEN_URI_VALUE)
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(PARTNER_TOKEN_PROPERTY_NAME)
.setDescription("The partner name and environment.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(METADATA_FETCH_THREAD_COUNT_PROPERTY_NAME)
.setDescription(
"The number of threads used to call a DatabaseMetaData method.")
.setDefaultValue(String.valueOf(DEFAULT_METADATA_FETCH_THREAD_COUNT_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(ENABLE_WRITE_API_PROPERTY_NAME)
.setDescription(
"Enables or disables Write API usage for bulk inserts in the Driver."
+ " Disabled by default.")
.setDefaultValue(String.valueOf(DEFAULT_ENABLE_WRITE_API_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(SWA_ACTIVATION_ROW_COUNT_PROPERTY_NAME)
.setDescription(
"Connector switches to BigQuery Storage Write API when the number of rows"
+ " for executeBatch insert exceed this value. Do not change unless"
+ " necessary.")
.setDefaultValue(String.valueOf(DEFAULT_SWA_ACTIVATION_ROW_COUNT_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(SWA_APPEND_ROW_COUNT_PROPERTY_NAME)
.setDescription("Size of the write stream. Do not change unless necessary.")
.setDefaultValue(String.valueOf(DEFAULT_SWA_APPEND_ROW_COUNT_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(ADDITIONAL_PROJECTS_PROPERTY_NAME)
.setDescription(
"A comma-separated list of Google Cloud project IDs that can be accessed"
+ " for querying, in addition to the primary project specified in the"
+ " connection.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(FILTER_TABLES_ON_DEFAULT_DATASET_PROPERTY_NAME)
.setDescription(
"If true and DefaultDataset is set, DatabaseMetaData.getTables() and"
+ " .getColumns() will filter results based on the DefaultDataset"
+ " when catalog/schema patterns are null or wildcards.")
.setDefaultValue(
String.valueOf(DEFAULT_FILTER_TABLES_ON_DEFAULT_DATASET_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(REQUEST_GOOGLE_DRIVE_SCOPE_PROPERTY_NAME)
.setDescription(
"Enables or disables whether the connector requests access to Google"
+ " Drive. Set to false (0) by default.")
.setDefaultValue(String.valueOf(DEFAULT_REQUEST_GOOGLE_DRIVE_SCOPE_VALUE))
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(SSL_TRUST_STORE_PROPERTY_NAME)
.setDescription(
"The full path of the Java TrustStore containing the server certificate"
+ " for one-way SSL authentication.\n"
+ "If the trust store requires a password, provide it using the"
+ " property SSLTrustStorePwd.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(SSL_TRUST_STORE_PWD_PROPERTY_NAME)
.setDescription(
"The password for accessing the Java TrustStore that is specified using"
+ " the property SSLTrustStore.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(HTTP_CONNECT_TIMEOUT_PROPERTY_NAME)
.setDescription(
"The timeout (in milliseconds) for establishing a connection to the"
+ " server.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(HTTP_READ_TIMEOUT_PROPERTY_NAME)
.setDescription("The timeout (in milliseconds) when reading from the server.")
.build(),
BigQueryConnectionProperty.newBuilder()
.setName(REQUEST_REASON_PROPERTY_NAME)
.setDescription(
"Reason for the request, which is passed as the x-goog-request-reason"
+ " header.")
.build())));
private static final List<String> NETWORK_PROPERTIES =
ImmutableList.of(
PARTNER_TOKEN_PROPERTY_NAME,
ENDPOINT_OVERRIDES_PROPERTY_NAME,
PRIVATE_SERVICE_CONNECT_PROPERTY_NAME);
private static final Map<String, String> PROPERTY_NAME_MAP;
static {
Map<String, String> map = new HashMap<>();
for (BigQueryConnectionProperty p : VALID_PROPERTIES) {
map.put(p.getName().toUpperCase(), p.getName());
}
for (BigQueryConnectionProperty p : AUTH_PROPERTIES) {
map.put(p.getName().toUpperCase(), p.getName());
}
for (BigQueryConnectionProperty p : PROXY_PROPERTIES) {
map.put(p.getName().toUpperCase(), p.getName());
}
for (String p : OVERRIDE_PROPERTIES) {
map.put(p.toUpperCase(), p);
}
for (String p : BYOID_PROPERTIES) {
map.put(p.toUpperCase(), p);
}
for (String p : NETWORK_PROPERTIES) {
map.put(p.toUpperCase(), p);
}
PROPERTY_NAME_MAP = Collections.unmodifiableMap(map);
}
private BigQueryJdbcUrlUtility() {}
/**
* Parses a URI property from the given URI.
*
* @param uri The URI to parse.
* @param property The name of the property to parse.
* @return The String value of the property, or the default value if the property is not found.
*/
static String parseUriProperty(String uri, String property) {
Map<String, String> map = parseUrl(uri);
if (PROPERTY_NAME_MAP.containsKey(property.toUpperCase())) {
return map.get(PROPERTY_NAME_MAP.get(property.toUpperCase()));
}
return map.get(property);
}
/**
* Parses a URI property from the given URI without validating any other properties.
*
* @param uri The URI to parse.
* @param property The name of the property to parse.
* @return The String value of the property, or null if the property is not found.
*/
static String parseUriPropertyWithoutValidation(String uri, String property) {
if (uri == null) {
return null;
}
int start = 0;
int len = uri.length();
while (start < len) {
int nextSemi = uri.indexOf(';', start);
int end = (nextSemi == -1) ? len : nextSemi;
int eqIndex = uri.indexOf('=', start);
if (eqIndex > start && eqIndex < end) {
String key = uri.substring(start, eqIndex).trim();
if (key.equalsIgnoreCase(property)) {
String value = uri.substring(eqIndex + 1, end);
return CharEscapers.decodeUriPath(value.replace("+", "%2B"));
}
}
start = end + 1;
}
return null;
}
/**
* Parses the URL into a map of key-value pairs, validating that all keys are known properties.
*
* @param url The URL to parse.
* @return A map of property names to values.
* @throws BigQueryJdbcRuntimeException if an unknown property is found or the URL is malformed.
*/
static Map<String, String> parseUrl(String url) {
return PARSE_CACHE.computeIfAbsent(url, BigQueryJdbcUrlUtility::parseUrlInternal);
}
private static Map<String, String> parseUrlInternal(String url) {
Map<String, String> map = new HashMap<>();
if (url == null) {
return map;
}
String[] urlParts = url.split(";", 2);
if (urlParts.length < 2) {
return map;
}
String urlToParse = urlParts[1];
// Parse PartnerToken separately as it contains ';'
Matcher matcher = PARTNER_TOKEN_PATTERN.matcher(urlToParse);
if (matcher.find()) {
String rawToken = matcher.group(1).trim();
String token =
(rawToken.startsWith("(") && rawToken.endsWith(")"))
? rawToken.substring(1, rawToken.length() - 1).trim()
: rawToken;
if (token.toUpperCase().startsWith("GPN:")) {
map.put(PARTNER_TOKEN_PROPERTY_NAME, " (" + token + ")");
}
urlToParse = matcher.replaceFirst("");
}
String[] parts = urlToParse.split(";");
for (String part : parts) {
if (part.trim().isEmpty()) {
continue;
}
String[] kv = part.split("=", 2);
String key = kv[0].trim().toUpperCase();
if (kv.length != 2 || !PROPERTY_NAME_MAP.containsKey(key)) {
String ref = (kv.length == 2) ? key : part;
String safeRef = ref.length() > 32 ? ref.substring(0, 32) + "..." : ref;
// Some tools can pass unknown keys. In order not to break compatibility, throw
// an exception only with incorrect format, otherwise log an error.
if (kv.length != 2) {
throw new BigQueryJdbcRuntimeException(
String.format("Wrong value or unknown setting: %s", safeRef));
} else {
LOG.warning("Wrong value or unknown setting: %s", safeRef);
continue;
}
}
String propertyName = PROPERTY_NAME_MAP.get(key);
String value = CharEscapers.decodeUriPath(kv[1].replace("+", "%2B"));
map.put(propertyName, value);
}
return Collections.unmodifiableMap(map);
}
/**
* Appends the given properties to the given URL.
*
* @param url The URL to append the properties to.
* @param properties The properties to append.
* @return The string value of the updated URL.
*/
static String appendPropertiesToURL(String url, String callerClassName, Properties properties) {
LOG.finest("++enter++ " + callerClassName);
StringBuilder urlBuilder = new StringBuilder(url);
for (Entry<Object, Object> entry : properties.entrySet()) {
if (entry.getValue() != null && !"".equals(entry.getValue())) {
LOG.finest("Appending %s with value %s to URL", entry.getKey(), entry.getValue());
String encodedValue =
UrlEscapers.urlFormParameterEscaper()
.escape((String) entry.getValue())
.replace("+", "%20");
urlBuilder.append(";").append(entry.getKey()).append("=").append(encodedValue);
}
}
return urlBuilder.toString();
}
static boolean convertIntToBoolean(String value, String propertyName) {
int integerValue;
try {
if (value.equalsIgnoreCase("true")) {
integerValue = 1;
} else if (value.equalsIgnoreCase("false")) {
integerValue = 0;
} else {
integerValue = Integer.parseInt(value);
}
} catch (NumberFormatException ex) {
IllegalArgumentException e =
new IllegalArgumentException(
String.format(
"Invalid value for %s. For Boolean connection properties, use 0 for false and 1 for"
+ " true.",
propertyName),
ex);
LOG.severe(e.getMessage(), e);
throw e;
}
if (integerValue == 1) {
return true;
} else if (integerValue == 0) {
return false;
} else {
IllegalArgumentException ex =
new IllegalArgumentException(
String.format(
"Invalid value for %s. For Boolean connection properties, use 0 for false and 1 for"
+ " true.",
propertyName));
LOG.severe(ex.getMessage(), ex);
throw ex;
}
}
public static Level parseLogLevel(String logLevelString) {
int logLevel = logLevelString != null ? Integer.parseInt(logLevelString) : DEFAULT_LOG_LEVEL;
switch (logLevel) {
case 8:
return Level.ALL;
case 7:
return Level.FINEST;
case 6:
return Level.FINER;
case 5:
return Level.FINE;
case 4:
return Level.CONFIG;
case 3:
return Level.INFO;
case 2:
return Level.WARNING;
case 1:
return Level.SEVERE;
case 0:
default:
LOG.info("%s value not provided, defaulting to %s.", LOG_LEVEL_PROPERTY_NAME, Level.OFF);
return Level.OFF;
}
}
static Map<String, String> parsePropertiesMapFromValue(
String propertiesString, String propertyName, String context) {
if (propertiesString == null || propertiesString.isEmpty()) {
LOG.fine("Unable to parse property name: %s from context: %s", propertyName, context);
return null;
}
Map<String, String> propertiesMap = new HashMap<>();
for (String keyValuePair : Splitter.on(",").split(propertiesString)) {
List<String> parts = Splitter.on("=").limit(2).splitToList(keyValuePair);
if (parts.size() == 2) {
propertiesMap.put(parts.get(0), parts.get(1));
} else {
LOG.warning(
"Invalid KeyValue pair: %s found in context: %s for property name: %s",
keyValuePair, context, propertyName);
}
}
return propertiesMap;
}
}