UNPKG

68.3 kBJavaScriptView Raw
1"use strict";
2/*!
3 * Copyright 2019 Google LLC
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17Object.defineProperty(exports, "__esModule", { value: true });
18exports.BigQueryInt = exports.BigQueryTime = exports.BigQueryDatetime = exports.BigQueryTimestamp = exports.Geography = exports.BigQueryDate = exports.BigQueryRange = exports.BigQuery = exports.PROTOCOL_REGEX = exports.common = void 0;
19const common_1 = require("@google-cloud/common");
20const common = require("@google-cloud/common");
21exports.common = common;
22const paginator_1 = require("@google-cloud/paginator");
23const promisify_1 = require("@google-cloud/promisify");
24const precise_date_1 = require("@google-cloud/precise-date");
25const arrify = require("arrify");
26const Big = require("big.js");
27const extend = require("extend");
28const is = require("is");
29const uuid = require("uuid");
30const dataset_1 = require("./dataset");
31const job_1 = require("./job");
32const table_1 = require("./table");
33const logger_1 = require("./logger");
34exports.PROTOCOL_REGEX = /^(\w*):\/\//;
35/**
36 * @typedef {object} BigQueryOptions
37 * @property {string} [projectId] The project ID from the Google Developer's
38 * Console, e.g. 'grape-spaceship-123'. We will also check the environment
39 * variable `GCLOUD_PROJECT` for your project ID. If your app is running in
40 * an environment which supports {@link
41 * https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application
42 * Application Default Credentials}, your project ID will be detected
43 * automatically.
44 * @property {string} [keyFilename] Full path to the a .json, .pem, or .p12 key
45 * downloaded from the Google Developers Console. If you provide a path to a
46 * JSON file, the `projectId` option above is not necessary. NOTE: .pem and
47 * .p12 require you to specify the `email` option as well.
48 * @property {string} [token] An OAUTH access token. If provided, we will not
49 * manage fetching, re-using, and re-minting access tokens.
50 * @property {string} [email] Account email address. Required when using a .pem
51 * or .p12 keyFilename.
52 * @property {object} [credentials] Credentials object.
53 * @property {string} [credentials.client_email]
54 * @property {string} [credentials.private_key]
55 * @property {Constructor} [promise] Custom promise module to use instead of
56 * native Promises.
57 * @property {string[]} [scopes] Additional OAuth scopes to use in requests. For
58 * example, to access an external data source, you may need the
59 * `https://www.googleapis.com/auth/drive.readonly` scope.
60 */
61/**
62 * In the following examples from this page and the other modules (`Dataset`,
63 * `Table`, etc.), we are going to be using a dataset from
64 * {@link http://goo.gl/f2SXcb| data.gov} of higher education institutions.
65 *
66 * We will create a table with the correct schema, import the public CSV file
67 * into that table, and query it for data.
68 *
69 * This client supports enabling query-related preview features via environmental
70 * variables. By setting the environment variable QUERY_PREVIEW_ENABLED to the string
71 * "TRUE", the client will enable preview features, though behavior may still be
72 * controlled via the bigquery service as well. Currently, the feature(s) in scope
73 * include: stateless queries (query execution without corresponding job metadata).
74 *
75 * @class
76 *
77 * See {@link https://cloud.google.com/bigquery/what-is-bigquery| What is BigQuery?}
78 *
79 * @param {BigQueryOptions} options Constructor options.
80 *
81 * @example Install the client library with <a href="https://www.npmjs.com/">npm</a>:
82 * ```
83 * npm install @google-cloud/bigquery
84 *
85 * ```
86 * @example Import the client library
87 * ```
88 * const {BigQuery} = require('@google-cloud/bigquery');
89 *
90 * ```
91 * @example Create a client that uses <a href="https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application">Application Default Credentials (ADC)</a>:
92 * ```
93 * const bigquery = new BigQuery();
94 *
95 * ```
96 * @example Create a client with <a href="https://cloud.google.com/docs/authentication/production#obtaining_and_providing_service_account_credentials_manually">explicit credentials</a>:
97 * ```
98 * const bigquery = new BigQuery({
99 * projectId: 'your-project-id',
100 * keyFilename: '/path/to/keyfile.json'
101 * });
102 *
103 * ```
104 * @example <caption>include:samples/quickstart.js</caption>
105 * region_tag:bigquery_quickstart
106 * Full quickstart example:
107 */
108class BigQuery extends common_1.Service {
109 createQueryStream(options) {
110 // placeholder body, overwritten in constructor
111 return new paginator_1.ResourceStream({}, () => { });
112 }
113 getDatasetsStream(options) {
114 // placeholder body, overwritten in constructor
115 return new paginator_1.ResourceStream({}, () => { });
116 }
117 getJobsStream(options) {
118 // placeholder body, overwritten in constructor
119 return new paginator_1.ResourceStream({}, () => { });
120 }
121 constructor(options = {}) {
122 let universeDomain = 'googleapis.com';
123 const servicePath = 'bigquery';
124 if (options.universeDomain) {
125 universeDomain = BigQuery.sanitizeDomain(options.universeDomain);
126 }
127 const EMULATOR_HOST = process.env.BIGQUERY_EMULATOR_HOST;
128 let apiEndpoint = `https://${servicePath}.${universeDomain}`;
129 if (typeof EMULATOR_HOST === 'string') {
130 apiEndpoint = BigQuery.sanitizeEndpoint(EMULATOR_HOST);
131 }
132 if (options.apiEndpoint) {
133 apiEndpoint = BigQuery.sanitizeEndpoint(options.apiEndpoint);
134 }
135 options = Object.assign({}, options, {
136 apiEndpoint,
137 });
138 const baseUrl = EMULATOR_HOST || `${options.apiEndpoint}/bigquery/v2`;
139 const config = {
140 apiEndpoint: options.apiEndpoint,
141 baseUrl,
142 scopes: ['https://www.googleapis.com/auth/bigquery'],
143 packageJson: require('../../package.json'),
144 autoRetry: options.autoRetry,
145 maxRetries: options.maxRetries,
146 retryOptions: options.retryOptions,
147 };
148 if (options.scopes) {
149 config.scopes = config.scopes.concat(options.scopes);
150 }
151 super(config, options);
152 const QUERY_PREVIEW_ENABLED = process.env.QUERY_PREVIEW_ENABLED;
153 this._enableQueryPreview = false;
154 if (typeof QUERY_PREVIEW_ENABLED === 'string') {
155 if (QUERY_PREVIEW_ENABLED.toUpperCase() === 'TRUE') {
156 this._enableQueryPreview = true;
157 }
158 }
159 this._universeDomain = universeDomain;
160 this.location = options.location;
161 /**
162 * Run a query scoped to your project as a readable object stream.
163 *
164 * @method
165 * @param {object} query Configuration object. See {@link BigQuery.query} for a complete
166 * list of options.
167 *
168 * @example
169 * ```
170 * const {BigQuery} = require('@google-cloud/bigquery');
171 * const bigquery = new BigQuery();
172 *
173 * const query = 'SELECT url FROM `publicdata.samples.github_nested` LIMIT
174 * 100';
175 *
176 * bigquery.createQueryStream(query)
177 * .on('error', console.error)
178 * .on('data', function(row) {
179 * // row is a result from your query.
180 * })
181 * .on('end', function() {
182 * // All rows retrieved.
183 * });
184 *
185 * //-
186 * // If you anticipate many results, you can end a stream early to prevent
187 * // unnecessary processing and API requests.
188 * //-
189 * bigquery.createQueryStream(query)
190 * .on('data', function(row) {
191 * this.end();
192 * });
193 * ```
194 */
195 this.createQueryStream = paginator_1.paginator.streamify('queryAsStream_');
196 /**
197 * List all or some of the {@link Dataset} objects in your project as
198 * a readable object stream.
199 *
200 * @param {object} [options] Configuration object. See
201 * {@link BigQuery.getDatasets} for a complete list of options.
202 *
203 * @example
204 * ```
205 * const {BigQuery} = require('@google-cloud/bigquery');
206 * const bigquery = new BigQuery();
207 *
208 * bigquery.getDatasetsStream()
209 * .on('error', console.error)
210 * .on('data', function(dataset) {
211 * // dataset is a Dataset object.
212 * })
213 * .on('end', function() {
214 * // All datasets retrieved.
215 * });
216 *
217 * //-
218 * // If you anticipate many results, you can end a stream early to prevent
219 * // unnecessary processing and API requests.
220 * //-
221 * bigquery.getDatasetsStream()
222 * .on('data', function(dataset) {
223 * this.end();
224 * });
225 * ```
226 */
227 this.getDatasetsStream = paginator_1.paginator.streamify('getDatasets');
228 /**
229 * List all or some of the {@link Job} objects in your project as a
230 * readable object stream.
231 *
232 * @param {object} [options] Configuration object. See
233 * {@link BigQuery.getJobs} for a complete list of options.
234 *
235 * @example
236 * ```
237 * const {BigQuery} = require('@google-cloud/bigquery');
238 * const bigquery = new BigQuery();
239 *
240 * bigquery.getJobsStream()
241 * .on('error', console.error)
242 * .on('data', function(job) {
243 * // job is a Job object.
244 * })
245 * .on('end', function() {
246 * // All jobs retrieved.
247 * });
248 *
249 * //-
250 * // If you anticipate many results, you can end a stream early to prevent
251 * // unnecessary processing and API requests.
252 * //-
253 * bigquery.getJobsStream()
254 * .on('data', function(job) {
255 * this.end();
256 * });
257 * ```
258 */
259 this.getJobsStream = paginator_1.paginator.streamify('getJobs');
260 // Disable `prettyPrint` for better performance.
261 // https://github.com/googleapis/nodejs-bigquery/issues/858
262 this.interceptors.push({
263 request: (reqOpts) => {
264 return extend(true, {}, reqOpts, { qs: { prettyPrint: false } });
265 },
266 });
267 }
268 // eslint-disable-next-line @typescript-eslint/no-explicit-any
269 trace_(msg, ...otherArgs) {
270 (0, logger_1.logger)('[bigquery]', msg, ...otherArgs);
271 }
272 get universeDomain() {
273 return this._universeDomain;
274 }
275 static sanitizeEndpoint(url) {
276 if (!exports.PROTOCOL_REGEX.test(url)) {
277 url = `https://${url}`;
278 }
279 return this.sanitizeDomain(url);
280 }
281 static sanitizeDomain(url) {
282 return url.replace(/\/+$/, ''); // Remove trailing slashes
283 }
284 /**
285 * Merge a rowset returned from the API with a table schema.
286 *
287 * @private
288 *
289 * @param {object} schema
290 * @param {array} rows
291 * @param {object} options
292 * @param {boolean|IntegerTypeCastOptions} options.wrapIntegers Wrap values of
293 * 'INT64' type in {@link BigQueryInt} objects.
294 * If a `boolean`, this will wrap values in {@link BigQueryInt} objects.
295 * If an `object`, this will return a value returned by
296 * `wrapIntegers.integerTypeCastFunction`.
297 * Please see {@link IntegerTypeCastOptions} for options descriptions.
298 * @param {array} options.selectedFields List of fields to return.
299 * If unspecified, all fields are returned.
300 * @param {array} options.parseJSON parse a 'JSON' field into a JSON object.
301 * @returns Fields using their matching names from the table's schema.
302 */
303 static mergeSchemaWithRows_(schema, rows, options) {
304 var _a;
305 if (options.selectedFields && options.selectedFields.length > 0) {
306 const selectedFieldsArray = options.selectedFields.map(c => {
307 return c.split('.');
308 });
309 const currentFields = selectedFieldsArray.map(c => c.shift());
310 //filter schema fields based on selected fields.
311 schema.fields = (_a = schema.fields) === null || _a === void 0 ? void 0 : _a.filter(field => currentFields
312 .map(c => c.toLowerCase())
313 .indexOf(field.name.toLowerCase()) >= 0);
314 options.selectedFields = selectedFieldsArray
315 .filter(c => c.length > 0)
316 .map(c => c.join('.'));
317 }
318 return arrify(rows).map(mergeSchema).map(flattenRows);
319 function mergeSchema(row) {
320 return row.f.map((field, index) => {
321 const schemaField = schema.fields[index];
322 let value = field.v;
323 if (schemaField.mode === 'REPEATED') {
324 value = value.map(val => {
325 return convertSchemaFieldValue(schemaField, val.v, options);
326 });
327 }
328 else {
329 value = convertSchemaFieldValue(schemaField, value, options);
330 }
331 // eslint-disable-next-line @typescript-eslint/no-explicit-any
332 const fieldObject = {};
333 fieldObject[schemaField.name] = value;
334 return fieldObject;
335 });
336 }
337 // eslint-disable-next-line @typescript-eslint/no-explicit-any
338 function flattenRows(rows) {
339 return rows.reduce((acc, row) => {
340 const key = Object.keys(row)[0];
341 acc[key] = row[key];
342 return acc;
343 }, {});
344 }
345 }
346 /**
347 * The `DATE` type represents a logical calendar date, independent of time
348 * zone. It does not represent a specific 24-hour time period. Rather, a given
349 * DATE value represents a different 24-hour period when interpreted in
350 * different time zones, and may represent a shorter or longer day during
351 * Daylight Savings Time transitions.
352 *
353 * @param {object|string} value The date. If a string, this should be in the
354 * format the API describes: `YYYY-[M]M-[D]D`.
355 * Otherwise, provide an object.
356 * @param {string|number} value.year Four digits.
357 * @param {string|number} value.month One or two digits.
358 * @param {string|number} value.day One or two digits.
359 *
360 * @example
361 * ```
362 * const {BigQuery} = require('@google-cloud/bigquery');
363 * const bigquery = new BigQuery();
364 * const date = bigquery.date('2017-01-01');
365 *
366 * //-
367 * // Alternatively, provide an object.
368 * //-
369 * const date2 = bigquery.date({
370 * year: 2017,
371 * month: 1,
372 * day: 1
373 * });
374 * ```
375 */
376 static date(value) {
377 return new BigQueryDate(value);
378 }
379 /**
380 * @param {object|string} value The date. If a string, this should be in the
381 * format the API describes: `YYYY-[M]M-[D]D`.
382 * Otherwise, provide an object.
383 * @param {string|number} value.year Four digits.
384 * @param {string|number} value.month One or two digits.
385 * @param {string|number} value.day One or two digits.
386 *
387 * @example
388 * ```
389 * const {BigQuery} = require('@google-cloud/bigquery');
390 * const date = BigQuery.date('2017-01-01');
391 *
392 * //-
393 * // Alternatively, provide an object.
394 * //-
395 * const date2 = BigQuery.date({
396 * year: 2017,
397 * month: 1,
398 * day: 1
399 * });
400 * ```
401 */
402 date(value) {
403 return BigQuery.date(value);
404 }
405 /**
406 * A `DATETIME` data type represents a point in time. Unlike a `TIMESTAMP`,
407 * this does not refer to an absolute instance in time. Instead, it is the
408 * civil time, or the time that a user would see on a watch or calendar.
409 *
410 * @method BigQuery.datetime
411 * @param {object|string} value The time. If a string, this should be in the
412 * format the API describes: `YYYY-[M]M-[D]D[ [H]H:[M]M:[S]S[.DDDDDD]]`.
413 * Otherwise, provide an object.
414 * @param {string|number} value.year Four digits.
415 * @param {string|number} value.month One or two digits.
416 * @param {string|number} value.day One or two digits.
417 * @param {string|number} [value.hours] One or two digits (`00` - `23`).
418 * @param {string|number} [value.minutes] One or two digits (`00` - `59`).
419 * @param {string|number} [value.seconds] One or two digits (`00` - `59`).
420 * @param {string|number} [value.fractional] Up to six digits for microsecond
421 * precision.
422 *
423 * @example
424 * ```
425 * const {BigQuery} = require('@google-cloud/bigquery');
426 * const datetime = BigQuery.datetime('2017-01-01 13:00:00');
427 *
428 * //-
429 * // Alternatively, provide an object.
430 * //-
431 * const datetime = BigQuery.datetime({
432 * year: 2017,
433 * month: 1,
434 * day: 1,
435 * hours: 14,
436 * minutes: 0,
437 * seconds: 0
438 * });
439 * ```
440 */
441 /**
442 * A `DATETIME` data type represents a point in time. Unlike a `TIMESTAMP`,
443 * this does not refer to an absolute instance in time. Instead, it is the
444 * civil time, or the time that a user would see on a watch or calendar.
445 *
446 * @param {object|string} value The time. If a string, this should be in the
447 * format the API describes: `YYYY-[M]M-[D]D[ [H]H:[M]M:[S]S[.DDDDDD]]`.
448 * Otherwise, provide an object.
449 * @param {string|number} value.year Four digits.
450 * @param {string|number} value.month One or two digits.
451 * @param {string|number} value.day One or two digits.
452 * @param {string|number} [value.hours] One or two digits (`00` - `23`).
453 * @param {string|number} [value.minutes] One or two digits (`00` - `59`).
454 * @param {string|number} [value.seconds] One or two digits (`00` - `59`).
455 * @param {string|number} [value.fractional] Up to six digits for microsecond
456 * precision.
457 *
458 * @example
459 * ```
460 * const {BigQuery} = require('@google-cloud/bigquery');
461 * const bigquery = new BigQuery();
462 * const datetime = bigquery.datetime('2017-01-01 13:00:00');
463 *
464 * //-
465 * // Alternatively, provide an object.
466 * //-
467 * const datetime = bigquery.datetime({
468 * year: 2017,
469 * month: 1,
470 * day: 1,
471 * hours: 14,
472 * minutes: 0,
473 * seconds: 0
474 * });
475 * ```
476 */
477 static datetime(value) {
478 return new BigQueryDatetime(value);
479 }
480 datetime(value) {
481 return BigQuery.datetime(value);
482 }
483 /**
484 * A `TIME` data type represents a time, independent of a specific date.
485 *
486 * @method BigQuery.time
487 * @param {object|string} value The time. If a string, this should be in the
488 * format the API describes: `[H]H:[M]M:[S]S[.DDDDDD]`. Otherwise, provide
489 * an object.
490 * @param {string|number} [value.hours] One or two digits (`00` - `23`).
491 * @param {string|number} [value.minutes] One or two digits (`00` - `59`).
492 * @param {string|number} [value.seconds] One or two digits (`00` - `59`).
493 * @param {string|number} [value.fractional] Up to six digits for microsecond
494 * precision.
495 *
496 * @example
497 * ```
498 * const {BigQuery} = require('@google-cloud/bigquery');
499 * const time = BigQuery.time('14:00:00'); // 2:00 PM
500 *
501 * //-
502 * // Alternatively, provide an object.
503 * //-
504 * const time = BigQuery.time({
505 * hours: 14,
506 * minutes: 0,
507 * seconds: 0
508 * });
509 * ```
510 */
511 /**
512 * A `TIME` data type represents a time, independent of a specific date.
513 *
514 * @param {object|string} value The time. If a string, this should be in the
515 * format the API describes: `[H]H:[M]M:[S]S[.DDDDDD]`. Otherwise, provide
516 * an object.
517 * @param {string|number} [value.hours] One or two digits (`00` - `23`).
518 * @param {string|number} [value.minutes] One or two digits (`00` - `59`).
519 * @param {string|number} [value.seconds] One or two digits (`00` - `59`).
520 * @param {string|number} [value.fractional] Up to six digits for microsecond
521 * precision.
522 *
523 * @example
524 * ```
525 * const {BigQuery} = require('@google-cloud/bigquery');
526 * const bigquery = new BigQuery();
527 * const time = bigquery.time('14:00:00'); // 2:00 PM
528 *
529 * //-
530 * // Alternatively, provide an object.
531 * //-
532 * const time = bigquery.time({
533 * hours: 14,
534 * minutes: 0,
535 * seconds: 0
536 * });
537 * ```
538 */
539 static time(value) {
540 return new BigQueryTime(value);
541 }
542 time(value) {
543 return BigQuery.time(value);
544 }
545 /**
546 * A timestamp represents an absolute point in time, independent of any time
547 * zone or convention such as Daylight Savings Time.
548 *
549 * The recommended input here is a `Date` or `PreciseDate` class.
550 * If passing as a `string`, it should be Timestamp literals: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#timestamp_literals.
551 * When passing a `number` input, it should be epoch seconds in float representation.
552 *
553 * @method BigQuery.timestamp
554 * @param {Date|string} value The time.
555 *
556 * @example
557 * ```
558 * const {BigQuery} = require('@google-cloud/bigquery');
559 * const timestamp = BigQuery.timestamp(new Date());
560 * ```
561 */
562 static timestamp(value) {
563 return new BigQueryTimestamp(value);
564 }
565 /**
566 * A timestamp represents an absolute point in time, independent of any time
567 * zone or convention such as Daylight Savings Time.
568 *
569 * The recommended input here is a `Date` or `PreciseDate` class.
570 * If passing as a `string`, it should be Timestamp literals: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#timestamp_literals.
571 * When passing a `number` input, it should be epoch seconds in float representation.
572 *
573 * @param {Date|string|string|number} value The time.
574 *
575 * @example
576 * ```
577 * const {BigQuery} = require('@google-cloud/bigquery');
578 * const bigquery = new BigQuery();
579 * const timestamp = bigquery.timestamp(new Date());
580 * ```
581 */
582 timestamp(value) {
583 return BigQuery.timestamp(value);
584 }
585 /**
586 * A range represents contiguous range between two dates, datetimes, or timestamps.
587 * The lower and upper bound for the range are optional.
588 * The lower bound is inclusive and the upper bound is exclusive.
589 *
590 * @method BigQuery.range
591 * @param {string|BigQueryRangeOptions} value The range API string or start/end with dates/datetimes/timestamp ranges.
592 * @param {string} elementType The range element type - DATE|DATETIME|TIMESTAMP
593 *
594 * @example
595 * ```
596 * const {BigQuery} = require('@google-cloud/bigquery');
597 * const timestampRange = BigQuery.range('[2020-10-01 12:00:00+08, 2020-12-31 12:00:00+08)', 'TIMESTAMP');
598 * ```
599 */
600 static range(value, elementType) {
601 return new BigQueryRange(value, elementType);
602 }
603 /**
604 * A range represents contiguous range between two dates, datetimes, or timestamps.
605 * The lower and upper bound for the range are optional.
606 * The lower bound is inclusive and the upper bound is exclusive.
607 *
608 * @param {string|BigQueryRangeOptions} value The range API string or start/end with dates/datetimes/timestamp ranges.
609 * @param {string} elementType The range element type - DATE|DATETIME|TIMESTAMP
610 *
611 * @example
612 * ```
613 * const {BigQuery} = require('@google-cloud/bigquery');
614 * const bigquery = new BigQuery();
615 * const timestampRange = bigquery.range('[2020-10-01 12:00:00+08, 2020-12-31 12:00:00+08)', 'TIMESTAMP');
616 * ```
617 */
618 range(value, elementType) {
619 return BigQuery.range(value, elementType);
620 }
621 /**
622 * A BigQueryInt wraps 'INT64' values. Can be used to maintain precision.
623 *
624 * @param {string|number|IntegerTypeCastValue} value The INT64 value to convert.
625 * @param {IntegerTypeCastOptions} typeCastOptions Configuration to convert
626 * value. Must provide an `integerTypeCastFunction` to handle conversion.
627 * @returns {BigQueryInt}
628 *
629 * @example
630 * ```
631 * const {BigQuery} = require('@google-cloud/bigquery');
632 * const bigquery = new BigQuery();
633 *
634 * const largeIntegerValue = Number.MAX_SAFE_INTEGER + 1;
635 *
636 * const options = {
637 * integerTypeCastFunction: value => value.split(),
638 * };
639 *
640 * const bqInteger = bigquery.int(largeIntegerValue, options);
641 *
642 * const customValue = bqInteger.valueOf();
643 * // customValue is the value returned from your `integerTypeCastFunction`.
644 * ```
645 */
646 static int(value, typeCastOptions) {
647 return new BigQueryInt(value, typeCastOptions);
648 }
649 int(value, typeCastOptions) {
650 return BigQuery.int(value, typeCastOptions);
651 }
652 /**
653 * A geography value represents a surface area on the Earth
654 * in Well-known Text (WKT) format.
655 *
656 * @param {string} value The geospatial data.
657 *
658 * @example
659 * ```
660 * const {BigQuery} = require('@google-cloud/bigquery');
661 * const bigquery = new BigQuery();
662 * const geography = bigquery.geography('POINT(1, 2)');
663 * ```
664 */
665 static geography(value) {
666 return new Geography(value);
667 }
668 geography(value) {
669 return BigQuery.geography(value);
670 }
671 /**
672 * Convert an INT64 value to Number.
673 *
674 * @private
675 * @param {object} value The INT64 value to convert.
676 */
677 static decodeIntegerValue_(value) {
678 const num = Number(value.integerValue);
679 if (!Number.isSafeInteger(num)) {
680 throw new Error('We attempted to return all of the numeric values, but ' +
681 (value.schemaFieldName ? value.schemaFieldName + ' ' : '') +
682 'value ' +
683 value.integerValue +
684 " is out of bounds of 'Number.MAX_SAFE_INTEGER'.\n" +
685 "To prevent this error, please consider passing 'options.wrapIntegers' as\n" +
686 '{\n' +
687 ' integerTypeCastFunction: provide <your_custom_function>\n' +
688 ' fields: optionally specify field name(s) to be custom casted\n' +
689 '}\n');
690 }
691 return num;
692 }
693 /**
694 * Return a value's provided type.
695 *
696 * @private
697 *
698 * @throws {error} If the type provided is invalid.
699 *
700 * See {@link https://cloud.google.com/bigquery/data-types| Data Type}
701 *
702 * @param {*} providedType The type.
703 * @returns {string} The valid type provided.
704 */
705 static getTypeDescriptorFromProvidedType_(providedType) {
706 // The list of types can be found in src/types.d.ts
707 const VALID_TYPES = [
708 'DATE',
709 'DATETIME',
710 'TIME',
711 'TIMESTAMP',
712 'BYTES',
713 'NUMERIC',
714 'DECIMAL',
715 'BIGNUMERIC',
716 'BIGDECIMAL',
717 'BOOL',
718 'INT64',
719 'INT',
720 'SMALLINT',
721 'INTEGER',
722 'BIGINT',
723 'TINYINT',
724 'BYTEINT',
725 'FLOAT64',
726 'FLOAT',
727 'STRING',
728 'GEOGRAPHY',
729 'ARRAY',
730 'STRUCT',
731 'JSON',
732 'RANGE',
733 ];
734 if (is.array(providedType)) {
735 providedType = providedType;
736 return {
737 type: 'ARRAY',
738 arrayType: BigQuery.getTypeDescriptorFromProvidedType_(providedType[0]),
739 };
740 }
741 else if (is.object(providedType)) {
742 return {
743 type: 'STRUCT',
744 structTypes: Object.keys(providedType).map(prop => {
745 return {
746 name: prop,
747 type: BigQuery.getTypeDescriptorFromProvidedType_(providedType[prop]),
748 };
749 }),
750 };
751 }
752 providedType = providedType.toUpperCase();
753 if (!VALID_TYPES.includes(providedType)) {
754 throw new Error(`Invalid type provided: "${providedType}"`);
755 }
756 return { type: providedType.toUpperCase() };
757 }
758 /**
759 * Detect a value's type.
760 *
761 * @private
762 *
763 * @throws {error} If the type could not be detected.
764 *
765 * See {@link https://cloud.google.com/bigquery/data-types| Data Type}
766 *
767 * @param {*} value The value.
768 * @returns {string} The type detected from the value.
769 */
770 static getTypeDescriptorFromValue_(value) {
771 let typeName;
772 if (value === null) {
773 throw new Error("Parameter types must be provided for null values via the 'types' field in query options.");
774 }
775 if (value instanceof BigQueryDate) {
776 typeName = 'DATE';
777 }
778 else if (value instanceof BigQueryDatetime) {
779 typeName = 'DATETIME';
780 }
781 else if (value instanceof BigQueryTime) {
782 typeName = 'TIME';
783 }
784 else if (value instanceof BigQueryTimestamp) {
785 typeName = 'TIMESTAMP';
786 }
787 else if (value instanceof Buffer) {
788 typeName = 'BYTES';
789 }
790 else if (value instanceof Big) {
791 if (value.c.length - value.e >= 10) {
792 typeName = 'BIGNUMERIC';
793 }
794 else {
795 typeName = 'NUMERIC';
796 }
797 }
798 else if (value instanceof BigQueryInt) {
799 typeName = 'INT64';
800 }
801 else if (value instanceof Geography) {
802 typeName = 'GEOGRAPHY';
803 }
804 else if (value instanceof BigQueryRange) {
805 return {
806 type: 'RANGE',
807 rangeElementType: {
808 type: value.elementType,
809 },
810 };
811 }
812 else if (Array.isArray(value)) {
813 if (value.length === 0) {
814 throw new Error("Parameter types must be provided for empty arrays via the 'types' field in query options.");
815 }
816 return {
817 type: 'ARRAY',
818 arrayType: BigQuery.getTypeDescriptorFromValue_(value[0]),
819 };
820 }
821 else if (is.boolean(value)) {
822 typeName = 'BOOL';
823 }
824 else if (is.number(value)) {
825 typeName = value % 1 === 0 ? 'INT64' : 'FLOAT64';
826 }
827 else if (is.object(value)) {
828 return {
829 type: 'STRUCT',
830 structTypes: Object.keys(value).map(prop => {
831 return {
832 name: prop,
833 // eslint-disable-next-line @typescript-eslint/no-explicit-any
834 type: BigQuery.getTypeDescriptorFromValue_(value[prop]),
835 };
836 }),
837 };
838 }
839 else if (is.string(value)) {
840 typeName = 'STRING';
841 }
842 if (!typeName) {
843 throw new Error([
844 'This value could not be translated to a BigQuery data type.',
845 value,
846 ].join('\n'));
847 }
848 return {
849 type: typeName,
850 };
851 }
852 /**
853 * Convert a value into a `queryParameter` object.
854 *
855 * @private
856 *
857 * See {@link https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query#request-body| Jobs.query API Reference Docs (see `queryParameters`)}
858 *
859 * @param {*} value The value.
860 * @param {string|ProvidedTypeStruct|ProvidedTypeArray} providedType Provided
861 * query parameter type.
862 * @returns {object} A properly-formed `queryParameter` object.
863 */
864 static valueToQueryParameter_(
865 // eslint-disable-next-line @typescript-eslint/no-explicit-any
866 value, providedType) {
867 var _a, _b;
868 if (is.date(value)) {
869 value = BigQuery.timestamp(value);
870 }
871 let parameterType;
872 if (providedType) {
873 parameterType = BigQuery.getTypeDescriptorFromProvidedType_(providedType);
874 }
875 else {
876 parameterType = BigQuery.getTypeDescriptorFromValue_(value);
877 }
878 const queryParameter = { parameterType, parameterValue: {} };
879 const typeName = queryParameter.parameterType.type;
880 if (typeName === 'ARRAY') {
881 queryParameter.parameterValue.arrayValues = value.map(itemValue => {
882 const value = BigQuery._getValue(itemValue, parameterType.arrayType);
883 if (is.object(value) || is.array(value)) {
884 if (is.array(providedType)) {
885 providedType = providedType;
886 return BigQuery.valueToQueryParameter_(value, providedType[0])
887 .parameterValue;
888 }
889 else {
890 return BigQuery.valueToQueryParameter_(value).parameterValue;
891 }
892 }
893 return { value };
894 });
895 }
896 else if (typeName === 'STRUCT') {
897 queryParameter.parameterValue.structValues = Object.keys(value).reduce((structValues, prop) => {
898 let nestedQueryParameter;
899 if (providedType) {
900 nestedQueryParameter = BigQuery.valueToQueryParameter_(value[prop], providedType[prop]);
901 }
902 else {
903 nestedQueryParameter = BigQuery.valueToQueryParameter_(value[prop]);
904 }
905 // eslint-disable-next-line @typescript-eslint/no-explicit-any
906 structValues[prop] = nestedQueryParameter.parameterValue;
907 return structValues;
908 }, {});
909 }
910 else if (typeName === 'RANGE') {
911 let rangeValue;
912 if (value instanceof BigQueryRange) {
913 rangeValue = value;
914 }
915 else {
916 rangeValue = BigQuery.range(value, (_b = (_a = queryParameter.parameterType) === null || _a === void 0 ? void 0 : _a.rangeElementType) === null || _b === void 0 ? void 0 : _b.type);
917 }
918 queryParameter.parameterValue.rangeValue = {
919 start: {
920 value: rangeValue.value.start,
921 },
922 end: {
923 value: rangeValue.value.end,
924 },
925 };
926 }
927 else if (typeName === 'JSON' && is.object(value)) {
928 queryParameter.parameterValue.value = JSON.stringify(value);
929 }
930 else {
931 queryParameter.parameterValue.value = BigQuery._getValue(value, parameterType);
932 }
933 return queryParameter;
934 }
935 // eslint-disable-next-line @typescript-eslint/no-explicit-any
936 static _getValue(value, type) {
937 if (value === null) {
938 return null;
939 }
940 if (value.type)
941 type = value;
942 return BigQuery._isCustomType(type) ? value.value : value;
943 }
944 static _isCustomType({ type }) {
945 return (type.indexOf('TIME') > -1 ||
946 type.indexOf('DATE') > -1 ||
947 type.indexOf('GEOGRAPHY') > -1 ||
948 type.indexOf('RANGE') > -1 ||
949 type.indexOf('BigQueryInt') > -1);
950 }
951 createDataset(id, optionsOrCallback, cb) {
952 const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
953 const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
954 const reqOpts = {
955 method: 'POST',
956 uri: '/datasets',
957 json: extend(true, {
958 location: this.location,
959 }, options, {
960 datasetReference: {
961 datasetId: id,
962 },
963 }),
964 };
965 if (options.projectId) {
966 reqOpts.projectId = options.projectId;
967 }
968 this.request(reqOpts, (err, resp) => {
969 if (err) {
970 callback(err, null, resp);
971 return;
972 }
973 const dataset = this.dataset(id, options);
974 dataset.metadata = resp;
975 callback(null, dataset, resp);
976 });
977 }
978 createQueryJob(opts, callback) {
979 const options = typeof opts === 'object' ? opts : { query: opts };
980 this.trace_('[createQueryJob]', options, callback);
981 if ((!options || !options.query) && !options.pageToken) {
982 throw new Error('A SQL query string is required.');
983 }
984 const query = extend(true, {
985 useLegacySql: false,
986 }, options);
987 this.trace_('[createQueryJob]', query);
988 if (options.destination) {
989 if (!(options.destination instanceof table_1.Table)) {
990 throw new Error('Destination must be a Table object.');
991 }
992 query.destinationTable = {
993 datasetId: options.destination.dataset.id,
994 projectId: options.destination.dataset.projectId,
995 tableId: options.destination.id,
996 };
997 delete query.destination;
998 }
999 if (query.params) {
1000 const { parameterMode, params } = this.buildQueryParams_(query.params, query.types);
1001 query.parameterMode = parameterMode;
1002 query.queryParameters = params;
1003 delete query.params;
1004 }
1005 const reqOpts = {};
1006 reqOpts.configuration = {
1007 query,
1008 };
1009 if (typeof query.jobTimeoutMs === 'number') {
1010 reqOpts.configuration.jobTimeoutMs = query.jobTimeoutMs.toString();
1011 delete query.jobTimeoutMs;
1012 }
1013 if (query.dryRun) {
1014 reqOpts.configuration.dryRun = query.dryRun;
1015 delete query.dryRun;
1016 }
1017 if (query.labels) {
1018 reqOpts.configuration.labels = query.labels;
1019 delete query.labels;
1020 }
1021 if (query.jobPrefix) {
1022 reqOpts.jobPrefix = query.jobPrefix;
1023 delete query.jobPrefix;
1024 }
1025 if (query.location) {
1026 reqOpts.location = query.location;
1027 delete query.location;
1028 }
1029 if (query.jobId) {
1030 reqOpts.jobId = query.jobId;
1031 delete query.jobId;
1032 }
1033 this.createJob(reqOpts, callback);
1034 }
1035 buildQueryParams_(params, types) {
1036 if (!params) {
1037 return {
1038 parameterMode: undefined,
1039 params: undefined,
1040 };
1041 }
1042 const parameterMode = is.array(params) ? 'positional' : 'named';
1043 const queryParameters = [];
1044 if (parameterMode === 'named') {
1045 const namedParams = params;
1046 for (const namedParameter of Object.getOwnPropertyNames(namedParams)) {
1047 const value = namedParams[namedParameter];
1048 let queryParameter;
1049 if (types) {
1050 if (!is.object(types)) {
1051 throw new Error('Provided types must match the value type passed to `params`');
1052 }
1053 const namedTypes = types;
1054 if (namedTypes[namedParameter]) {
1055 queryParameter = BigQuery.valueToQueryParameter_(value, namedTypes[namedParameter]);
1056 }
1057 else {
1058 queryParameter = BigQuery.valueToQueryParameter_(value);
1059 }
1060 }
1061 else {
1062 queryParameter = BigQuery.valueToQueryParameter_(value);
1063 }
1064 queryParameter.name = namedParameter;
1065 queryParameters.push(queryParameter);
1066 }
1067 }
1068 else {
1069 if (types) {
1070 if (!is.array(types)) {
1071 throw new Error('Provided types must match the value type passed to `params`');
1072 }
1073 const positionalTypes = types;
1074 if (params.length !== types.length) {
1075 throw new Error('Incorrect number of parameter types provided.');
1076 }
1077 params.forEach((value, i) => {
1078 const queryParameter = BigQuery.valueToQueryParameter_(value, positionalTypes[i]);
1079 queryParameters.push(queryParameter);
1080 });
1081 }
1082 else {
1083 params.forEach((value) => {
1084 const queryParameter = BigQuery.valueToQueryParameter_(value);
1085 queryParameters.push(queryParameter);
1086 });
1087 }
1088 }
1089 return {
1090 parameterMode,
1091 params: queryParameters,
1092 };
1093 }
1094 createJob(options, callback) {
1095 var _a;
1096 const JOB_ID_PROVIDED = typeof options.jobId !== 'undefined';
1097 const DRY_RUN = ((_a = options.configuration) === null || _a === void 0 ? void 0 : _a.dryRun)
1098 ? options.configuration.dryRun
1099 : false;
1100 const reqOpts = Object.assign({}, options);
1101 let jobId = JOB_ID_PROVIDED ? reqOpts.jobId : uuid.v4();
1102 if (reqOpts.jobId) {
1103 delete reqOpts.jobId;
1104 }
1105 if (reqOpts.jobPrefix) {
1106 jobId = reqOpts.jobPrefix + jobId;
1107 delete reqOpts.jobPrefix;
1108 }
1109 reqOpts.jobReference = {
1110 projectId: this.projectId,
1111 jobId,
1112 location: this.location,
1113 };
1114 if (options.location) {
1115 reqOpts.jobReference.location = options.location;
1116 delete reqOpts.location;
1117 }
1118 const job = this.job(jobId, {
1119 location: reqOpts.jobReference.location,
1120 });
1121 this.request({
1122 method: 'POST',
1123 uri: '/jobs',
1124 json: reqOpts,
1125 }, async (err, resp) => {
1126 const ALREADY_EXISTS_CODE = 409;
1127 if (err) {
1128 if (err.code === ALREADY_EXISTS_CODE &&
1129 !JOB_ID_PROVIDED &&
1130 !DRY_RUN) {
1131 // The last insert attempt flaked, but the API still processed the
1132 // request and created the job. Because of our "autoRetry" feature,
1133 // we tried the request again, which tried to create it again,
1134 // unnecessarily. We will get the job's metadata and treat it as if
1135 // it just came back from the create call.
1136 err = null;
1137 [resp] = await job.getMetadata();
1138 }
1139 else {
1140 callback(err, null, resp);
1141 return;
1142 }
1143 }
1144 if (resp.status.errors) {
1145 err = new common_1.util.ApiError({
1146 errors: resp.status.errors,
1147 response: resp,
1148 });
1149 }
1150 // Update the location with the one used by the API.
1151 job.location = resp.jobReference.location;
1152 job.metadata = resp;
1153 callback(err, job, resp);
1154 });
1155 }
1156 /**
1157 * Create a reference to a dataset.
1158 *
1159 * @param {string} id ID of the dataset.
1160 * @param {object} [options] Dataset options.
1161 * @param {string} [options.projectId] The GCP project ID.
1162 * @param {string} [options.location] The geographic location of the dataset.
1163 * Required except for US and EU.
1164 *
1165 * @example
1166 * ```
1167 * const {BigQuery} = require('@google-cloud/bigquery');
1168 * const bigquery = new BigQuery();
1169 * const dataset = bigquery.dataset('higher_education');
1170 * ```
1171 */
1172 dataset(id, options) {
1173 if (typeof id !== 'string') {
1174 throw new TypeError('A dataset ID is required.');
1175 }
1176 if (this.location) {
1177 options = extend({ location: this.location }, options);
1178 }
1179 return new dataset_1.Dataset(this, id, options);
1180 }
1181 getDatasets(optionsOrCallback, cb) {
1182 const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
1183 const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
1184 const reqOpts = {
1185 uri: '/datasets',
1186 qs: options,
1187 };
1188 if (options.projectId) {
1189 reqOpts.projectId = options.projectId;
1190 }
1191 this.request(reqOpts, (err, resp) => {
1192 if (err) {
1193 callback(err, null, null, resp);
1194 return;
1195 }
1196 let nextQuery = null;
1197 if (resp.nextPageToken) {
1198 nextQuery = Object.assign({}, options, {
1199 pageToken: resp.nextPageToken,
1200 });
1201 }
1202 // eslint-disable-next-line @typescript-eslint/no-explicit-any
1203 const datasets = (resp.datasets || []).map((dataset) => {
1204 const dsOpts = {
1205 location: dataset.location,
1206 };
1207 if (options.projectId) {
1208 dsOpts.projectId = options.projectId;
1209 }
1210 const ds = this.dataset(dataset.datasetReference.datasetId, dsOpts);
1211 ds.metadata = dataset;
1212 return ds;
1213 });
1214 callback(null, datasets, nextQuery, resp);
1215 });
1216 }
1217 getJobs(optionsOrCallback, cb) {
1218 const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
1219 const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
1220 this.request({
1221 uri: '/jobs',
1222 qs: options,
1223 useQuerystring: true,
1224 }, (err, resp) => {
1225 if (err) {
1226 callback(err, null, null, resp);
1227 return;
1228 }
1229 let nextQuery = null;
1230 if (resp.nextPageToken) {
1231 nextQuery = Object.assign({}, options, {
1232 pageToken: resp.nextPageToken,
1233 });
1234 }
1235 const jobs = (resp.jobs || []).map((jobObject) => {
1236 const job = this.job(jobObject.jobReference.jobId, {
1237 location: jobObject.jobReference.location,
1238 });
1239 job.metadata = jobObject;
1240 return job;
1241 });
1242 callback(null, jobs, nextQuery, resp);
1243 });
1244 }
1245 /**
1246 * Create a reference to an existing job.
1247 *
1248 * @param {string} id ID of the job.
1249 * @param {object} [options] Configuration object.
1250 * @param {string} [options.location] The geographic location of the job.
1251 * Required except for US and EU.
1252 *
1253 * @example
1254 * ```
1255 * const {BigQuery} = require('@google-cloud/bigquery');
1256 * const bigquery = new BigQuery();
1257 *
1258 * const myExistingJob = bigquery.job('job-id');
1259 * ```
1260 */
1261 job(id, options) {
1262 if (this.location) {
1263 options = extend({ location: this.location }, options);
1264 }
1265 return new job_1.Job(this, id, options);
1266 }
1267 query(query, optionsOrCallback, cb) {
1268 let options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
1269 const queryOpts = typeof query === 'object'
1270 ? {
1271 wrapIntegers: query.wrapIntegers,
1272 parseJSON: query.parseJSON,
1273 }
1274 : {};
1275 const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
1276 this.trace_('[query]', query, options);
1277 const queryReq = this.buildQueryRequest_(query, options);
1278 this.trace_('[query] queryReq', queryReq);
1279 if (!queryReq) {
1280 this.createQueryJob(query, (err, job, resp) => {
1281 if (err) {
1282 callback(err, null, resp);
1283 return;
1284 }
1285 if (typeof query === 'object' && query.dryRun) {
1286 callback(null, [], resp);
1287 return;
1288 }
1289 // The Job is important for the `queryAsStream_` method, so a new query
1290 // isn't created each time results are polled for.
1291 options = extend({ job }, queryOpts, options);
1292 job.getQueryResults(options, callback);
1293 });
1294 return;
1295 }
1296 this.runJobsQuery(queryReq, (err, job, res) => {
1297 this.trace_('[runJobsQuery callback]: ', query, err, job, res);
1298 if (err) {
1299 callback(err, null, job);
1300 return;
1301 }
1302 options = extend({ job }, queryOpts, options);
1303 if (res && res.jobComplete) {
1304 let rows = [];
1305 if (res.schema && res.rows) {
1306 rows = BigQuery.mergeSchemaWithRows_(res.schema, res.rows, {
1307 wrapIntegers: options.wrapIntegers || false,
1308 parseJSON: options.parseJSON,
1309 });
1310 delete res.rows;
1311 }
1312 this.trace_('[runJobsQuery] job complete');
1313 options._cachedRows = rows;
1314 options._cachedResponse = res;
1315 if (res.pageToken) {
1316 this.trace_('[runJobsQuery] has more pages');
1317 options.pageToken = res.pageToken;
1318 }
1319 else {
1320 this.trace_('[runJobsQuery] no more pages');
1321 }
1322 job.getQueryResults(options, callback);
1323 return;
1324 }
1325 // If timeout override was provided, return error.
1326 if (queryReq.timeoutMs) {
1327 const err = new Error(`The query did not complete before ${queryReq.timeoutMs}ms`);
1328 callback(err, null, job);
1329 return;
1330 }
1331 delete options.timeoutMs;
1332 this.trace_('[runJobsQuery] job not complete');
1333 job.getQueryResults(options, callback);
1334 });
1335 }
1336 /**
1337 * Check if the given Query can run using the `jobs.query` endpoint.
1338 * Returns a bigquery.IQueryRequest that can be used to call `jobs.query`.
1339 * Return undefined if is not possible to convert to a bigquery.IQueryRequest.
1340 *
1341 * @param query string | Query
1342 * @param options QueryOptions
1343 * @returns bigquery.IQueryRequest | undefined
1344 */
1345 buildQueryRequest_(query, options) {
1346 if (process.env.FAST_QUERY_PATH === 'DISABLED') {
1347 return undefined;
1348 }
1349 const queryObj = typeof query === 'string'
1350 ? {
1351 query: query,
1352 }
1353 : query;
1354 this.trace_('[buildQueryRequest]', query, options, queryObj);
1355 // This is a denylist of settings which prevent us from composing an equivalent
1356 // bq.QueryRequest due to differences between configuration parameters accepted
1357 // by jobs.insert vs jobs.query.
1358 if (!!queryObj.destination ||
1359 !!queryObj.tableDefinitions ||
1360 !!queryObj.createDisposition ||
1361 !!queryObj.writeDisposition ||
1362 (!!queryObj.priority && queryObj.priority !== 'INTERACTIVE') ||
1363 queryObj.useLegacySql ||
1364 !!queryObj.maximumBillingTier ||
1365 !!queryObj.timePartitioning ||
1366 !!queryObj.rangePartitioning ||
1367 !!queryObj.clustering ||
1368 !!queryObj.destinationEncryptionConfiguration ||
1369 !!queryObj.schemaUpdateOptions ||
1370 !!queryObj.jobTimeoutMs ||
1371 // User has defined the jobID generation behavior
1372 !!queryObj.jobId) {
1373 return undefined;
1374 }
1375 if (queryObj.dryRun) {
1376 return undefined;
1377 }
1378 if (options.job) {
1379 return undefined;
1380 }
1381 const req = {
1382 useQueryCache: queryObj.useQueryCache,
1383 labels: queryObj.labels,
1384 defaultDataset: queryObj.defaultDataset,
1385 createSession: queryObj.createSession,
1386 maximumBytesBilled: queryObj.maximumBytesBilled,
1387 timeoutMs: options.timeoutMs,
1388 location: queryObj.location || options.location,
1389 formatOptions: {
1390 useInt64Timestamp: true,
1391 },
1392 maxResults: queryObj.maxResults || options.maxResults,
1393 query: queryObj.query,
1394 useLegacySql: false,
1395 requestId: uuid.v4(),
1396 jobCreationMode: 'JOB_CREATION_OPTIONAL',
1397 };
1398 if (!this._enableQueryPreview) {
1399 delete req.jobCreationMode;
1400 }
1401 const { parameterMode, params } = this.buildQueryParams_(queryObj.params, queryObj.types);
1402 if (params) {
1403 req.queryParameters = params;
1404 }
1405 if (parameterMode) {
1406 req.parameterMode = parameterMode;
1407 }
1408 return req;
1409 }
1410 runJobsQuery(req, callback) {
1411 this.trace_('[runJobsQuery]', req, callback);
1412 this.request({
1413 method: 'POST',
1414 uri: '/queries',
1415 json: req,
1416 }, async (err, res) => {
1417 this.trace_('jobs.query res:', res, err);
1418 if (err) {
1419 callback(err, null, res);
1420 return;
1421 }
1422 let job = null;
1423 if (res.jobReference) {
1424 const jobRef = res.jobReference;
1425 job = this.job(jobRef.jobId, {
1426 location: jobRef.location,
1427 });
1428 }
1429 else if (res.queryId) {
1430 job = this.job(res.queryId); // stateless query
1431 }
1432 callback(null, job, res);
1433 });
1434 }
1435 /**
1436 * This method will be called by `createQueryStream()`. It is required to
1437 * properly set the `autoPaginate` option value.
1438 *
1439 * @private
1440 */
1441 queryAsStream_(query, callback) {
1442 if (query.job) {
1443 query.job.getQueryResults(query, callback);
1444 return;
1445 }
1446 const { location, maxResults, pageToken, wrapIntegers, parseJSON } = query;
1447 const opts = {
1448 location,
1449 maxResults,
1450 pageToken,
1451 wrapIntegers,
1452 parseJSON,
1453 autoPaginate: false,
1454 };
1455 delete query.location;
1456 delete query.maxResults;
1457 delete query.pageToken;
1458 delete query.wrapIntegers;
1459 delete query.parseJSON;
1460 this.query(query, opts, callback);
1461 }
1462}
1463exports.BigQuery = BigQuery;
1464BigQuery.setLogFunction = logger_1.setLogFunction;
1465/*! Developer Documentation
1466 *
1467 * These methods can be auto-paginated.
1468 */
1469paginator_1.paginator.extend(BigQuery, ['getDatasets', 'getJobs']);
1470/*! Developer Documentation
1471 *
1472 * All async methods (except for streams) will return a Promise in the event
1473 * that a callback is omitted.
1474 */
1475(0, promisify_1.promisifyAll)(BigQuery, {
1476 exclude: [
1477 'dataset',
1478 'date',
1479 'datetime',
1480 'geography',
1481 'int',
1482 'job',
1483 'time',
1484 'timestamp',
1485 'range',
1486 ],
1487});
1488function convertSchemaFieldValue(schemaField,
1489// eslint-disable-next-line @typescript-eslint/no-explicit-any
1490value, options) {
1491 if (is.null(value)) {
1492 return value;
1493 }
1494 switch (schemaField.type) {
1495 case 'BOOLEAN':
1496 case 'BOOL': {
1497 value = value.toLowerCase() === 'true';
1498 break;
1499 }
1500 case 'BYTES': {
1501 value = Buffer.from(value, 'base64');
1502 break;
1503 }
1504 case 'FLOAT':
1505 case 'FLOAT64': {
1506 value = Number(value);
1507 break;
1508 }
1509 case 'INTEGER':
1510 case 'INT64': {
1511 const { wrapIntegers } = options;
1512 value = wrapIntegers
1513 ? typeof wrapIntegers === 'object'
1514 ? BigQuery.int({ integerValue: value, schemaFieldName: schemaField.name }, wrapIntegers).valueOf()
1515 : BigQuery.int(value)
1516 : Number(value);
1517 break;
1518 }
1519 case 'NUMERIC': {
1520 value = new Big(value);
1521 break;
1522 }
1523 case 'BIGNUMERIC': {
1524 value = new Big(value);
1525 break;
1526 }
1527 case 'RECORD': {
1528 value = BigQuery.mergeSchemaWithRows_(schemaField, value, options).pop();
1529 break;
1530 }
1531 case 'DATE': {
1532 value = BigQuery.date(value);
1533 break;
1534 }
1535 case 'DATETIME': {
1536 value = BigQuery.datetime(value);
1537 break;
1538 }
1539 case 'TIME': {
1540 value = BigQuery.time(value);
1541 break;
1542 }
1543 case 'TIMESTAMP': {
1544 const pd = new precise_date_1.PreciseDate();
1545 pd.setFullTime(precise_date_1.PreciseDate.parseFull(BigInt(value) * BigInt(1000)));
1546 value = BigQuery.timestamp(pd);
1547 break;
1548 }
1549 case 'GEOGRAPHY': {
1550 value = BigQuery.geography(value);
1551 break;
1552 }
1553 case 'JSON': {
1554 const { parseJSON } = options;
1555 value = parseJSON ? JSON.parse(value) : value;
1556 break;
1557 }
1558 case 'RANGE': {
1559 value = BigQueryRange.fromSchemaValue_(value, schemaField.rangeElementType.type);
1560 break;
1561 }
1562 default:
1563 break;
1564 }
1565 return value;
1566}
1567/**
1568 * Range class for BigQuery.
1569 * A range represents contiguous range between two dates, datetimes, or timestamps.
1570 * The lower and upper bound for the range are optional.
1571 * The lower bound is inclusive and the upper bound is exclusive.
1572 * See https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#range_literals
1573 */
1574class BigQueryRange {
1575 constructor(value, elementType) {
1576 if (typeof value === 'string') {
1577 if (!elementType) {
1578 throw new Error('invalid RANGE. Element type required when using RANGE API string.');
1579 }
1580 const [start, end] = BigQueryRange.fromStringValue_(value);
1581 this.start = this.convertElement_(start, elementType);
1582 this.end = this.convertElement_(end, elementType);
1583 this.elementType = elementType;
1584 }
1585 else {
1586 const { start, end } = value;
1587 if (start && end) {
1588 if (typeof start !== typeof end) {
1589 throw Error('upper and lower bound on a RANGE should be of the same type.');
1590 }
1591 }
1592 const inferredType = {
1593 BigQueryDate: 'DATE',
1594 BigQueryDatetime: 'DATETIME',
1595 BigQueryTimestamp: 'TIMESTAMP',
1596 }[(start || end || Object).constructor.name] || elementType;
1597 this.start = this.convertElement_(start, inferredType);
1598 this.end = this.convertElement_(end, inferredType);
1599 this.elementType = inferredType;
1600 }
1601 }
1602 /*
1603 * Get Range string representation used by the BigQuery API.
1604 */
1605 get apiValue() {
1606 return `[${this.start ? this.start.value : 'UNBOUNDED'}, ${this.end ? this.end.value : 'UNBOUNDED'})`;
1607 }
1608 /*
1609 * Get Range literal representation accordingly to
1610 * https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#range_literals
1611 */
1612 get literalValue() {
1613 return `RANGE<${this.elementType}> ${this.apiValue}`;
1614 }
1615 get value() {
1616 return {
1617 start: this.start ? this.start.value : 'UNBOUNDED',
1618 end: this.end ? this.end.value : 'UNBOUNDED',
1619 };
1620 }
1621 static fromStringValue_(value) {
1622 let cleanedValue = value;
1623 if (cleanedValue.startsWith('[') || cleanedValue.startsWith('(')) {
1624 cleanedValue = cleanedValue.substring(1);
1625 }
1626 if (cleanedValue.endsWith(')') || cleanedValue.endsWith(']')) {
1627 cleanedValue = cleanedValue.substring(0, cleanedValue.length - 1);
1628 }
1629 const parts = cleanedValue.split(',');
1630 if (parts.length !== 2) {
1631 throw new Error('invalid RANGE. See RANGE literal format docs for more information.');
1632 }
1633 const [start, end] = parts.map((s) => s.trim());
1634 return [start, end];
1635 }
1636 static fromSchemaValue_(value, elementType) {
1637 const [start, end] = BigQueryRange.fromStringValue_(value);
1638 const convertRangeSchemaValue = (value) => {
1639 if (value === 'UNBOUNDED' || value === 'NULL') {
1640 return null;
1641 }
1642 return convertSchemaFieldValue({ type: elementType }, value, {
1643 wrapIntegers: false,
1644 });
1645 };
1646 return BigQuery.range({
1647 start: convertRangeSchemaValue(start),
1648 end: convertRangeSchemaValue(end),
1649 }, elementType);
1650 }
1651 convertElement_(value, elementType) {
1652 if (typeof value === 'string') {
1653 if (value === 'UNBOUNDED' || value === 'NULL') {
1654 return undefined;
1655 }
1656 switch (elementType) {
1657 case 'DATE':
1658 return new BigQueryDate(value);
1659 case 'DATETIME':
1660 return new BigQueryDatetime(value);
1661 case 'TIMESTAMP':
1662 return new BigQueryTimestamp(value);
1663 }
1664 return undefined;
1665 }
1666 return value;
1667 }
1668}
1669exports.BigQueryRange = BigQueryRange;
1670/**
1671 * Date class for BigQuery.
1672 */
1673class BigQueryDate {
1674 constructor(value) {
1675 if (typeof value === 'object') {
1676 value = BigQuery.datetime(value).value;
1677 }
1678 this.value = value;
1679 }
1680}
1681exports.BigQueryDate = BigQueryDate;
1682/**
1683 * Geography class for BigQuery.
1684 */
1685class Geography {
1686 constructor(value) {
1687 this.value = value;
1688 }
1689}
1690exports.Geography = Geography;
1691/**
1692 * Timestamp class for BigQuery.
1693 *
1694 * The recommended input here is a `Date` or `PreciseDate` class.
1695 * If passing as a `string`, it should be Timestamp literals: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#timestamp_literals.
1696 * When passing a `number` input, it should be epoch seconds in float representation.
1697 *
1698 */
1699class BigQueryTimestamp {
1700 constructor(value) {
1701 let pd;
1702 if (value instanceof precise_date_1.PreciseDate) {
1703 pd = value;
1704 }
1705 else if (value instanceof Date) {
1706 pd = new precise_date_1.PreciseDate(value);
1707 }
1708 else if (typeof value === 'string') {
1709 if (/^\d{4}-\d{1,2}-\d{1,2}/.test(value)) {
1710 pd = new precise_date_1.PreciseDate(value);
1711 }
1712 else {
1713 const floatValue = Number.parseFloat(value);
1714 if (!Number.isNaN(floatValue)) {
1715 pd = this.fromFloatValue_(floatValue);
1716 }
1717 else {
1718 pd = new precise_date_1.PreciseDate(value);
1719 }
1720 }
1721 }
1722 else {
1723 pd = this.fromFloatValue_(value);
1724 }
1725 // to keep backward compatibility, only converts with microsecond
1726 // precision if needed.
1727 if (pd.getMicroseconds() > 0) {
1728 this.value = pd.toISOString();
1729 }
1730 else {
1731 this.value = new Date(pd.getTime()).toJSON();
1732 }
1733 }
1734 fromFloatValue_(value) {
1735 const secs = Math.trunc(value);
1736 // Timestamps in BigQuery have microsecond precision, so we must
1737 // return a round number of microseconds.
1738 const micros = Math.trunc((value - secs) * 1e6 + 0.5);
1739 const pd = new precise_date_1.PreciseDate([secs, micros * 1000]);
1740 return pd;
1741 }
1742}
1743exports.BigQueryTimestamp = BigQueryTimestamp;
1744/**
1745 * Datetime class for BigQuery.
1746 */
1747class BigQueryDatetime {
1748 constructor(value) {
1749 if (typeof value === 'object') {
1750 let time;
1751 if (value.hours) {
1752 time = BigQuery.time(value).value;
1753 }
1754 const y = value.year;
1755 const m = value.month;
1756 const d = value.day;
1757 time = time ? ' ' + time : '';
1758 value = `${y}-${m}-${d}${time}`;
1759 }
1760 else {
1761 value = value.replace(/^(.*)T(.*)Z$/, '$1 $2');
1762 }
1763 this.value = value;
1764 }
1765}
1766exports.BigQueryDatetime = BigQueryDatetime;
1767/**
1768 * Time class for BigQuery.
1769 */
1770class BigQueryTime {
1771 constructor(value) {
1772 if (typeof value === 'object') {
1773 const h = value.hours;
1774 const m = value.minutes || 0;
1775 const s = value.seconds || 0;
1776 const f = is.defined(value.fractional) ? '.' + value.fractional : '';
1777 value = `${h}:${m}:${s}${f}`;
1778 }
1779 this.value = value;
1780 }
1781}
1782exports.BigQueryTime = BigQueryTime;
1783/**
1784 * Build a BigQueryInt object. For long integers, a string can be provided.
1785 *
1786 * @class
1787 * @param {string|number|IntegerTypeCastValue} value The 'INT64' value.
1788 * @param {object} [typeCastOptions] Configuration to convert
1789 * values of 'INT64' type to a custom value. Must provide an
1790 * `integerTypeCastFunction` to handle conversion.
1791 * @param {function} typeCastOptions.integerTypeCastFunction A custom user
1792 * provided function to convert value.
1793 * @param {string|string[]} [typeCastOptions.fields] Schema field
1794 * names to be converted using `integerTypeCastFunction`.
1795 *
1796 * @example
1797 * ```
1798 * const {BigQuery} = require('@google-cloud/bigquery');
1799 * const bigquery = new BigQuery();
1800 * const anInt = bigquery.int(7);
1801 * ```
1802 */
1803class BigQueryInt extends Number {
1804 constructor(value, typeCastOptions) {
1805 super(typeof value === 'object' ? value.integerValue : value);
1806 this._schemaFieldName =
1807 typeof value === 'object' ? value.schemaFieldName : undefined;
1808 this.value =
1809 typeof value === 'object'
1810 ? value.integerValue.toString()
1811 : value.toString();
1812 this.type = 'BigQueryInt';
1813 if (typeCastOptions) {
1814 if (typeof typeCastOptions.integerTypeCastFunction !== 'function') {
1815 throw new Error('integerTypeCastFunction is not a function or was not provided.');
1816 }
1817 const typeCastFields = typeCastOptions.fields
1818 ? arrify(typeCastOptions.fields)
1819 : undefined;
1820 let customCast = true;
1821 if (typeCastFields) {
1822 customCast = this._schemaFieldName
1823 ? typeCastFields.includes(this._schemaFieldName)
1824 ? true
1825 : false
1826 : false;
1827 }
1828 customCast &&
1829 (this.typeCastFunction = typeCastOptions.integerTypeCastFunction);
1830 }
1831 }
1832 // eslint-disable-next-line @typescript-eslint/no-explicit-any
1833 valueOf() {
1834 const shouldCustomCast = this.typeCastFunction ? true : false;
1835 if (shouldCustomCast) {
1836 try {
1837 return this.typeCastFunction(this.value);
1838 }
1839 catch (error) {
1840 if (error instanceof Error) {
1841 error.message = `integerTypeCastFunction threw an error:\n\n - ${error.message}`;
1842 }
1843 throw error;
1844 }
1845 }
1846 else {
1847 return BigQuery.decodeIntegerValue_({
1848 integerValue: this.value,
1849 schemaFieldName: this._schemaFieldName,
1850 });
1851 }
1852 }
1853 toJSON() {
1854 return { type: this.type, value: this.value };
1855 }
1856}
1857exports.BigQueryInt = BigQueryInt;
1858//# sourceMappingURL=bigquery.js.map
\No newline at end of file